I am executing a SQL Server stored procedure from my C# code which essentially pulls some data from a database based on the supplied condition.
DataSet GetAllxxxxxByDate(string entityValue,string companyValue)
{
using (var sqlConn = new SqlConnection(GetConnectionString()))
{
using (var cmd = new SqlCommand())
{
var data = new DataSet();
cmd.CommandText = “myStoredprodecure”;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConn;
var eVal = string.IsNullOrWhiteSpace(entityValue) ? string.Empty : entityValue;
cmd.Parameters.AddWithValue("#entity_value", eVal);
var company = string.IsNullOrWhiteSpace(companyValue) ? string.Empty : companyValue;
cmd.Parameters.AddWithValue("#company", company);
var sqlDataAdaptor = new SqlDataAdapter(cmd);
sqlDataAdaptor.Fill(data);
return data;
}
}
}
Here entityValue, companyValue are comma separated strings, formed dynamically within C# code and pass it to stored procedure.
Eg:
’first’,’second’,’third’
And the stored procedure uses these values to fill the NOT IN condition defined within it.
The issue is that, I am getting inconsistent number of records when I execute the code.
Following is a quick screenshot where first WHERE clause return 3 records and second WHERE clause return 1 record. The input values for the first WHERE clause is been filled from c# code and the second is been filled manually to test.
The only difference, which I can spot is number of quotes.
Question: can someone help me to zero in the issue or the difference in these give WHERE clause ?
Well, you don't show what entity_value is in your results, but the difference between the two is you're adding single quotes around the literal values:
N'''FSEC''' in SQL is the literal valiue 'FSEC'
'FSEC' in SQL is just FSEC (without the quotes).
My guess is that records 2004981 and 2004982 have a value of FSEC (without the quotes) for entity_value.
If you're adding parameter values from C# code, don't add quotes around them like you would if you were building a string. SQL will treat the values as strings without needing string qualifiers.
EDIT
Okay, I just read this statement:
Here entityValue, companyValue are comma separated string
You can't just pass in a comma-delimited string to an IN clause. To search for multiple values there are a few options:
Add commas to each end and use LIKE:
Where (',' + #entity_value +',' LIKE '%,' + entity_value + ',%')
Parse the string into a temporary table, than use that table in your IN clause
Pass the values as a table-valued parameter and use that in your IN clause.
Build the SQL statement as a string and execute it with EXEC
Related
I have in my class "fattura" this
public string sommaFattura(String costo)
{
MySqlCommand command = new MySqlCommand();
String sumQuery = "SELECT SUM(`prezzo`) FROM `fatturetemp`";
command.CommandText = sumQuery;
command.Connection = conn.getConnection();
command.Parameters.Add("#prezzo", MySqlDbType.Int32).Value = costo;
conn.openConnection();
//Need the command for take the result
conn.closeConnection();
}
What is the command for take the result of Sum query?
I want use this command, insert in a variable. Can you correct me?
I think the command you want is
command.ExecuteScalar();
However, note, Execute Scalar is intended that the result will return a single row, single column which your query does without regard to your parameter. However your query does not specifically make sense either..
Now your query itself. You have an explicit prezzo which would imply an existing column name and not that of a parameter. It would have to a numeric field for obvious reasons to sum it.
Now you also have an expected string coming in via "costo" parameter to the function call. If your intent is to have the incoming string be representative of a known column of the table, this is not going to work for you. You would have to build the SQL command with that explicit column name, or build doing dynamic-sql, but that would really be too much.
The concern with building a SQL statement with string parameters is you would be open to SQL-injection, especially if web-based. If your incoming string is coming from a controlled source, such as you are presenting a list of columns to a user and they can only pick one such column. Or, you have buttons on a screen asking for a sum of a certain thing, and YOU control that column name you would be in a better condition, but still be cautious passing parameter to build out SQL.
Say for example your table has numeric fields of qtySold and dollarSales. You want either the sum of either of these columns and are passing one of these respective strings into the function such as
var qtySoldAnswer = sommaFattura( "qtySold" );
or
var dollarSalesAnswer = sommaFattura( "dollarSales" );
Then your function would be CLOSER to...
public string sommaFattura(String costo)
{
MySqlCommand command = new MySqlCommand();
command.CommandText = "SELECT SUM(" + costo + ") FROM fatturetemp";
command.Connection = conn.getConnection();
conn.openConnection();
var answer = command.ExecuteScalar();
conn.closeConnection();
// I would put a breakpoint in here to see the results.
// you can then format the answer such as decimal point, etc.
return answer.ToString();
}
AGAIN, ONLY if YOU have control of the column name you are trying to send in. Someone could do sql-injection and do a lot of damage. I would only do with heavily controlled consideration and explicitly validating the columns you WOULD allow, and if so, set your variable at that point. Don't allow for any more or less than the column name. No special characters, quotes, comments, sql-terminator and new statement...
But hopefully this clarifies what I THINK you are trying to accomplish.
Recent bug report states that a method being called is crashing the service causing it to restart. After troubleshooting, the cause was found to be an obnoxious Oracle SQL call with thousands of strings passed. There is a collection of strings being passed to a method from an external service which often is more than 10,000 records. The original code used a where clause on the passed collection using the LIKE keyword, which I think is really, really bad.
public IList<ContainerState> GetContainerStates(IList<string> containerNumbers)
{
string sql =
String.Format(#"Select CTNR_NO, CNTR_STATE FROM CONTAINERS WHERE CTRN_SEQ = 0 AND ({0})",
string.Join("OR", containerNumbers
.Select(item => string.Concat(" cntr_no LIKE '", item.SliceLeft(10), "%' ")))
);
return DataBase.SelectQuery(sql, MapRecordToContainerState, new { }).ToList();
}
Clarification of in house methods used which may be confusing:
DataBase.SelectQuery is an internal library method using generics which gets passed the sql string, a function to map the records to .NET objects, and the parameters being passed and returns an IEnumerable of Objects of type retuned by the Mapping function.
SliceLeft is an extension method from another internal helper library that just returns the first part of a string up to the number of characters specified by the parameter.
The reason that the LIKE statement was apparently used, is that the strings being passed and the strings in the database only are guaranteed to match the first 10 characters. Example ("XXXX000000-1" in the strings being passed should match a database record like "XXXX000000-8").
I believed that the IN clause using the SUBSTR would be more efficent than using multiple LIKE clauses and replaced the code with:
public IList<ContainerRecord> GetContainerStates(IList<string> containerNumbers)
{
string sql =
String.Format(#"Select CTNR_NO, CNTR_STATE FROM CONTAINERS WHERE CTRN_SEQ = 0 AND ({0})",
string.Format("SUBSTR(CNTR_NO, 1, 10) IN ({0}) ",
string.Join(",", containerNumbers.Select(item => string.Format("\'{0}\'", item.SliceLeft(10) ) ) )
)
);
return DataBase.SelectQuery(sql, MapRecordToContainerState, new { }).ToList();
}
This helped slightly, and there were fewer issues in my tests, but when there are huge amounts of records passed, there is still an exception thrown and core dumps occur, as the SQL is longer than the server can parse during these times. The DBA suggests saving all the strings being passed to a temporary table, and then joining against that temp table.
Given that advice, I changed the function to:
public IList<ContainerRecord> GetContainerStates(IList<string> containerNumbers)
{
string sql =
#"
CREATE TABLE T1(cntr_num VARCHAR2(10));
DECLARE GLOBAL TEMPORARY TABLE SESSION.T1 NOT LOGGED;
INSERT INTO SESSION.T1 VALUES (:containerNumbers);
SELECT
DISTINCT cntr_no,
'_IT' cntr_state
FROM
tb_master
WHERE
cntr_seq = 0
AND cntr_state IN ({0})
AND adjustment <> :adjustment
AND SUBSTR(CTNR_NO, 1, 10) IN (SELECT CNTR_NUM FROM SESSION.T1);
";
var parameters = new
{
#containerNumbers = containerNumbers.Select( item => item.SliceLeft(10)).ToList()
};
return DataBase.SelectQuery(sql, MapRecordToContainerState, parameters).ToList();
}
Now I'm getting a "ORA-00900: invalid SQL statement". This is really frustrating, how can I properly write a SQL Statement that will put this list of strings into a temporary table and then use it in a SELECT Statement to return the list I need?
There are couple possible places could cause this error, it seams that the "DECLARE GLOBAL TEMPORARY" is a JAVA API, I don't think .net has this function. Please try "Create global temporary table" instead. And, I don't know whether your internal API could handle multiple SQLs in one select sql. As far as I know, ODP.net Command class can only execute one sql per call. Moreover, "create table" is a DDL, it therefore has its own transaction. I can't see any reason we should put them in the same sql to execute. Following is a sample code for ODP.net,
using (OracleConnection conn = new OracleConnection(BD_CONN_STRING))
{
conn.Open();
using (OracleCommand cmd = new OracleCommand("create global temporary table t1(id number(9))", conn))
{
// actually this should execute once only
cmd.ExecuteNonQuery();
}
using (OracleCommand cmd = new OracleCommand("insert into t1 values (1)", conn)) {
cmd.ExecuteNonQuery();
}
// customer table is a permenant table
using (OracleCommand cmd = new OracleCommand("select c.id from customer c, t1 tmp1 where c.id=tmp1.id", conn)) {
cmd.ExecuteNonQuery();
}
}
Hello so i m creating a registration form in C# with MySql so it connects to the database and everything but i get this error Napaka pri registraciji Unknown column " in 'field list' the translation of Napaka pri registraciji means Error at registering i just have it in my language. I get this error when i insert data in textboxes and press Register..
the code:
private void btn_Reg_Click(object sender, EventArgs e)
{
MySqlConnection dataConnection = new MySqlConnection();
dataConnection.ConnectionString = "datasource=localhost;port=3306;username=root;password=";
dataConnection.Open();
MySqlTransaction transakcija = dataConnection.BeginTransaction();
MySqlCommand dataCommand = new MySqlCommand();
dataCommand.Connection = dataConnection;
dataCommand.Transaction = transakcija;
try
{
dataCommand.CommandText = "Insert INTO lr.users (upIme,geslo) VALUES (`"+this.tB_upIme.Text+"`,`"+this.tB_geslo.Text+"`)";
dataCommand.CommandType = CommandType.Text;
dataCommand.ExecuteNonQuery();
transakcija.Commit();
MessageBox.Show("Registracija uspešna!");
}
catch (Exception eks)
{
transakcija.Rollback();
MessageBox.Show("Napaka pri registraciji\n" + eks.Message);
}
finally
{
dataCommand.Connection.Close();
}
}
There are two things I immediately see wrong here...
First, you're using back ticks to wrap your values. In MySQL Back ticks represent database objects, so the query is looking for objects named by those values instead of using the values themselves. So instead of this:
`"+this.tB_upIme.Text+"`
You'd want this:
'"+this.tB_upIme.Text+"'
Second, and vastly more importantly, your code is wide open to SQL injection attacks. You'll want to use query parameters, not direct string concatenation. While it may look like you're just putting values into the query string, you're actually taking user input and treating it as executable code in your query string, which means users can run any arbitrary code they want on your database.
First, add parameters to your query:
"Insert INTO lr.users (upIme,geslo) VALUES (#upIme, #geslo)"
(You'll notice this also makes the query a heck of a lot cleaner and easier to read.) Then add your parameters to the command:
dataCommand.Parameters.AddWithValue("#upIme", this.tB_upIme.Text);
dataCommand.Parameters.AddWithValue("#geslo", this.tB_geslo.Text);
Then when you execute that command it will treat the user-input values as values instead of as executable code.
Change to single quotes ' in the values.
dataCommand.CommandText =
"Insert INTO lr.users (upIme,geslo)
VALUES ('"+this.tB_upIme.Text+"','"+this.tB_geslo.Text+"');";
First off I'd like to apologize if this is more of a question than an example but I'm really lost here. I have a Windows Form that loads info from a text file. In each text file there is all the cities and counties in a given state, each section is separated by the .Split. I have a SQL Server 2008 database, 2 columns, Name and type. What I'd like to do is take all of the information and add it too individual rows with the name column being the name and the type column being state or county. Here is how I have the information split. How would I add a new row for each entry in the text?
void PopulateZones()
{
ofdFile.Filter = "Text File (.txt)|*.txt|All Files (*.*|*.*";
ofdFile.FilterIndex = 1;
ofdFile.Multiselect = true;
ofdFile.FileName = String.Empty;
if (ofdFile.ShowDialog() == DialogResult.OK)
{
ofdFileLocTextBox.Text = ofdFile.FileName.ToString();
string groups = File.ReadAllText(ofdFile.FileName);
string[] parts = groups.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
stateTextBox.Text = parts[0];
citiesTextBox.Text = parts[1];
countiesTextBox.Text = parts[2];
AddtoSQL(parts[0], parts[1]);
}
}
void AddtoSQL(string cities, string counties)
{
Sqlconnection conn = new SqlConnection(connString)
Sqlcommand comm = new Sqlcommand("INSERT into [Table] (Name, Type) Values (#Name, #Type))";
comm.Parameters.Add(#Name, each line of textbox);
comm.Parameters.Add(#Type, City or County);
comm.ExecuteNonQuery();
}
So, the first problem you have is that your code is not doing what you think it does.The big problem is that you are reading in all the text and then only ever selecting the first three values of it.You don't give the format of your data, but suppose it looks like this:
Scotland*Edinburgh*Midlothian*
Scotland*Perth*Perthshire*
Your code
string groups = File.ReadAllText(ofdFile.FileName);
Reads the whole file into one string, such that it will look like this
Scotland*Edinburgh*Midlothian*\r\nScotland*Perth*Perthshire*
So splitting it using the following
string[] parts = groups.Split(new char[] { '*' },
StringSplitOptions.RemoveEmptyEntries);
gives you a string array of 6 parts. Inserting multiple lines from this is doable but won't be very neat. You'd be much better to read your text files in by lines, and then iterate over the array of lines, splitting each one as you go and then adding the relevant parts to SQL. Something like
string[] lines = System.IO.File.ReadAllLines(ofdFile.FileName);
foreach (var line in lines)
{
string[] parts = line.Split('*');
AddtoSQL(parts[0], parts[1]);
}
That should insert all the data, but as an aside, if you are looking to execute numerous inserts at once, I'd recommend housing those inserts inside of a SQL Transaction.
I'd direct you to have a look at this MSDN article on the SqlTransaction Class
The gist of it is that you declare a transaction first, then loop over your inserts executing those against the transaction. Finally, when you commit your transaction the queries are all written to the database en mass. The reason I'd do this is that it will be much quicker and safer.
Does it work if you change your sql statement into "insert into [table] (name, type) values(#name, #type)"? with the bracket.
In SQL Server 2008 you can insert multiple line (records) with one query. All you need to do is a loop to extract row values and construct query string. So in AddToSQL method make your query like:
INSERT INTO [Table](Name, Type)
VALUES ('First',State1),
('Second',State2),
('Third',State3),
('Fourth',State4),
Insert Multiple Records Using One Insert Statement
I have the following code in asp.net:
using (OleDbCommand command = dbConnW.CreateCommand())
{
string CreateTableK = null;
CreateTableK += "Create Table DSKKAR00 (DSK_ID c(10),DSK_KIND N(1),MON_PYM C(3))";
OleDbCommand cmdCreateTable = new OleDbCommand(CreateTableK, dbConnW);
cmdCreateTable.ExecuteNonQuery();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(WorkRoomNo + ",");
sb.Append("1,");
sb.Append(",");
OleDbCommand cmd3 = new OleDbCommand("Insert into DSKKAR00 (DSK_ID,DSK_KIND,MON_PYM) Values (" + sb.ToString() + ")", dbConnW);
cmd3.ExecuteNonQuery();
But I have the following error:
Syntax error
In addition to what Chris has offered, you are starting your CREATE TABLE with a NULL string variable, then doing a += to it. From what I remember, a NULL += "anystring" will remain a null value... You might be crashing right there too.
Although VFP is not really suceptible to SQL Injection like other SQL engines, its good habit to do parameterizing. When you do, use "?" as a place-holder for the value you want to insert, and add parameters in the same order sequence as the "?" represent.
string CreateTableK =
"Create Table DSKKAR00 (DSK_ID c(10),DSK_KIND N(1),MON_PYM C(3))";
OleDbCommand cmdCreateTable = new OleDbCommand(CreateTableK, dbConnW);
cmdCreateTable.ExecuteNonQuery();
string MyInsert =
"insert into DSKKAR00 ( dsk_id, dsk_kind, mon_pym ) values ( ?, ?, ? )";
OleDbCommand cmd3 = new OleDbCommand( MyInsert, dbConnW);
cmd3.Parameters.AddWithValue( "parmSlot1", WorkRoomNo );
cmd3.Parameters.AddWithValue( "parmSlot2", 1);
cmd3.Parameters.AddWithValue( "parmSlot3", 'tst' ); // or whatever variable to put
cmd3.ExecuteNonQuery();
First off, any time you have an error it's usually best to post the entire error message you get.
Also, when trying to debug a query problem, you should emit the actual query being sent to your server/database and inspect it. This way you can find various problems like too many commas.
Speaking of which, looking at your code, you are concatenating a String and it really looks like you have way too many commas.
The emitted query looks like it will be:
insert into DSKKAR00(DSK_ID, DSK_KIND, MON_PYM) VALUES( X,1, ,)
where X is the value of your WorkRoomNo variable.
Obviously, that isn't valid syntax and would result in the error you've seen. The commas indicate there are 4 values being passed, but the insert query only identifies 3 columns.
The next issue has to do with the column definitions themselves. The first column of that table is a c(10); the third is a c(3). I'm a little rusty, but aren't those character fields?
If so then you need to adjust your string builder to add the appropriate quotes around the values...
Which leads us to the final problem: Don't use String concatentation to build queries. Use Parameterized queries