Parameterized query which sends NULL value, with C# and SQL Server - c#

I tried to pass some variables into a parameterized query but when the value is NULL it throws an exception that says the value is not provided.
How can I fix that without an if or something like that?
My code is this
var cmdPersona_Log = new SqlCommand();
cmdPersona_Log.Parameters.Clear();
cmdPersona_Log.Connection = mySqlConnection;
cmdPersona_Log.CommandType = CommandType.Text;
cmdPersona_Log.CommandText = #"INSERT INTO [Tomin].[TominRH].[Persona_Log] "
+ "([Id_Action],[Id_User],[Id_Date],[Id_Entidad],[Nombre],[Paterno],[Materno],[Sexo],[Id_Nacionalidad])"
+ " Values (1, 'Admin', #fecha, #id_entidad, #nombre, #paterno, #materno, 1, 52)";
cmdPersona_Log.Parameters.Add("#fecha", DateTime.Now);
cmdPersona_Log.Parameters.Add("#id_entidad", dbRow["CUENTA"]);
cmdPersona_Log.Parameters.Add("#nombre", nombre);
cmdPersona_Log.Parameters.Add("#paterno", paterno);
cmdPersona_Log.Parameters.Add("#materno", materno);
cmdPersona_Log.ExecuteNonQuery();

I will assume that your problem is that the underlying field doesn't support nulls. If this is the case, you could do something like this
cmdPersona_Log.Parameters.Add("#nombre", nombre ?? string.Empty);
cmdPersona_Log.Parameters.Add("#paterno", paterno ?? string.Empty);
cmdPersona_Log.Parameters.Add("#materno", materno ?? string.Empty);
You'll be inserting an empty string in case you have a null using the null-coalescing operator.

Related

.NET: How to deal with possible null values in an SQL query?

Code:
private void DoSomethingWithDatabase(string f1, int f2)
{
SqlCommand myCommand = new SqlCommand("SELECT Field1,Field2,Field3 FROM MyTable WHERE Field1 = #F1 AND Field2 = #F2", this.myConn);
myCommand.Parameters.Add("#F1", System.Data.SqlDbType.VarChar);
myCommand.Parameters.Add("#F2", System.Data.SqlDbType.Int);
if (f1 == "")
myCommand.Parameters["#F1"].Value = DBNull.Value;
else
myCommand.Parameters["#F1"].Value = f1;
if (f2 < 0)
myCommand.Parameters["#F2"].Value = DBNull.Value;
else
myCommand.Parameters["#F2"].Value = f2;
// code to do stuff with the results here
}
The server is a Microsoft SQL Server instance.
The database table MyTable contains fields which are nullable. Therefore null is a valid value to search on when performing the query.
From my reading, and also from testing code like this, doing something like what I did here doesn't work properly because apparently you can't do an "equals null" comparison this way - you're supposed to do "IS NULL".
It looks like you can correct this and make it work by setting ANSI_NULL to OFF (as per https://msdn.microsoft.com/en-us/library/ms188048.aspx) but it also indicates this method is deprecated and should not be used.
That article suggests you can use an OR operator to do something like WHERE Field1 = 25 OR Field1 IS NULL. The problem is, with a single call to this function, I want to check for either null and only null, or for the given constant value and nothing else.
So far, it seems like I need to basically build the query piece by piece, string by string, to account for the possibility of NULL values. So I'd need to do something like:
string theQuery = "SELECT Field1,Field2,Field3 FROM MyTable WHERE ";
if (f1 == "")
theQuery += "Field1 IS NULL ";
else
theQuery += "Field1 = #F1 ";
// ...
SqlCommand myCommand = new SqlCommand(theQuery, this.myConn);
if (f1 == "")
{
myCommand.Parameters.Add("#F1", System.Data.SqlDbType.VarChar);
myCommand.Parameters["#F1"].Value = f1;
}
// ...
Is this really how it needs to be done? Is there a more efficient way to do this without repeating that if block and by using parameters rather than concatenating a query string together?
(Notes: An empty string is converted to a NULL here for the example. In the scenario I'm actually working with, empty strings are never used, but instead NULL is stored. The database is out of my control, so I can't just say "change all your NULLs to empty strings". Same goes for the ints - if we pass, say, -1 into the function it should be testing for a null value. Bad practice? Maybe, but the database itself is not in my control, only the code to access it is.)
Why not using:
string theQuery = "SELECT Field1, Field2, Field3 FROM MyTable WHERE ISNULL(Field1,'') = #F1";
?
That way you get rid of your if block and your null values are interpreted as an empty string, like your f1 variable.
SQL Server has a function called ISNULL(Column,Value) where you can specify one column to check and set a Default Value in case this Column is NULL.
You can check here
You could do something like
WHERE ISNULL(Field, '') = #F1
In that case, NULL fields are treated like empty strings. Another way would be:
WHERE Field IS NULL OR Field = #1
The way you are dealing with NULLs is the right way. Maybe you could use a helper method like this one:
private string AppendParameter(string query, string parameterName, string parameterValue, SqlParameterCollection parameters)
{
if (string.IsNullOrEmpty(parameterValue))
query += "Field1 IS NULL ";
else
{
query += "Field1 = " + parameterName + " ";
parameters.AddWithValue(parameterName, parameterValue);
}
return query;
}
SqlCommand myCommand = new SqlCommand(#"SELECT Field1,Field2,Field3
FROM MyTable
WHERE
( (#F1 IS NULL AND [field1] IS NULL) OR [field1] = #F1 ) AND
( (#F2 IS NULL AND [field2] IS NULL) OR [field2] = #F2 );", this.myconn);
myCommand.Parameters.Add("#F1", System.Data.SqlDbType.VarChar).Value = DBNull.Value;
myCommand.Parameters.Add("#F2", System.Data.SqlDbType.Int).Value = DBNull.Value;
if (!string.IsNullOrEmpty(f1))
myCommand.Parameters["#F1"].Value = f1;
if (f2 != -1)
myCommand.Parameters["#F2"].Value = f2;
This would utilize indexes on fields.
If you want to avoid if blocks, you can probably change the query into something like this:
SELECT Field1,Field2,Field3 FROM MyTable
WHERE COALESCE(Field1,'') = #F1 AND COALESCE(Field2,-1) = #F2

How to convert nullable int to SQL Null value?

I need to perform an update to a View that has multiple underlying tables using the ExecuteCommand method of a DataContext. I am using this method because of the known restriction of linqToSQL when performing this type of operation on Views having multiple underlying tables.
My existing SQL statement is similar to the following where I am setting newFieldID to a null value simply for this post to illustrate the issue. In the application, newFieldID is assigned a passed parameter and could actually be an integer value; but my question is specific to the case where the value being provided is a null type:
using (var _ctx = new MyDataContext())
{
int? newFieldID = null;
var updateCmd = "UPDATE [SomeTable] SET fieldID = " + newFieldID + "
WHERE keyID = " + someKeyID;
try
{
_ctx.ExecuteCommand(updateCmd);
_ctx.SubmitChanges();
}
catch (Exception exc)
{
// Handle the error...
}
}
This code will fail for the obvious reason that the updateCmd won't be completed in the case of a null value for the newFieldID. So how can I replace or translate the CLR null value with an SQL null to complete the statement?
I know I could move all of this to a Stored Procedure but I am looking for an answer to the existing scenario. I've tried experimenting with DBNull.Value but aside from the challenge of substituting it for the newFieldID to use in the statement, simply placing it into the string breaks the validity of the statement.
Also, enclosing it within a single quotes:
var updateCmd = "UPDATE [SomeTable] SET fieldID = '" + DBNull.Value + "'
WHERE keyID = " + someKeyID;
Will complete the statement but the value of the field is translated to an integer 0 instead of an SQL null.
So How does one go about converting a CLR null or nullable int to an SQL Null value given this situation?
Correct way to do it: use override of ExecuteCommand accepting not only command text, but also array of parameters and use parameterized query instead of command string concatenation:
var updateCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
_ctx.ExecuteCommand(updateCmd, new [] {newFieldID, someKeyID});
It will not only prevent you from sql injection, but also it will do following for you (from MSDN description):
If any one of the parameters is null, it is converted to DBNull.Value.
Try checking newFieldID == null and change the statement accordingly.
Something like below or using separate if / else statement.
var updateCmd = "UPDATE [SomeTable] SET fieldID =" + (newFieldID == null ? "null" : Convert.ToString(newFieldID)) + " WHERE keyID = " + someKeyID;
Normally, when using Stored Procedures or Prepared Statements, you use Parameters to assign values. When you have a DbParameter, you can assign null or DBNull.Value to the Value-Property or your parameter.
If you want to have the null as text in the statement, simply use the SQL-keyword NULL
var updateCmd = "UPDATE [SomeTable] SET fieldID = NULL WHERE keyID = " + someKeyID;
As pointed out by Andy Korneyev and others, a parameterized array approach is the best and probably the more appropriate method when using Prepared statements. Since I am using LinqToSQL, the ExecuteCommand method with the second argument which takes an array of parameters would be advised but it has the following caveats to its usage.
A query parameter cannot be of type System.Nullable`1[System.Int32][] (The main issue I'm trying to resolve in this case)
All parameters must be of the same type
Shankar's answer works although it can quickly become very verbose as the number of parameters could potentially increase.
So my workaround for the problem involve somewhat of a hybrid between the use of parameters as recommended by Andy and Shankar's suggestion by creating a helper method to handle the null values which would take an SQL statement with parameter mappings and the actual parameters.
My helper method is:
private static string PrepareExecuteCommand (string sqlParameterizedCommand, object [] parameterArray)
{
int arrayIndex = 0;
foreach (var obj in parameterArray)
{
if (obj == null || Convert.IsDBNull(obj))
{
sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", "NULL");
}
else
sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", obj.ToString());
++arrayIndex;
}
return sqlParameterizedCommand;
}
So I can now execute my statement with parameters having potential null values like this:
int? newFieldID = null;
int someKeyID = 12;
var paramCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
var newCmd = PrepareExecuteCommand(paramCmd, new object[] { newFieldID });
_ctx.ExecuteCommand(newCmd);
_ctx.SubmitChanges();
This alleviates the two previously referenced limitations of the ExecuteCommand with a parameter array. Null values get translated appropriately and the object array may varied .NET types.
I am marking Shankar's proposal as the answer since it sparked this idea but posting my solution because I think it adds a bit more flexibility.

INSERT query - Null vs Empty String .. does it matter?

I have the following code that inserts some data into an Access table. It starts with a comma delimited line that I split into a string array, then add the array elements as parameters:
string line = "one\ttwo\t\tfour";
string[] values = line.Split('\t');
using (OleDbCommand com = new OleDbCommand("INSERT INTO [MyTable] (" +
"[Field1], [Field2], [Field3], [Field4] "
") VALUES (" +
"?, ?, ?, ?" +
")", con))
{
com.Parameters.Add("#p0", OleDbType.Char, 255).Value = (object)values[0] ?? DBNull.Value;
com.Parameters.Add("#p1", OleDbType.Char, 255).Value = (object)values[1] ?? DBNull.Value;
com.Parameters.Add("#p2", OleDbType.Char, 255).Value = (object)values[2] ?? DBNull.Value;
com.Parameters.Add("#p3", OleDbType.Char, 255).Value = (object)values[3] ?? DBNull.Value;
com.ExecuteNonQuery();
}
In the above example, #p2 will not have a value. It gets inserted into the Access table as a blank value, instead of a null value. At least I think I'm explaining that correctly. If I query for Field3 is Null I get no results. If I query for Field3 = "" I do get results. Does it matter if it's blank or null? Is there a 'preferred' option?
Thanks!
Well, null is definitely not the same as an empty string. So, yes, it matters. But only in your domain. So in other words, if the field isn't really nullable and Access is interpreting DBNull.Value as an empty string, then you just have to keep that in mind when looking for rows with no value. If the column were nullable in Access you wouldn't be seeing this behavior.
However, if the field in nullable, then I would recommend you actually just send in null instead of DBNull.Value. So, in other words:
values[0];
instead of:
(object)values[0] ?? DBNull.Value;
Original statement:
com.Parameters.Add("#p2", OleDbType.Char, 255).Value = (object)values[2] ?? DBNull.Value;
From the description it is sending the empty string '' from values[2], not the DBNull.Value. I would explicitly convert the empty string (or null) rather than using the null-coalescing operator (??):
com.Parameters.Add("#p2", OleDbType.Char, 255).Value = String.IsNullOrEmpty(values[2]) ? DBNull.Value : values[2];
Added: As the type-specification (OleDbType.Char) is preventing this from sending DBNull to the database, I would revert to AddWithValue:
com.Parameters.AddWithValue("#p2", String.IsNullOrEmpty(values[2]) ? DBNull.Value : values[2]);
This doesn't specify the type so should accept DBNull or the string. I prefer this approach anyway: if we specify the type we need to ensure that it is entirely consistent with the field data-type -> let C# work this out.

SqlCommand SUM(NVL()) issue

Hello I'm trying to select SUM of all payments but got this exception: nvl is not a recognized function name
with this code:
SqlCommand sc2 = new SqlCommand("SELECT SUM(NVL(payments,0)) AS sumcastka FROM kliplat WHERE akce=" + zakce.Text, spojeni);
spojeni.Open();
int sumOfPrice = 0;
object vysledek2 = sc2.ExecuteScalar();
if (vysledek2 != DBNull.Value)
sumOfPrice = Convert.ToInt32(vysledek2);
// int vysledek2 = Convert.ToInt32(sc2.ExecuteScalar());
spojeni.Close();
This should work as when no records are found for column "payments" I would like to get "0" if possible.
Thank you for reading this.
NVL() is an oracle-specific function. You can use the ANSI COALSECE function to perform the same task. The benefit of COALESCE is that it takes more than two parameters, and picks the first non-null value.
This should work as when no records are found for column "payments"
No, it will only treat NULL values in the payments column as 0.
If no records are found, then ExecuteScalar returns null (not DBNull):
SqlCommand sc2 = new SqlCommand("SELECT SUM(ISNULL(payments,0)) AS sumcastka FROM kliplat WHERE akce=" + zakce.Text, spojeni);
spojeni.Open();
int sumOfPrice = 0;
object vysledek2 = sc2.ExecuteScalar();
if (vysledek2 != null && vysledek2 != DBNull.Value)
sumOfPrice = Convert.ToInt32(vysledek2);
spojeni.Close();
You should also look into using SqlParameters instead of concatenating strings, but that's a separate issue.
In SQL server there is a funcation called ISNULL for the purpose. Please find the query below:
SELECT SUM(ISNULL(payments,0)) AS sumcastka FROM kliplat WHERE akce=" + zakce.Text, spojeni

Update command error

I want to update a row by code in my form. Maybe some columns are null. So while command is
executing, an error will rise and say Incorrect syntax near the keyword 'where':
SqlCommand Update = new SqlCommand("Update Table_065_Turn SET Column02=" + Row["Column48"] + " , Column15= " + Row["Column15"].ToString() +
" where ColumnId=" + StatusTable.Rows[0]["ColumnId"], Con);
Update.ExecuteNonQuery();
I know this error will be displayed because Row["Column15"] is null.
How can I check if the column in datarow is null; of course without any extra variable or commands before Update command.
I mean check columns exactly in update command.
I would recommend using SqlParameters, also SqlCommand implements IDisposable so you should wrap it up in a using statement e.g.
using (SqlCommand update = new SqlCommand("Update Table_065_Turn SET Column02=#Col2, Column15=#Col15 where ColumnId=#ColId", con))
{
update.Parameters.AddWithValue("#Col2", Row["Column48"]);
update.Parameters.AddWithValue("#Col15", Row["Column15"]);
update.Parameters.AddWithValue("#ColId", StatusTable.Rows[0]["ColumnId"]);
update.ExecuteNonQuery();
}
Also you might be better actually validating the fields before you execute the query unless null is a valid column value.
I suspect you have to "replace" .net null with database keyword NULL, e.g.
string sql = "Column15 = " + (row[15] == null ? "NULL" : row[15].ToString()) in your case, but the much better way is to use Parameters as written by James, also keep in mind someone could provide hamful strings to your query:
row[15] = ";DROP DATABASE; --" would be enough in your case to cause all your data to be lost ;) (see "SQL injection" on your favorite search engine
you could use
var value = "" + Row["columnname"] ;
so ,you do not need to check the object is null
it is safe..
You could do like this:
string filterString;
if (StatusTable.Rows[0]["ColumnId"]!=System.DBNull.Value)
filterString= #" WHERE ColumnID= StatusTable.Rows[0]["ColumnId"]";//I assume the value returned is a string
else
filterString="";
And then you can just append the filterString variable to your SQLCommand string.

Categories

Resources