Getting C# To Insert Defaults Into SQL Server - c#

How can I INSERT values into SQL Server that are stored in a string[] such that some of the values should be disregarded in favor of the values stored in SQL Server as default constraints on the table? What do I need to pass(e.g. NULL or something else) to use those defaults?
Currently, I add all the defaults in my code, and it is getting bulky.
Below is my code:
if (value == "") value = "0";
string insert = "INSERT INTO " + table + " (" + columns + ") VALUES (" + atColumns + ")";
using (SqlConnection connect = new SqlConnection(connection))
{
using (SqlCommand command = new SqlCommand(insert, connect))
{
//adds values to corresponding parameters
for (int i = 0; i < table_cols.Length; i++)
{
command.Parameters.AddWithValue("#" + table_cols[i], table_vals[i]);
}
foreach (SqlParameter Parameter in command.Parameters)
{
if (Convert.ToString(Parameter.Value) == "")
{
Parameter.Value = DBNull.Value;
}
}
connect.Open();
command.ExecuteNonQuery();
response = "Success";
return response;
If a specific Parameter.Value is not-null and has a default set by SQL Server, this code will not work with null.
In SQL Server, this gets handled by omitting the value in your insert statement (this omission triggers inserting the default value for the table, whereas providing null produces errors).

If you want SQL Server to use the default value constraint for the column then don't include the column as part of the insert parameters.
Example:
--From SQL Server
CREATE TABLE Orders
(
Id INT IDENTITY PRIMARY KEY
,Amount INT NOT NULL
,Cost MONEY NOT NULL
,SaleDate DATE NOT NULL DEFAULT GETUTCDATE()
);
//From C#
public int Insert(decimal cost, int amount)
{
using (var connection = new SqlConnection(connectionString))
{
var command = connection.CreateCommand();
//Don't specify the SaleDate and it will insert the current time
command.CommandText = "INSERT INTO Orders(Amount, Cost) VALUES(#Amount, #Cost); SELECT SCOPE_IDENTITY();";
command.Parameters.AddWithValue("#Amount", amount);
command.Parameters.AddWithValue("#Cost", cost);
using(var reader = command.ExecuteReader())
{
if(reader.Read())
return Convert.ToInt32(reader[0]);
}
}
return 0;
}
If you want to use a parameters list in your C# code then just keep the parameter names grouped with their values and if the value is null and it has a default value then just skip it.

Passing in a NULL tells SQL that you want a NULL in that column overriding the default. If you want to pass something in pass in the keyword DEFAULT. I wrote an article, "keyword DEFAULT", about the usage:
The DEFAULT keyword causes the default value (from the constraint) to be inserted into the column.
Just remember that when you pass in DEFAULT don't put quotes around it. That makes it the string DEFAULT rather than the keyword DEFAULT.

The only other way of doing it I can think of would be triggers based your approach (and you're better off coding it at that point).
However, if you alter your approach to use stored procedures, you can do your value handling natively in SQL, otherwise you're stuck coding it into your app... might i recommend Ternary statements for your example: http://msdn.microsoft.com/en-us/library/ty67wk28%28v=vs.80%29.aspx .

If you include a column in the column list, it will try and insert the value you give, it. It will not assume that NULL means "just insert the default value".
If you don't want to insert a value into that column, don't include it in your column list (or value list, obviously).
While it may seem more efficient to loop through the table columns and be agnostic of the column name, type, etc. In the long run you may be better off handling each column explicitly so you can choose whether or not to use a default, verify the value, etc.

I actually used the INFORMATION_SCHEMA.COLUMNS table to pull back from SQL Server all of the Column Defaults. Then I just organized the defaults into a string[] and looped through it to insert defaults rather than nulls (some defaults are null).

Related

c# SQLite Insert Into Command with parameter

I am using c# to insert a record into the SQLite DB table 'VS_Types' with one parameter from table 'VS_Groups'. I test it with SQLite Browser and it works. While I implement the same sqlite command inside the C# with System.Data.SQlite. It doesn't work.
I modify the commandText to as following it works. Looks like the parameter '#value0' influence the c# SQLite command execution.
cmd.CommandText = "INSERT INTO VS_Types(Name, Group_id, Color) Values('20', 10, 1245679);";
SQLite SQL:
SELECT #value0=ID FROM VS_Groups WHERE (Name = 'Ato_h');
INSERT INTO VS_Types(Name, Group_id, Color) VALUES('20', #value0, 65536);
The Code inside the C#
using (SQLiteConnection conn = new SQLiteConnection(cs))
{
try
{
conn.Open();
SQLiteCommand cmd = new SQLiteCommand(conn);
cmd.CommandText = "Select #groudId = ID From VS_Groups Where(Name = 'Ato_h'); "
+ "INSERT INTO VS_Types(Name, Group_id, Color) Values('20', #groudId, 1245679);";
cmd.ExecuteNonQuery();
conn.Close();
testOutput.WriteLine("Insert Successful");
}
catch (Exception ex)
{
this.testOutput.WriteLine("Failed to open connection: {0}", ex.Message);
}
}
Please give me some suggestions about how to use the parameter inside C# Sqlite command in this situation.
Update: the VS_Groups and VS_Types table and its content
CREATE TABLE "VS_Groups" (
`ID` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT,
`Width` INTEGER,
`Height` INTEGER,
`Flags` INTEGER,
`Limi_recognition` INTEGER,
`Base` TEXT,
`Flags1` INTEGER,
`Limit_recognition` INTEGER,
`Flags2` INTEGER,
`Limit_recognition2` INTEGER,
`Distance_threshold` INTEGER
)
CREATE TABLE `VS_Types` (
`ID` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT,
`Group_id` INTEGER,
`Color` INTEGER
)
The existing record is:9,Ato_h,160,140,65536,,,,400,,,100
Sqlite SQL does not work like that. You cannot declare and store a value in a variable, then reuse that variable later. Terms like #groudId are only placeholders for parameters passed to the query. That means that the expression #groudId = ID is NOT an assignment, rather it is a comparison. Since you are not binding the parameter #groudId to anything, the parameter value is null, so the expression is a comparison like null = ID which will result in false which numerically is 0 (zero). The select statement returns 0 and is not used in the INSERT statement.
If the INSERT is working at all, it is probably resulting in something like INSERT INTO VS_Types(Name, Group_id, Color) Values('20', null, 1245679);
At the end of the question, you also say "The existing record is ..." and you only show a single record for VS_Groups, although the insert statement is for the table VS_Types. But you don't show output for the VS_Types table! You are inspecting the wrong table for the inserted data. If you query the table VS_Types, you will likely find many records with Name == '20', GroupID == null and Color == 123456789... exactly as the INSERT statement says.
In summary, you are not using parameters correctly, but you really don't even need a parameter in the code you show, so it is difficult to know how to answer properly. An answer showing proper use of parameters would be wasted, but a replacement SQL may not be want you want in the end either. I suggest researching parameters separately to learn how to use them properly. For now, use this nested statement:
INSERT INTO VS_Types(Name, Group_id, Color)
VALUES ('20', (Select ID From VS_Groups Where Name = 'Ato_h'), 1245679);
Regrettably that's not all. The table definition does not show that VS_Groups.Name is unique, so technically there could be multiple rows that match the nested query, so the INSERT statement could still fail. I suggest adding a UNIQUE constraint to the VS_Groups.Name column.

Get output of the updated row only if the updated row is not null?

I am having issues when trying to get the result of an updated row, the query that I use as shown below update a certain row when it meets a specific conditions,however, and though the conditions are not met, the Update query output gets the value "0" rather than NULL, Is there any tips so that I can fix this in the same query,with that said, I'm trying to avoid the use of other queries because it's within a method that has to optimised the most possible.
SqlCommand loadbonus = new SqlCommand();
loadbonus.Connection = conn;
loadbonus.CommandText = "UPDATE Client SET [Restant Bonus]=[Restant Bonus]-(SELECT SUM([Prix Total]) FROM Comptoir WHERE [N° Comptoir]='" + tabcontrol.SelectedIndex + "'),[Total Consommé]=[Total Consommé]+(SELECT SUM([Prix Total]) FROM Comptoir WHERE [N° Comptoir]='" + tabcontrol.SelectedIndex + "') OUTPUT INSERTED.[Restant Bonus] WHERE [Code Client]=#CodeClient AND [Bonus Fidélité]='1'";
loadbonus.Parameters.Add("#CodeClient", SqlDbType.Int).Value = string.IsNullOrWhiteSpace(clientcodecomptoire.Text) ? DBNull.Value : (object)Convert.ToInt32(clientcodecomptoire.Text);
int restantbonus = Convert.ToInt32(loadbonus.ExecuteScalar());
if (restantbonus <= 0)
{// here is the issue! the left side of the If statement can containt there "0" value for both a "0" value and a Null value}
Using a stored procedure has many benefits. It starts to get you a separate data layer from the application. It is also parameterized so you can avoid sql injection.
Here is what your procedure might look like.
create procedure Client_Update
(
#NComptoir int
) as
set nocount on;
declare #MyTotal int;
SELECT #MyTotal = SUM([Prix Total])
FROM Comptoir
WHERE [N° Comptoir] = #NComptoir;
UPDATE Client
SET [Restant Bonus] = [Restant Bonus] - #MyTotal
,[Total Consommé] = [Total Consommé] + #MyTotal;
select #MyTotal;
Then to use in dotnet you should change up a few things. You need to use the USING statement around objects like commands and connections to ensure they get properly disposed of. You already have a connection object so I am leaving that to you.
using (SqlCommand loadbonus = new SqlCommand("Client_Update", conn))
{
loadbonus.CommandType = CommandType.StoredProcedure;
loadbonus.Parameters.Add("#NComptoir", SqlDbType.Int).Value = tabcontrol.SelectedIndex;
int restantbonus = Convert.ToInt32(loadbonus.ExecuteScalar());
if (restantbonus <= 0)
{
}
}
The issue I see though is that you add a parameter (#Seuil) in your code but it is not part of the update statement. And since you are setting the value to the text of a textbox it will get 0 when set to an empty string. That is because it is being converted to an int. I have no idea what the purpose of that parameter is though because it is not part of your query.
I hope this helps at least give you a push in the right direction.

Setting DEFAULT value using SQLiteParameter

My query is supposed to add an additional column "_DisableAccounting" to the "Settings_Global" table, specifying a DEFAULT value using a SQLiteParemeter.
ALTER TABLE Settings_Global ADD COLUMN `_DisableAccounting` BOOL NOT NULL DEFAULT #defaultValue;
When the SQLiteCommand is executed, #defaultValue should be replaced by the supplied parameter.
// ....
SQLiteParameter defaultValueParam = null;
if (defaultValue != null) {
query += " DEFAULT #defaultValue`";
defaultValueParam = new SQLiteParameter { ParameterName = "#defaultValue", Value = defaultValue };
}
query += ";";
using (SQLiteCommand cmd = _Connection.CreateCommand()) {
cmd.CommandText = query;
if (defaultValueParam != null) {
cmd.Parameters.Add(defaultValueParam);
}
cmd.ExecuteNonQuery();
}
// ....
However, I get the error "'SQL logic error or missing database near
#defaultValue": syntax error'". So it seems that the command is never replacing #defaultValue with the actual value.
Why isn't this working? I am doing essentially the same with MySQL (my application can optionally use MySQL) and it is working.
You appear to have a spurious grave accent. That is #defaultValue is followed grave accent, when it likely shouldn't be.
Try changing
ALTER TABLE Settings_Global ADD COLUMN `_DisableAccounting` BOOL NOT NULL DEFAULT #defaultValue`;
to
ALTER TABLE Settings_Global ADD COLUMN `_DisableAccounting` BOOL NOT NULL DEFAULT #defaultValue;
or to
ALTER TABLE Settings_Global ADD COLUMN `_DisableAccounting` BOOL NOT NULL DEFAULT `#defaultValue`;
When doing ALTER TABLE, SQLite does not modify the column definition in any way and just inserts it directly at the end of the CREATE TABLE statement.
This means that the parameter marker would end up in that CREATE TABLE statement, which would result in the parameter value not being available when the statement is interpreted later.
You have to insert the default value directly into the SQL command.

tinyint in SQL Server to byte in C#

In database management and application development we must be mindful of space and memory requirements. I have always been taught to use the data type that uses the least amount of space for your needs.
In my scenario, I have a column in a table that stores values {0,1,2,3,4} (SQL Server 2012). For this I chose to use the tinyint datatype. These values are pulled from the database into a C# application. Currently, I cannot get this tinyint data type to convert to a byte. When I try, I get an error "Cannot implicitly convert int to byte". If I change the datatype in the application to an integer, I can pull it just fine. Same with a string.
For performance purposes, is it okay to use integers throughout my entire application where I would normally use byte? If not, how do you convert an integer to a byte?
This is the code that I use that gives an error:
string strSQL = "SELECT securityLevel FROM security WHERE employeeID=#empID;";
using (SqlConnection dbConn = new SqlConnection(connParam))
{
dbConn.Open();
byte level = 0;
using (SqlCommand dbCommand = new SqlCommand(strSQL, dbConn))
{
dbCommand.CommandType = System.Data.CommandType.Text;
dbCommand.Parameters.AddWithValue("#empID", "12345");
using (SqlDataReader dbReader = dbCommand.ExecuteReader())
{
while (dbReader.Read())
{
level = dbReader.GetByte(0);
}
}
}
Console.WriteLine(level);
Console.ReadLine();
}
I have also tried:
level = (byte)dbReader.GetValue(0);
Yes, you were correct to pick TINYINT as the datatype if you are storing only 0 - 4.
Yes, TINYINT equates to a byte in .Net. You can see a list of mappings here:
http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
No, you did not actually use TINYINT when creating the table, else you would not be getting this error. The error message is very specific about the source datatype being INT.
No, do not use INT to store these values. That is unnecessary and what you are attempting to do (i.e. TINYINT and byte) is perfectly valid and I have done it many times.
Assuming you do not have millions of rows of data in that table and constant hits against it, run the following:
ALTER TABLE [security] ALTER COLUMN [securityLevel] TINYINT NOT NULL;
(I am assuming that the column is currently NOT NULL and in that case, if you leave off the NOT NULL in the ALTER TABLE statement, it will change the field to TINYINT NULL. If the field isn't currently NOT NULL, then just leave that part off)
I wrote the following LinqPad (against SqlServer express) to verify that you can read in a tinyint as a byte using .NET and SqlServer:
var cb = new SqlConnectionStringBuilder { DataSource = #".\Sqlexpress", InitialCatalog = "Medallion_OData_Tests_CustomersContext251990930203", IntegratedSecurity = true };
using (var c = new SqlConnection(cb.ConnectionString))
{
c.Open();
var cmd = c.CreateCommand();
cmd.CommandText = "SELECT CAST(1 AS tinyint)";
var reader = cmd.ExecuteReader();
reader.Read();
reader.GetByte(0).Dump();
}
This suggests to me that the actual securityLevel column in your database is not of the type TINYINT. To verify, why not temporarily modify your select query to include a CAST to TINYINT as in my example? If this works, that will confirm that the table schema is the problem.
Other ways to check the actual table schema include querying sys.columns or highlighting the name of the table in SqlServer Management Studio and hitting ALT+F1.
I think safest way is to use Convert.ToByte Method:
level = Convert.ToByte(dbReader.GetValue(0));
It converts from many value types to byte.

ExecuteScalar() returns null altough data was added to DB

I have a code as bellow where I try to insert a data into a table and return the ID (given by auto increment) of the new element.
int newEquipmentID = new int();
query = database.ParameterizedQueryString("INSERT INTO Equipment (EquipmentTypeID) VALUES ({0})", "equipmenttypeID");
newEquipmentID = (int)database.Connection.ExecuteScalar(query, DefaultTimeout, equipment.EquipmentTypeID);
But it fails and returns null, as if the new item wasn't added yet. But actually I can see the new item making a simple consult at the DB.
My question is "when" the data is actually added into the DB and how can I get the ID of the new added item.
Thanks!
You don't need two queries to create the new record and retrieve the new identity value:
using (var con = new SqlConnection(ConnectionString)) {
int newID;
var cmd = "INSERT INTO foo (column_name)VALUES (#Value);SELECT CAST(scope_identity() AS int)";
using (var insertCommand = new SqlCommand(cmd, con)) {
insertCommand.Parameters.AddWithValue("#Value", "bar");
con.Open();
newID = (int)insertCommand.ExecuteScalar();
}
}
Side-Note: I wouldn't use such a Database-Class since it's prone to errors.
Your SQL query does not return the newly generated Id. To return it, use an OUTPUT clause:
INSERT INTO Equipment (<list of fields>)
OUTPUT inserted.EquipmentTypeID
VALUES (<list of values>)
Some things to be careful about:
<list of fields> represents a comma separated list of columns for which you will supply values
the list of fields should not include the auto-increment ID column (that will be automatically assigned a value)
<list of values> represents a comma separated list of values that will be inserted in the above specified fields. The number of values should be equal to the number of fields and the data types must match
To return the id of just inserted row you need to select it, because ExecuteScalar() returns
the first column of the first row in the result set returned by the query
and INSERT doesn't select/return anything.
insert ...
select ...
See #Tim's answer for more details.
OUTPUT Clause will help you to get the ID of the new added item. For more information please see the link below :
Click here! for more detail

Categories