Updating data with same primary key - c#

I am reading data from csv file and adding data in database. At time of inserting data into database I want to update data with same primary key.
e.g.) I am using two Columns Bar-codes (PK) and Quantity. So, when I insert data from csv file similar barcode quantity will get added.
Can anyone help me? I am using C#.NET and SQL.
Thanks,
Rushabh Shah.

check out the merge keyword. it should do pretty much waht you're asking for.
here's a stored proc that should do it for you.
CREATE PROCEDURE dbo.InsertBarcodeData
#Barcode varchar(255),
#Quantity int
AS
BEGIN
SET NOCOUNT ON;
MERGE myTableName AS target
USING (SELECT #Barcode, #Quantity) AS source (BarCode, Quantity)
ON (target.Barcode= source.Barcode)
WHEN MATCHED THEN
UPDATE SET Quantity = source.Quantity + target.Quantity
WHEN NOT MATCHED THEN
INSERT (BarCode, Quantity)
VALUES (source.BarCode, source.Quantity)
END;
GO

create procedure InsertOrUpdateSales
(
#bar_code nvarchar(100),
#quantity int
)
as
if exists (select * from sales where bar_code = #bar_code)
update sales set quantity = quantity + #quantity where bar_code = #bar_code
else
insert into sales ( bar_code, quantity) values ( #bar_code, #quantity )
go
And
public static void InsertOrUpdateSales(string connection, string barCode, int quantity)
{
using(SqlConnection conn = new SqlConnection(connection))
{
using(SqlCommand comm = new SqlCommand("InsertOrUpdateSales", conn))
{
comm.CommandType = CommandType.StoredProcedure;
comm.Paramters.AddWithValue("#bar_code", barCode);
comm.Paramters.AddWithValue("#quantity", quantity);
comm.ExecuteNonQuery();
}
}
}
Alternatively, if you want to use the merge statement (as #Chris Lively and #nathan gonzalez mentioned) you could get really fancy and do it like this:
BULK INSERT the data from the CSV file to an empty temp table.
MERGE the temp table with the existing table.
TRUNCATE the temp table.
This might give you the best results. (For certain values of "best".)

If you can assume that there is already an existing entry for all of the bar codes in the table you could do this with a Stored procedure with two incominig parameters (#BarCodeID and #AdditionalQuantity)
UPDATE yourTable SET Quantity = Quantity + #AdditionalQuantity WHERE BarCode = #BarCodeID

You can add a Trigger to the table. When ever something is inserted in the table, you can have it run a stored procedure.

Related

Inserting across several tables using joins and junction table

I do apologize, I know this question pops up a lot, but I'm afraid I just don't understand the answers I've read on the subject and was hoping someone could clear things up for me.
I have three tables:
Recipes (ID int primary identity, RecipeName varchar(20), Directions varchar(max), RecIngID int)
Ingredients (ID int primary identity, IngredientName varchar(30), Quantity int)
and a junction table, Recipe_Ingredients (RecipeID int foreign key references Recipe(ID), IngredientID int foreign key references Ingredient(ID)
Now, that being said, I need to create stored procedures for everything (because I'm creating an ntier program using asp.net MVC 4 and C#, blah blah blah....
But I have no clue how to write a stored procedure for inserting a new recipe into that table, and taking into account the relationship between the junction table Recipe_Ingredients and the Ingredient table.
Can someone please, PLEASE help me here? All the explanations I've read haven't really made sense. If there's something I'm doing wrong, please tell me.
for each ingredient call this stored procedure
create procedure insertIngredients
(
#name varchar,
#quantity int
)
as
insert into Ingredients (IngredientName, Quantity)
values (#name, #quantity)
select SCOPE_IDENTITY()
which inserts ingredient and returns it's newly inserted ID.
Store that ids into some variable (ie, List or comma delimited string).
After that, insert recipe calling procedure:
create procedure insertRecipe
(
#name varchar,
#directions varchar(max),
#RecIngID int
)
as
insert into Recipes (RecipeName, Directions, RecIngID)
values (#name, #directions, #RecIngID)
select SCOPE_IDENTITY()
also save ID which that procedure returns after insert.
And, as last step insert data into junction table using recipe ID and all those Ingredient ID's you previously inserted.
create procedure insertRecipeIngredients
(
#recipeID int,
#ingredientID int
)
as
insert into Recipe_Ingredients (RecipeID, IngredientID)
values (#recipeID, #ingredientID)
select SCOPE_IDENTITY()
in your c# code call procedures like this:
public int InsertIngredient(string name, int quantity)
{
SqlConnection conn = new SqlConnection("[your connection string goes here]");
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "insertIngredients";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#name", name);
cmd.Parameters.AddWithValue("#quantity", quantity);
conn.Open();
int newlyInsertedId = (int) cmd.ExecuteScalar();
if (conn.State == System.Data.ConnectionState.Open)
conn.Close();
return newlyInsertedId;
}

stored procedure calling from c# and iteration issue in Merge

I have over a million records in the list. I pass all records at once from table to stored procedure .In stored procedure i have to have iteration to go thorugh all the rows in the table and for each row it takes table row modified date based on jobid and checks if it exist in database and based on it either it updates or insert the record. I feel that my procedure is not correct, would be glad if someone help on this.
foreach (No_kemi no_list in newforSQL)
{
DataTable _dt = new DataTable("table");
_dt.Columns.Add("JobID", typeof(string));
_dt.Columns.Add("CreatedDate", typeof(datetime));
_dt.Columns.Add("ModifiedDate", typeof(datetime));
_dt.Columns.Add("DbDate", typeof(datetime));
_dt.Columns.Add("SubGUID", typeof(string));
_dt.Columns.Add("eType", typeof(string));
// adding over a million records in the table
_dt.Rows.Add(no_list.ID,no_list.CreatedDate,no_list.ModifiedDate,no_list.DbDate,no_list.SubGUID,no_list.eType);
}
using (SqlCommand sqlCommand = new SqlCommand())
{
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandText = "Process_NO_table";
sqlCommand.Connection = connection;
SqlParameter typeParam = sqlCmd.Parameters.AddWithValue("#track", _dt);
typeParam .SqlDbType = SqlDbType.Structured;
sqlCmd.ExecuteNonQuery();
}
my tabletype and procedure:
CREATE TYPE TrackType AS TABLE
(
t_Id uniqueidentifier, t_JobID nvarchar(50), t_CreatedDate datetime2(7), t_ModifiedDate datetime2(7), t_DbDate datetime2(7)
t_SubGUID nvarchar(MAX), t_eType nvarchar(MAX)
);
GO
ALTER/CREATE PROCEDURE [dbo].[Process_NO_table] // i will change to alter after i create it
#track TrackType READONLY
AS
// i need to iterate all the rows of the table(over a million)
Declare #rows INT
Declare #i int = 0
Declare #count int = (SELECT COUNT(*) FROM #track)
DECLARE #is INT
WHILE (#i < #count)
BEGIN
-- first i check modified date from the database table
SELECT #is = COUNT(*) FROM NO_table WHERE [JobID] IN (SELECT [t_JobID] FROM #track)
MERGE [dbo].[NO_table] AS [Target]
USING #track AS [Source]
-- if the database modifed date is less than the modifeid date from the proceduretable(#track) then it updates the records
ON [Target].[ModifiedDate] < [Source].[t_ModifiedDate] AND JobID = t_JobID
WHEN MATCHED THEN
UPDATE SET [JobID] = [Source].[t_JobID],
[CreatedDate] = [Source].[t_CreatedDate]
[DbDate]= [Source].[t_DbDate]
[ModifiedDate] = [Source].[t_ModifiedDate]
[SubGUID] = [Source].[t_SubGUID]
[eType] = [Source].[t_eType]
-- if the database modifed dateis not existing then it insert the record
MERGE [dbo].[NO_table] AS [Target]
USING #track AS [Source]
ON (#is != 0)
WHEN NOT MATCHED THEN
INSERT INTO [NO_table] ( [JobID], [CreatedDate], [ModifiedDate], [DbDate], [SubGUID], [eType] )
VALUES ( [Source].[t_JobID], [Source].[t_CreatedDate], [Source].[t_ModifiedDate], [Source].[t_DbDate], [Source].[t_SubGUID], [Source].[t_eType] );
SET #i = #i + 1
END
GO
I think you have a large number of syntax errors in your SQL (assuming MS SQL), but your merge condition is probably giving you the invalid syntax near WHERE, because you need to use AND, not WHERE.
ON [Target].[ModifiedDate] < [Source].[t_ModifiedDate] WHERE JobID = t_JobID
should be
ON [Target].[ModifiedDate] < [Source].[t_ModifiedDate] AND JobID = t_JobID
The Select Top 1 and the WHEN MATCHED THEN after the null check for #dbmoddate need to go away as well, as those are also causing syntax issues.
The insert after the null check for #dbmoddate needs a table specified so it actually knows what to insert into.
You also need to end your merge statement with a semicolon.
UPDATED ANSWER:
Now that you have this more cleaned up, I can better see what you're trying to do. At a high level, you want to simply update existing records where the modified date is less than the modified date of on your custom type. If there does not exist a record in your table that does exist in your custom type, then insert it.
With that said, you don't actually need to loop because you aren't doing anything with your loop. What you currently have and what I'm posting below this is all set-based results, not iterative.
You can make this much simpler by getting rid of the merge statements and doing a simple Update and Insert like I have below. The merge would make more sense if your condition between the two statements was the same (i.e. if you didn't have the check for modified date, then merge would be OK) because then you can use the keywords WHEN MATCHED and WHEN NOT MATCHED and have it in one single merge statement. I personally stay away from MERGE statements because they tend to be a little buggy and there are a number of things you have to watch out for.
I think this solution will be better in the long run as it is easier to read and more maintainable...
CREATE TYPE TrackType AS TABLE
(
t_Id uniqueidentifier, t_JobID nvarchar(50), t_CreatedDate datetime2(7), t_ModifiedDate datetime2(7), t_DbDate datetime2(7)
,t_SubGUID nvarchar(MAX), t_eType nvarchar(MAX)
);
GO
CREATE PROCEDURE [dbo].[Process_NO_table] -- i will change to alter after i create it
#track TrackType READONLY
AS
-- i need to iterate all the rows of the table(over a million)
Update [NO_table]
SET [JobID] = T.[t_JobID],
[CreatedDate] = T.[t_CreatedDate],
[DbDate]= T.[t_DbDate],
[ModifiedDate] = T.[t_ModifiedDate],
[SubGUID] = T.[t_SubGUID] ,
[eType] = T.[t_eType]
From #track T
Where [NO_table].[JobID] = T.[t_JobID]
And [NO_table].[ModifiedDate] < T.[t_ModifiedDate]
Insert [NO_Table]
(
[JobID],
[CreatedDate],
[ModifiedDate],
[DbDate],
[SubGUID],
[eType]
)
Select T.[t_JobID],
T.[t_CreatedDate],
T.[t_ModifiedDate],
T.[t_DbDate],
T.[t_SubGUID],
T.[t_eType]
From #track T
Where Not Exists (Select 1 From [NO_table] where T.[t_JobID] = [NO_table].[JobID])
GO

Uupdate values in two tables via stored procedure

I have two tables and I need to update values in them via a stored procedure. Tried too much to update but some times it update the first table only, others the second or even fail due to cannot allow duplicates. Also when it updates the WHOLE data in the table becomes the same as the new updated ones. I've now reached to this error after all these lines of codes
Cannot insert the value NULL into column 'Emp_ID',table 'DatePics'; column does not allow nulls. UPDATE fails.The statement has been terminated
Here is the SQL code :
ALTER procedure [dbo].[UpdateEmp]
#EmpName nvarchar(100),
#Nationality nvarchar(30),
#Passport nvarchar(20),
#ContractDate date,
#HealthDate date
AS
BEGIN
set nocount on;
DECLARE #IDs table (ID int )
UPDATE Employee SET
EmpName=#EmpName, Nationality=#Nationality, Visa=#Visa, Passport=#Passport,
ReceivedDate=#ReceivedDate,IDIssue=#IDIssue, IDExpiry=#IDExpiry, Sponsor=#Sponsor
output inserted.ID into #IDs (ID)
WHERE ID = #ID
UPDATE DatePics SET
FingerDate=#FingerDate, ContractDate=#ContractDate, HealthDate=#HealthDate
where Emp_ID in (select ID from #IDs);
END
After writing the stored procedure code, I wrote the C# code like this:
private void updatebtn_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = db.con;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "UpdateEmp";
cmd.Parameters.AddWithValue("#EmpName", NameSeartxt.Text);
cmd.Parameters.AddWithValue("#Nationality", NatSeartxt.Text);
cmd.Parameters.AddWithValue("#Passport", PassSeartxt.Text);
cmd.Parameters.AddWithValue("#ContractDate", ContractSeartxt.Text);
cmd.Parameters.AddWithValue("#HealthDate", HealthSeartxt.Text);
db.con.Open();
int up = cmd.ExecuteNonQuery();
if (up > 0)
{
MessageBox.Show("Update done ", "DONE !");
SearNametxt.Text = "";
}
else
{
MessageBox.Show("Failed to update", "FAIL !");
SearNametxt.Text = "";
}
db.con.Close();
}
Any clue?
I can see three problems with your query. 1 You declare ID, but don't assign it before using it, so it will always be NULL for the first query, so this will never update any rows:
DECLARE #ID int
UPDATE FrstTable SET
EmpName=#EmpName, Nationality=#Nationality, Passport=#Passport
WHERE ID = #ID
Secondly, you are using SCOPE_IDENTITY to attempt to get the ID of the record that has been updated. You can't do that, SCOPE_IDENTITY will return the last inserted ID, it is not affected by updates. You will need to use OUTPUT to get the Updated ID:
DECLARE #IDs TABLE (ID INT);
UPDATE FirstTable
OUTPUT inserted.ID INTO #Ids (ID)
SET EmpName = #EmpName,
Nationality = #Nationality,
Passport = #Passport;
Thirdly, your second update statement has no where clause, so will update the entire table:
UPDATE ScndTable
SET Emp_ID=#ID, ContractDate=#ContractDate, HealthDate=#HealthDate
WHERE EmpID IN (SELECT ID FROM #Ids);
Your stored procedure looks weird to me. I believe there should be a WHERE cluase for the second UPDATE otherwise it will always update the whole ScndTable table. set #ID = SCOPE_IDENTITY(); seems to be reduntant here. Are you trying to perform insert into ScndTable if there's no corresponding Emp_ID there? Finnaly explicitly create a transaction to update either both tables or none.
Hope it helps!
Please assign the value of #ID variable, before executing the first update statement.
I think you are trying to update some row, so you can pass the 'id' value from the CSHARP code. When you use the SCOPE_IDENTITY, you will get the last inserted value. Try to pass the ID value from the front end.

Creating method on how to have Selecting MAX Function in C# with SQL

I have this kind of method on inserting data
private void InsertReceipt()
{
decimal Stub;
Stub = Math.Floor(decimal.Parse(txtAmount.Text) / 2000);
SqlCommand cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO Ticket(CustomerID, Date, Store, Amount, NoStub)" +
"VALUES (#CustomerID, #Date, #Store, #Amount, #NoStub) ";
cmd.Parameters.AddWithValue("#CustomerID", txtCustomerID.Text);
cmd.Parameters.AddWithValue("#Date", dtpDate.Value.Date.ToString());
cmd.Parameters.AddWithValue("#Store", txtStore.Text);
decimal amount = decimal.Parse(txtAmount.Text);
cmd.Parameters.AddWithValue("#Amount", amount);
cmd.Parameters.Add("#NoStub", SqlDbType.Decimal).Value = Stub;
cmd.ExecuteNonQuery();
}
I just want to have a method that if you insert a data in table "Ticket" there's another table will be updating.
CustomerID Date Store Amount NoStub
1 6/7/2013 Nike 4000 2
2 6/7/2013 Adidas 6000 3
This table will be updating, for example I will be using table named "StubRange", This output will be generate.
RangeID CustomerID NoStub TickerStart TickerEnd
1 1 2 00001 00002
2 2 3 00003 00005
I just want to learned how to have this kind of kind of method.
What you are looking for is an After Insert trigger.
Basically you can think of it as an event that triggers after an insert takes place (hence trigger...).
Your trigger should look something like:
CREATE TRIGGER YourTriggerName --The name of your trigger
ON Ticket --The table it will be observing
AFTER INSERT,UPDATE --It will trigger after insert / update
AS
--The actions you want to do. For example:
DECLARE #CustomerId int
SET #CustomerId = (SELECT CustomerId FROM inserted) --you might want to use 'inserted' table
--Inset values
Insert into StubRange (CustomerID , NoStub)
Select Distinct ins.CustomerID, ins.NoStub
From Inserted ins
--Update existing records
UPDATE StubRange
set --Set what ever it is you want to update
WHERE CustomerId = #CustomerId
More about Inserted table - According to Microsoft:
The inserted table stores copies of the affected rows during INSERT
and UPDATE statements. During an insert or update transaction, new
rows are added to both the inserted table and the trigger table. The
rows in the inserted table are copies of the new rows in the trigger
table.
Well you need to write a insert trigger when ever you insert record in Ticket table. You can refer below syntax to create trigger. This is a Oracle syntax
CREATE OR REPLACE TRIGGER TRIGGER_INSERT_STUBRANGE
AFTER INSERT ON TICKET
FOR EACH ROW
DECLARE
raise_exception Exception;
BEGIN
--WRITE YOUR INSERT STATEMENT HERE
Exception
when raise_exception then
RAISE_APPLICATION_ERROR(-20001, sqlerrm );
END;
This will create an insert trigger on your table1 on updating table1 , table2 will b updated with CustomerID, NoStub from table 1 and rest of properties depends on your business logic
CREATE TRIGGER trig_Update_table
ON [tableName1]
FOR INSERT
AS
Begin
Insert into tableName2 (CustomerID , NoStub)
Select Distinct i.CustomerID, i.NoStub
from Inserted i
End

Getting autonumber primary key from MS SQL Server

I am currently working in C#, and I need to insert a new record into one table, get the new primary key value, and then use that as a foreign key reference in inserting several more records. The Database is MS SQL Server 2003. All help is appreciated!
The way to get the identity of the inserted row is with the SCOPE_IDENTITY() function. If you're using stored procedures then this would look something like the following to return the row identity as an output parameter.
CREATE PROCEDURE dbo.MyProcedure
(
#RowId INT = NULL OUTPUT
)
AS
INSERT INTO MyTable
(
Column1
,Column2
,...
)
VALUES
(
#Param1
,#Param2
,...
);
SET #RowId = SCOPE_IDENTITY();
You can then use this value for any subsequent inserts (alternatively, if you can pass the data all into the stored procedure, then you can use it in the remainder of the procedure body).
If you're passing the SQL in dynamically then you use much the same technique, but with a single string with statement delimiters (also ; in SQL), e.g.:
var sql = "INSERT INTO MyTable (Column1, Column2, ...) VALUES (#P1, #P2, ...);" +
"SELECT SCOPE_IDENTITY();";
Then if you execute this using ExecuteScalar you'll be able to get the identity back as the scalar result and cast it to the right type. Alternatively you could build up the whole batch in one go, e.g.
var sql = "DECLARE #RowId INT;" +
"INSERT INTO MyTable (Column1, Column2, ...) VALUES (#P1, #P2, ...);" +
"SET #RowId = SCOPE_IDENTITY();" +
"INSERT INTO MyOtherTable (Column1, ...) VALUES (#P3, #P4, ...);";
This may not be exactly the right syntax, and you may need to use SET NOCOUNT ON; at the start (my mind is rusty as I rarely use dynamic SQL) but it should get you on the right track.
The best way of doing this is the use SCOPE_IDENTITY() function in TSQL. This should be executed as part of the insert i.e.
SqlCommand cmd = new SqlCommand(#"
INSERT INTO T (Name) VALUES(#Name)
SELECT SCOPE_IDENTITY() As TheId", conn);
cmd.AddParameter("#Name", SqlDbType.VarChar, 50).Value = "Test";
int tId = (int)cmd.ExecuteScalar();
Alternatively you can assign SCOPE_IDENTITY() to a variable to be used in successive statements. e.g.
DECLARE #T1 int
INSERT INTO T (Name) VALUES('Test')
SELECT #T1 = SCOPE_IDENTITY()
INSERT INTO T2 (Name, TId) VALUES('Test', #T1)
If you are just using SQL then check Duncan's answer. If however you are using LINQ then you can create the entity, save it to the DB and the ID parameter will be populated automatically.
Given a user entity and a user table it might look like this:
using(var db = new DataContext()) {
var user = new User { Name = "Jhon" };
db.Users.InsertOnSubmit(user);
db.SubmitChanges();
/* At this point the user.ID field will have the primary key from the database */
}

Categories

Resources