Return numerous SQL results to C# array - c#

I have seen a few questions asked about this kind of thing already, but whenever I try anything it just doesn't work with this exact instance. I have a remote SQL SERVER database set up, and it has over 2000 rows.
This is my C#:
command.Connection = connectionString;
command.CommandType = CommandType.Text;
command.CommandText = "SELECT IconUrl FROM Items WHERE DefIndex IN " + iconSqlString;
connection.Open();
string iconString = command.ExecuteScalar().ToString();
connection.Close();
I do have the SqlConnection set up and all that, I just don't think it is necessary to be included.
iconSqlString is in the format of (32, 126, 68). The string has over 150 items and is different each time. I need to return the IconUrl for all items in the string. The current query for some reason only returns on IconUrl, for the item with the value of 35 in the 51st position (out of 196). I know that the iconString is in the format of a string, but it is only for testing purposes, I was expecting it to have returned all the results, separated by commas or something else. I wish to return all the values possible to an array, to be called later with something like sqlItem[5]. How would I do this?

ExecuteScalar is for single result, you will need to use ExecuteReader instead.
Also as a side note, you should never use variables to perform a query because of an attack called SQLI or SQL Injection. Here is a link with some examples on how to execute parameterized queries.

Related

Not getting results from database

In my C# code, I'm trying to open a connection to a SQL Server database and get a count. I've copied the code (barring the query itself) from another application I wrote that works fine. I've run the query -- both the version I constructed, and the query the code constructs (I use variables) -- and they both work fine, returning an identical count. I'm just not getting anything back.
I've put in breakpoints and checked my connection and query, and it is what I expect. When I Googled this, I just got results for people having entirely different problems, so I'm stumped. I don't know why I'm not getting anything back. Where is the error?
SqlConnection RRconnection = new SqlConnection();
RRconnection.ConnectionString = "Data Source=;Initial Catalog=;User id=;Password=";
RRconnection.Open();
string ridQuery = "SELECT COUNT (t.RxTimeStamp) FROM...";
SqlCommand query = new SqlCommand(ridQuery, RRconnection);
SqlDataReader data = query.ExecuteReader();
if (data.Read())
transcount = Convert.ToInt32(data.GetValue(0));
In case the above paragraph wasn't clear, I'm expecting the query to return a count of 1592064 (for the specific instance I'm testing), and instead there's no data in data.
Probably not related, but my query includes three inner joins. It's the first time I've done that many, so maybe that could be the problem? Except if that was causing the problem, it wouldn't work when I run the query?
have you tried using SqlCommand.ExecuteScalar Method
SqlCommand query = new SqlCommand(ridQuery, RRconnection);
Int32 transcount = (Int32) query.ExecuteScalar();
Update
I am not sure why your query did not work, maybe try giving the count column an alias:
string ridQuery = "SELECT COUNT (t.RxTimeStamp) AS RxCount FROM ...";
It is best to use ExecuteScalar if the query returns a single value.
ExecuteScalar; return object value so must be Convert to the correct type
int totalCount = Convert.ToInt32(cmd.ExecuteScalar());
So, to the you question
It's a ExecuteReader don't use to get a Select Count result, but there's no problem this code. You can see the total count value for this code.
Note : The SqlDataReader Object is a stream-based and forward-only retrieval of query results from the Data Source.
The Read method processes only one row in memory then overwrites old record when itterate next row.
ExecureReader; In your case , reader first row and first column filled query result, other rows and column set to null.
if(reader.Read())
{
int totalRows = Convert.ToInt32(reader.GetValue(0)); //convert returned value
}
Note : use column name and use reader spesific type convert method is my best choice.
reader.GetInt32(reader.GetOrdinal("TotalCount"))
Advice,
Don't use ExecuteReader read for single value
Use ExecuteScalar then cast result to correct type
Use IDisposible objects (Connection , Command , Reader) inside to using code blog. using block close and dispose current object.
You should not read using code blog on column index value

SQL query syntax error with constructed query

I'm getting the error "syntax error (missing operator) in query expression" using this code:
x = "John's";
OleDbCommand commandRdd = new OleDbCommand("Select rdd_tbl.Customer, rdd_tbl.Balance From rdd_tbl WHERE rdd_tbl.Customer ='" + x + "'", con);
OleDbDataReader readerRdd = commandRdd.ExecuteReader();
I know the problem in John >'< s symbol, but i don't know how to make it a part of the name .
You really never want to do it by replacing any special character, while it may work with simple cases there can be other reserved characters that may require handling. Also, the escape sequences are DB specific, so different engines may need different treatments (I assume the multi-DB thing just because you used the generic OleDb classes instead of a more specific family).
Escaping special chars is a good solution for scripts and quick and dirty code, but never do that in a real life program, as there is a better option.
The real solution are parameters. You just send the query text with a parameter placeholder instead of the real value and then supply the value afterwards, before running. A quick implementation could be something like this:
x = "John's";
OleDbCommand commandRdd = new OleDbCommand("Select rdd_tbl.Customer, rdd_tbl.Balance From rdd_tbl WHERE rdd_tbl.Customer = #customer", con);
commandRdd.Parameters.Add(new OleDbParameter("#customer", OleDbType.VarChar, 100, ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Default, x));
OleDbDataReader readerRdd = commandRdd.ExecuteReader();
A few things to notice. First, the query text contains rdd_tbl.Customer = #customer, a fixed text independent of the actual value you want to submit. then it comes the parameter declaration, where the name, data type and actual value are specified. Be sure to fill all those details and match them to the underlying DB structure. The rest of the code is exactly the same.
This also has the added benefit of improving performance. Because of the query text being exactly the same, the query can often be cached and reused afterwards if submitted again, even if the parameter value changes (those values are not part of the query itself). If you need to run the same query a few times in a row with different values, this makes possible to use prepared statements, which again are precompiled queries that run subsequently with different values. Those possible optimizations are all allowed by the API, but the actual results will vary depending the DB engine.
Escape ' with a \. Like so.
x = "John\'s";
OleDbCommand commandRdd = new OleDbCommand("Select rdd_tbl.Customer, rdd_tbl.Balance From rdd_tbl WHERE rdd_tbl.Customer ='" + x + "'", con);
OleDbDataReader readerRdd = commandRdd.ExecuteReader();
Also, use SQL Parameters like #Plutonix has suggested, else you are vulnerable to SQL Injection / "Bobby Tables"

What is the better way of making an SQL statement in Visual Studio?

I was looking up how to insert into my database via sql and I noticed the way I had seen a person do an sql statement was different from the way I had done it and and now I'm wondering which way is better.
An example of what I had done in a previous (select) statement.
SqlConnection conn = new SqlConnection(Variables.Default.sqlConString);
conn.Open();
string builtCmd = Variables.Default.returnUserNameSql1 + usersInput + Variables.Default.returnUsernameSql2;
SqlCommand cmd = new SqlCommand(builtCmd, conn);
usersInput is a string.
Variables.Default.returnUserNameSql1 = SELECT [Username] from [dbo].[LoginDetails] WHERE [Username] = '
returnUsernameSql2 = '
What I have seen online (not my query):
cmd.CommandText = "INSERT INTO klant(klant_id,naam,voornaam) VALUES(#param1,#param2,#param3)";
cmd.Parameters.Add(new SqlParameter("#param1", klantId));
cmd.Parameters.Add(new SqlParameter("#param2", klantNaam));
cmd.Parameters.Add(new SqlParameter("#param3", klantVoornaam));
Is the use of the Parameters function (?) better? If so in what way?
Thanks for your time.
I modified my original query thanks to the help of some of the comments here. I'll post it if anyone's interested:
using (SqlConnection conn = new SqlConnection(Variables.Default.sqlConString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(Variables.Default.returnUserNameSql, conn))
{
cmd.Parameters.Add(new SqlParameter(Variables.Default.param1, usersInput));
SqlDataReader reader = cmd.ExecuteReader();
usernameTaken = reader.Read();
cmd.Dispose();
}
conn.Close();
}
Look up "SQL Injection attack" on google. Bobby Tables says hello. And then realize that your way is not bad, it is a security nightmare because everyone with access to your program can execute whatever SQL he wants.
SQL Injection is certainly an important reason not to use string concatenation, but there are a few others:
string delimiters - you'd need to include string delimiters in your SQL statement, and if the values you concatenate include them as well, you'll likely get syntax errors. With parameters you don't need string delimiters, and values with apostro[phes or quotes don't affect the SQL syntax.
string conversion of values - you'd need to convert all non-string values (numbers, dates, etc.) to strings, and ensure that their string represenatations are exactly reversible by the server. This is especially problematic for dates since the same string can represent two different dates depending on the culture of the server. With parameters, the values are passed without translation, so there's no risk that the server misinterprets values.
pre-compilation - with concatenation, the server must reanalyze each query to determine the "best" plan. With parameters, the server can re-use a cached plan since the actual query has been issued before, just with different parameters. This doesn't mean that queries will always perform faster, and in some cases can actually cause bad plans to be used, but it is a consideration if you issue millions of queries that only differ in parameter values.

ORA-01036: illegal variable name/number when running query through C#

I am trying to use ALTER USER query for Oracle database using OracleCommand in C# in the following code. It creates the query if the values for Username and password are not empty strings. But I get an error "ORA-01036: illegal variable name/number" when ExecuteNonQuery() is executed.
string updateQuery = "ALTER USER :user IDENTIFIED BY :password";
connection = new OracleConnection(LoginPage.connectionString);
connection.Open();
OracleCommand cmd = new OracleCommand(updateQuery, connection);
cmd.Connection = connection;
for(int i=0;i<usersList.Count;i++)
{
if (!(selectedUsersArray[i].Equals("")) && !passwordArray[i].Equals(""))
{
OracleParameter userName = new OracleParameter();
userName.ParameterName = "user";
userName.Value = selectedUsersArray[i];
OracleParameter passwd = new OracleParameter();
passwd.ParameterName = "password";
passwd.Value = passwordArray[i];
cmd.Parameters.Add(userName);
cmd.Parameters.Add(passwd);
cmd.Prepare();
cmd.ExecuteNonQuery();
}
}
Could you please suggest what is wrong with my implementation?.
The root cause
In Oracle you have three kinds of SQL statements (and additionally there are PL/SQL blocks):
Statements in the Data Definiton Language (DDL). These statements modify the structure of the database. They begin usually with the verbs "ALTER" or "CREATE"
Statements in the Data Modification Langugage (DML). There statements modify the content inside of tables, leaving the structure of each table unmodified. These statements usually begin with "INSERT", "MERGE" or "DELETE".
Statements in what I call "query language" (there seems to be no canonical name for these). This statements start with the verb "SELECT".
Bind variables in Oracle are only allowed in some special places in DML and query statements. You are trying to use bind variables in a places where they are not allowed. Hence the error.
Solution
Build your statement without bind variables. Build the complete query string instead using string concatenation.
If you want to sanitize the input before concatenating the string, use the DBMS_ASSERT package.
Background
Bind variables can only be used when Oracle can build a query plan without knowing the value of the variable. For DDL statements, there is no query plan. Hence bind variables are not allowed.
In DML and query statements, bind variables are only allowed, when they are used inside a tuple (regarding the underlying set theory), i.e. when the value will be compared with the value in a table or when the value will be inserted in a table. They are not allowed to change the structure of the execution plan (e.g. to change the target table or to change the number of comparisons).
Just for others getting this error and looking for info on it, it is also thrown if you happen to pass a binding parameter and then never use it. I couldn't really find that stated clearly anywhere but had to prove it through trial and error.
I just spent several days checking parameters because I have to pass 60 to a stored procedure. It turns out that the one of the variable names (which I load into a list and pass to the Oracle Write method I created) had a space in the name at the end. When comparing to the variables in the stored procedure they were the same, but in the editor I used to compare them, I didnt notice the extra space. Drove me crazy for the last 4 days trying everything I could find, and changing even the .net Oracle driver. Just wanted to throw that out here so it can help someone else. We tend to concentrate on the characters and ignore the spaces. . .
You defined one oracleCommand but used it in 'for'.
it means you are adding parameter with the same name to one OracleCommand.
you should use cmd.Parameters.clear() to refresh your parameters.
for(int i=0;i<usersList.Count;i++)
{
if (!(selectedUsersArray[i].Equals("")) && !passwordArray[i].Equals(""))
{
cmd.Parameters.clear();//Add this line
OracleParameter userName = new OracleParameter();
userName.ParameterName = "user";
userName.Value = selectedUsersArray[i];
OracleParameter passwd = new OracleParameter();
passwd.ParameterName = "password";
passwd.Value = passwordArray[i];
cmd.Parameters.Add(userName);
cmd.Parameters.Add(passwd);
cmd.Prepare();
cmd.ExecuteNonQuery();
}
}
The Oracle error ORA-01036 means that the query uses an undefined variable somewhere. From the query we can determine which variables are in use, namely all that start with #. However, if you're inputting this into an advanced query, it's important to confirm that all variables have a matching input parameter, including the same case as in the variable name, if your Oracle database is Case Sensitive.
This error happens when you are also missing cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("GUSERID ", OracleType.VarChar)).Value = userId;
I was having eight parameters and one was with space at the end as shown in the above code for "GUSERID ".Removed the space and everything started working .
I was having the same problem in an application that I was maintaining, among all the adjustments to prepare the environment, I also spent almost an hour banging my head with this error "ORA-01036: illegal variable name / number" until I found out that the application connection was pointed to an outdated database, so the application passed two more parameters to the outdated database procedure causing the error.
You cannot pass user/table name to pl/sql with a parameter. You can create a procedure and build sql and then execute immediately to achieve that.
I have faced same problem ... For the problem is like this, I am calling the PRC inside cpp program and my PRC taking 4 arguments but while calling I used only 1 arguments so this error came for me.
Begin Example_PRC(:1); End; // this cause the problem
Begin Example_PRC(:1,:2,:3,:4); End; // this is the solution
I had the same issue today when using Python module cx_Oracle. In my case, the root cause was an invalid variable name.
Example: SELECT * FROM DATA WHERE KEY IN (:_0, :_1, ...)
When I changed _0 to var0, it worked fine.
From this blog post, I found these rules for variable names:
Must start with a letter
Maximum size is limited to 30 letters
Cannot contain whitespace characters
Can contain dollar sign ('$'), underscore ('_') and hash sign ('#')
Is case-insensitive
I had the same problem, was learning connection to oracledb.
previous code-
SELECTALLCANDIDATES = "Select * from candidate_master";
data= await connection.execute(SELECTALLCANDIDATES, {autoCommit:true})
Removed the {autoCommit:true} and it started working fine.Correct code-
SELECTALLCANDIDATES = "Select * from candidate_master";
data= await connection.execute(SELECTALLCANDIDATES)
Still don't know why but it works.
Check your named variables match, I had the same problem, I had a spelling mistake/typo in one of my parameters

Insert adding items to wrong column

Below is an insert statement that is inputting the first value (acctNum) into the second column (itemCode) and vice versa:
string addUpcCode = "INSERT INTO compare1 (acct, itemcode)"
+ "VALUES ('"+acctNum+"', '"+itemCode+"')";
This is not how I want it to work, as I want the first value to go in the first column and the second in the second column. How can I go about this?
Side note: This is a rough draft until I learn more about parameterization. I won't be releasing this code until I learn and implement it.
The given SQL snipplet will do what you want - especially the sequence of columns will be preserved.
I suspect you might have switched your variables, leading to the same phenomen but out of an other reason.
You should use SQL Parameters which also avoids creating a SQL injection problem:
using (SqlCommand command = new SqlCommand("INSERT INTO compare1 (acct, itemcode) VALUES (#AcctNum, #ItemCode)", connection))
{
// Add new SqlParameter to the command.
command.Parameters.Add(new SqlParameter("AcctNum", acctNum));
command.Parameters.Add(new SqlParameter("ItemCode", itemCode));
As you're using C# the string.format function is perfect for this kind of thing
string.format("INSERT INTO compare1(acct, itemCode) Values('{0}','{1}'", acctNum, itemCode);
or
string.format("INSERT INTO compare1 SELECT '{0}','{1}' ", acctNum, itemCode);
But yeah, you probably have them mixed up somewhere else, the order will be kept. Oh and if acctNum is an integer, you won't need the quotation marks around {0}
Edit:
Oh and yeah, definitely look into parameters next
Use parametrized queries to prevent SQL injection and you can solve this matter
command.Parameters.Add("AcctNum", acctNum);

Categories