I have a table("Product_Location") with the following columns:
ProductID (PK), LocationID (PK), Quantity
i would like to update the table in the database from rows in a datatable. if row already exists then Update quantity otherwise Insert new row.
i have the following method which update the quantity in the table, if the combination of productID and LocationID exists, it just update otherwise insert new row for that combination. code:
public bool UpdateLocationQuantity(DataSet productLocationData,
SqlTransaction sqlTransaction)
{
try
{
bool result = true;
SqlCommand command = new SqlCommand();
//get the Transaction table which contains rows to update from dataset
DataTable table = productLocationData.Tables["Inventory_Transactions"];
//Create Command Text
string commandText = #" IF Exists (SELECT * FROM Product_Location PL
WHERE ProductID = #ProductID AND LocationID = #LocationID)
UPDATE Product_Location SET Quantity = Quantity + #Quantity
WHERE ProductID = #ProductID AND LocationID = #LocationID
ELSE
INSERT INTO Product_Location (ProductID,LocationID,Quantity)
VALUES(#ProductID,#LocationID,#quantity)";
command = new SqlCommand(commandText, this.CurrentConnection);
command.CommandType = CommandType.Text;
command.Transaction = sqlTransaction;
SqlParameterCollection paramCols = command.Parameters;
//this loop will do the update or insert for all rows in the table
// How can we optimize to only ONE CALL to database?
foreach (DataRow row in table.Rows)
{
paramCols.Clear();
paramCols.AddWithValue("#ProductID",row["ProductID"]);
paramCols.AddWithValue("#LocationID", row["LocationID"]);
paramCols.AddWithValue("#Quantity", row["Quantity"]);
result &= command.ExecuteNonQuery()>= 0;
}
return result;
}
catch
{
throw;
}
}
**My question is how we can optimize the code so only one call to ExecuteNonQuery to update the database instead of having it in a loop? Please note that we are not using StoredProcedure and all should be from C# and SQL Queries or Transactions.
if it was just Update the rows, we could call command.Update with providing the source table and it easily update all the rows without using rows. but since i am using 'IF Exists' then we are forced to use ExecuteNonQuery which is not accepting source table as parameter.
Thank You
Instead of using a ParameterCollection you could do:
command.Parameters.Add(new SqlParameter("#ProductID", ProductData.PRODUCTID_FIELD));
or
command.Parameters.AddWithValue("#ProductID", ProductData.PRODUCTID_FIELD);
and so on. You don't actually have to specify the type.
Then call:
int numOfRowsAffected = command.ExecuteNonQuery();
There is no dataset to be returned, only the number of rows affected, since this is a non-query.
The problem with making a ParameterCollection like you are doing is you then need to set command.Parameters = paramCols; but command.Parameters is Read-Only, so you can't. That is, its read-only as far as assignment goes. You can only add parameters to it through the methods Add and AddWithValue.
for multiple rows , add command in loop
foreach (DataRow row in table.Rows)
{
SqlCommand command = new SqlCommand();
.
.
.
}
Related
I have a SQL Server database which has a lot of information inside.
I want to select top 50 rows in a single query (which I did, with no problem) but then I want to update a column from false to true, so next time I select I wont select the same, my code looks like this:
string Command = "UPDATE HubCommands SET [Alreadytaken] = 'true' FROM (SELECT TOP 50 [CommandId],[DeviceId],[Commandtext], [HashCommand],[UserId] FROM HubCommands) I WHERE [HubId] = '18353fe9-82fd-4ac2-a078-51c199d9072b'";
using (SqlConnection myConnection = new SqlConnection(SqlConnection))
{
using (SqlDataAdapter myDataAdapter = new SqlDataAdapter(Command, myConnection))
{
DataTable dtResult = new DataTable();
myDataAdapter.Fill(dtResult);
foreach (DataRow row in dtResult.Rows)
{
Guid CommandId, DeviceId, UserId;
Guid.TryParse(row["CommandId"].ToString(), out CommandId);
Guid.TryParse(row["DeviceId"].ToString(), out DeviceId);
Guid.TryParse(row["UserId"].ToString(), out UserId);
Console.WriteLine("CommandId" + CommandId);
}
}
}
This code does work, and it updates what I ask it to update, but I don't get nothing in the data table, its like it is always updating but not selecting.
If I do a normal select it does work and give information.
Does anyone have any idea how to update and get some data back, in a single query?
So your question is:
How can I update a table in SQL Server using C# and return the truly updated
rows as a DataTable ?
First You have multiple issues in your query.
You should use 1 and 0, not true or false. SQL-Server has a bit datatype and not a Boolean.
Second, this is how you should've constructed your query:
DECLARE #IDs TABLE
(
[CommandId] uniqueidentifier
);
INSERT INTO #IDs
SELECT [CommandId] FROM HubCommands
WHERE [HubId] = '18353fe9-82fd-4ac2-a078-51c199d9072b' AND [Alreadytaken] = 0;
UPDATE HubCommands
SET [Alreadytaken] = 1
WHERE CommandId IN
(
SELECT [CommandId] FROM #IDs
);
SELECT * FROM HubCommands
WHERE CommandId IN
(
SELECT [CommandId] FROM #IDs
);
Wrap all the above in a single string and use SqlDataReader. No need for an Adapter in you case (Since we're mixing commands unlike what the adapter usually does):
var sqlCommand = new SqlCommand(Command, myConnection);
SqlDataReader dataReader = sqlCommand.ExecuteReader();
DataTable dtResult = new DataTable();
dtResult.Load(dataReader);
I highly advise you to create a stored procedure accepting HubId as a parameter that does all the above work. It is neater and better for maintenance.
Using C# in Visual Studio, I'm inserting a row into a table like this:
INSERT INTO foo (column_name)
VALUES ('bar')
I want to do something like this, but I don't know the correct syntax:
INSERT INTO foo (column_name)
VALUES ('bar')
RETURNING foo_id
This would return the foo_id column from the newly inserted row.
Furthermore, even if I find the correct syntax for this, I have another problem: I have SqlDataReader and SqlDataAdapter at my disposal. As far as I know, the former is for reading data, the second is for manipulating data. When inserting a row with a return statement, I am both manipulating and reading data, so I'm not sure what to use. Maybe there's something entirely different I should use for this?
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
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();
}
}
try this:
INSERT INTO foo (column_name)
OUTPUT INSERTED.column_name,column_name,...
VALUES ('bar')
OUTPUT can return a result set (among other things), see: OUTPUT Clause (Transact-SQL). Also, if you insert multiple values (INSERT SELECT) this method will return one row per inserted row, where other methods will only return info on the last row.
working example:
declare #YourTable table (YourID int identity(1,1), YourCol1 varchar(5))
INSERT INTO #YourTable (YourCol1)
OUTPUT INSERTED.YourID
VALUES ('Bar')
OUTPUT:
YourID
-----------
1
(1 row(s) affected)
I think you can use ##IDENTITY for this, but I think there's some special rules/restrictions around it?
using (var con = new SqlConnection("connection string"))
{
con.Open();
string query = "INSERT INTO table (column) VALUES (#value)";
var command = new SqlCommand(query, con);
command.Parameters.Add("#value", value);
command.ExecuteNonQuery();
command.Parameters.Clear();
command.CommandText = "SELECT ##IDENTITY";
int identity = Convert.ToInt32(command.ExecuteScalar());
}
Using C# in Visual Studio, I'm inserting a row into a table like this:
INSERT INTO foo (column_name)
VALUES ('bar')
I want to do something like this, but I don't know the correct syntax:
INSERT INTO foo (column_name)
VALUES ('bar')
RETURNING foo_id
This would return the foo_id column from the newly inserted row.
Furthermore, even if I find the correct syntax for this, I have another problem: I have SqlDataReader and SqlDataAdapter at my disposal. As far as I know, the former is for reading data, the second is for manipulating data. When inserting a row with a return statement, I am both manipulating and reading data, so I'm not sure what to use. Maybe there's something entirely different I should use for this?
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
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();
}
}
try this:
INSERT INTO foo (column_name)
OUTPUT INSERTED.column_name,column_name,...
VALUES ('bar')
OUTPUT can return a result set (among other things), see: OUTPUT Clause (Transact-SQL). Also, if you insert multiple values (INSERT SELECT) this method will return one row per inserted row, where other methods will only return info on the last row.
working example:
declare #YourTable table (YourID int identity(1,1), YourCol1 varchar(5))
INSERT INTO #YourTable (YourCol1)
OUTPUT INSERTED.YourID
VALUES ('Bar')
OUTPUT:
YourID
-----------
1
(1 row(s) affected)
I think you can use ##IDENTITY for this, but I think there's some special rules/restrictions around it?
using (var con = new SqlConnection("connection string"))
{
con.Open();
string query = "INSERT INTO table (column) VALUES (#value)";
var command = new SqlCommand(query, con);
command.Parameters.Add("#value", value);
command.ExecuteNonQuery();
command.Parameters.Clear();
command.CommandText = "SELECT ##IDENTITY";
int identity = Convert.ToInt32(command.ExecuteScalar());
}
public void RemoveTask(int index) {
SQL = "DELETE FROM Task where (...) = " +index;
dbConn.Open();
dbCommand = new SqlCeCommand(SQL, dbConn);
dbCommand.ExecuteNonQuery();
dbConn.Close();
}
What i want to do is to delete the record based on the index which specified the row number but I don't know what function or variable should be used ( note the blank ), i try something like rowNum but it does not work.
any help will be appreaciated
It isn't entirely clear what you are trying to do. I think the following code is what you are after - it deletes a row based on the primary key where in this case the name of the primary key column is TaskId (but you can change that based on your table column names).
Note that it also uses parameterised SQL which gives better performance and security.
SQL = "DELETE FROM Task where TaskId = #taskid";
dbConn.Open();
dbCommand = new SqlCeCommand(SQL, dbConn);
dbCommand.Parameters.Add("#taskid", SqlDbType.Int);
dbCommand.Parameters["#taskid"].Value = index;
dbCommand.ExecuteNonQuery();
dbConn.Close();
I came across a problem while updating a typed DataTable that has a primary key column.
At some point in my code I fill DataTables (some of them have primary and foreign key columns) and then I insert the data of all DataTables in one transaction using DataAdapters and Update(). Because the typed DataTables do not allow the PrimaryKey table to be empty I insert some integer values in there. After calling Update() I expected the PK columns to be updated with the database PKs.
public void UpdateMethod(DbTransaction transaction)
{
DbDataAdapter dataAdapter = mDbProviderFactory.CreateDataAdapter();
using (DbCommand insertCommand = CreateCommand())
{
insertCommand.Connection = mDbConnection;
insertCommand.Transaction = transaction;
dataAdapter.InsertCommand = insertCommand;
dataAdapter.Update(dataTable);
}
// not sure if i need to do this:
dataTable.AcceptChanges();
// I would expect that databaseId is now the Id used in the database,
// but it is the original Id which I set while creating the row entry
databaseId = (int)dataTable.Rows[0]["Id"];
}
private DbCommand CreateCommand()
{
// Make command object.
DbCommand cmd = mDbProviderFactory.CreateCommand();
// add command input parameters
DbParameter parameter1 = mDbProviderFactory.CreateParameter();
parameter1.ParameterName = mDatabaseParameterPrefix + "someColumn";
parameter1.SourceColumn = "someColumn";
parameter1.Size = 255;
parameter1.DbType = DbType.String;
// Output parameter
DbParameter idParameter = mDbProviderFactory.CreateParameter();
idParameter.ParameterName = mDatabaseParameterPrefix + "ID";
idParameter.SourceColumn = "ID";
idParameter.Direction = ParameterDirection.InputOutput;
idParameter.DbType = DbType.Int32;
// setup sql command
cmd.Parameters.Add(parameter1);
cmd.Parameters.Add(idParameter)
cmd.CommandText = #"INSERT INTO [SomeTable] ([someColumn], ...) VALUES(#someColumn, ... ) SELECT CAST(SCOPE_IDENTITY() AS int) AS 'ID'";
cmd.UpdatedRowSource = UpdateRowSource.Both;
return cmd;
}
Thanks for any hints!
When you have a primary key set when you fill the datatable, it won't overwrite rows that already exist (however, if it comes across a matching primary key it may or may not update non-key elements depending on what your loadoption is set to)
You would have to clear your datatable first before you fill it I believe.
Solved it. The problem was the SQL statement: Instead of
INSERT INTO [SomeTable] ([someColumn], ...) VALUES(#someColumn, ... ) SELECT CAST(SCOPE_IDENTITY() AS int) AS 'ID'
It must be:
INSERT INTO [SomeTable] ([someColumn], ...) VALUES(#someColumn, ... ) SELECT #ID = SCOPE_IDENTITY()
Otherwise the value of the primary key is not assigned to the output parameter.