Update or insert data depending on whether row exists - sql

I have a collection of rows that I get from a web service. Some of these rows are to be inserted, some are updates to existing rows. There is no way of telling unless I do a query for the ID in the table. If I find it, then update. If I don't, then insert.
Select #ID from tbl1 where ID = #ID
IF ##ROWCOUNT = 0
BEGIN
Insert into tbl1
values(1, 'AAAA', 'BBBB', 'CCCC', 'DDD')
END
ELSE
BEGIN
UPDATE tbl1
SET
A = #AAA,
B = #BBB,
C = #CCC,
D = #DDD
WHERE ID = #ID
END
I am trying to figure out the most effient way to update/insert these rows into the table without passing them into a stored procedure one at a time.
UPDATE 1
I should have mentioned I am using SQL Server 2005. Also if I have 300 records I don't want to make 300 stored procedure calls.

the most efficient way will be first try to update the table if it returns 0 row updated then only do insertion. for ex.
UPDATE tbl1
SET
A = #AAA,
B = #BBB,
C = #CCC,
D = #DDD
WHERE ID = #ID
IF ##ROWCOUNT = 0
BEGIN
Insert into tbl1
values(1, 'AAAA', 'BBBB', 'CCCC', 'DDD')
END
ELSE
BEGIN
END

Instead of paying for a seek first and then updating using another seek, just go ahead and try to update. If the update doesn't find any rows, you've still only paid for one seek, and didn't have to raise an exception, but you know that you can insert.
UPDATE dbo.tbl1 SET
A = #AAA,
B = #BBB,
C = #CCC,
D = #DDD
WHERE ID = #ID;
IF ##ROWCOUNT = 0
BEGIN
INSERT dbo.tbl1(ID,A,B,C,D)
VALUES(#ID,#AAA,#BBB,#CCC,#DDD);
END
You can also look at MERGE but I shy away from this because (a) the syntax is daunting and (b) there have been many bugs and several of them are still unresolved.
And of course instead of doing this one #ID at a time, you should use a table-valued parameter.
CREATE TYPE dbo.tbl1_type AS TABLE
(
ID INT UNIQUE,
A <datatype>,
B <datatype>,
C <datatype>,
D <datatype>
);
Now your stored procedure can look like this:
CREATE PROCEDURE dbo.tbl1_Update
#List AS dbo.tbl1_type READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t
SET A = i.A, B = i.B, C = i.C, D = i.D
FROM dbo.tbl1 AS t
INNER JOIN #List AS i
ON t.ID = i.ID;
INSERT dbo.tbl1
SELECT ID, A, B, C, D
FROM #List AS i
WHERE NOT EXISTS
(
SELECT 1
FROM dbo.tbl1 WHERE ID = i.ID
);
END
GO
Now you can just pass your DataTable or other collection from C# directly into the procedure as a single parameter.

From the collection of rows you get from the server find out which ones are already there:
select #id from tbl1 where id in (....)
Then you have a list of ids that are in the table and one that there are not in the table.
You will have then 2 batch operations: one for update, the other for insert.

what i understand is this :
at the front end u issue a single sql statement
ArrayofIDsforInsert = select ID from tbl1 where ID not in ( array of ids at the front end)
ArrayofIDsforUpdate = (IntialArrayofids at frontend) - (ArrayofIdsforInsert)
one insert into table and one update table...
now call the insert into table with ArrayofIds for insert
call the update table with ArrayofIds for update..

Related

Automation Anywhere SQL results

I am trying to capture if my SQL Query have 0 rows or multiple rows. If it has 0 rows then I will insert, if 1 will perform an update, if > 1 will perform additional analysis.
Is there a way I can see if my query resulted in x results or no results in automation anywhere?
Any assistance will be appreciated.
You can make use of if exists and if not exists and check if rows exists or not, or even if there are multiple before doing the insert.
Here is a simple example using if not exists where if the row doesn't exist on dbo.Table it will insert a row. If it already exists then the ID will be logged to an Error table.
declare #InsertID int = 5, #Name nvarchar(max) = 'some name'
if ((select count(1) from dbo.Table where ID = #InsertID) > 1) -- detect error; more than one record for an id
begin
insert into dbo.Error (ErrorID, ErrorDate)
select #InsertID, getdate()
end
else if not exists (select 1 from dbo.Table where ID = #InsertID) -- no record exists for ID, insert it
begin
insert into dbo.Table (ID, Name)
select #InsertID, #Name
else if exists (select 1 from dbo.Table where ID = #InsertID) -- update the single record
begin
update dbo.Table set Name = #Name where ID = #InsertID
end
A2019 returns the results of a SQL Query as a table...
You could have an if statement right after your query which checks to see if the row count of the returned table is > 0 then take action accordingly.

Alternative to Iteration for INSERT SELECT UPDATE in a sequence

I have a table with around 17k unique rows for which I need to run these set of statements in sequence
INSERT INTO TABLE1 using MASTERTABLE data (MASTERTABLE have 6 column)
SELECT value of column ID (Primary Key) of newly inserted row from TABLE1
Update that ID value in TABLE2 using a Stored Procedure
I have tried:
while loop: took around 3 hours to complete the execution
cursor: cancelled the query after executing it overnight
In my understanding I can not use JOIN as I need to execute the statements in a sequence
The questions is not detailed enough. The general idea I would like to use something like this
-- create a output table to hold new id, and key columns to join later
DECLARE #OutputTbl TABLE (ID INT, key_Columns in MASTERTABLE)
INSERT INTO TABLE1
OUTPUT INSERTED.ID, MASTERTABLE.key_columns INTO #OutputTbl
SELECT *
FROM MASTERTABLE
UPDATE T2
SET ID = o.ID
FROM TABLE2 t2
INNER JOIN OutputTbl o
ON t2.key_column = o.key_column
Maybe you can consider a TRIGGER on TABLE1 from which to call the stored procedure on TABLE2, and then you can call your INSERT as you wish/need.. one by one or in blocks..
DROP TRIGGER TR_UPD_TABLE2
GO
CREATE TRIGGER TR_UPD_TABLE2 ON TABLE1 AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
DECLARE #columnID INT = NULL
IF (SELECT COUNT(*) FROM INSERTED)=1 BEGIN
-- SINGLE INSERT
SET #columnID = (SELECT columnID FROM INSERTED)
EXEC TableTwoUpdateProcedure #columnID
END ELSE BEGIN
-- MASSIVE INSERT (IF NEEDED)
SET #columnID = 0
WHILE #columnID IS NOT NULL BEGIN
SET #columnID = (SELECT MIN(columnID) FROM INSERTED WHERE columnID > #columnID)
IF #columnID IS NOT NULL BEGIN
EXEC TableTwoUpdateProcedure #columnID
END
END
END
END

Update a column with the result-dataset-count of a stored procedure

I have a table #data which has the columns Id and Count. In addition I have a stored procedure MyProc which accepts a parameter #id (equals the Id column) and returns a dataset (the count equals the Count column).
My goal is to assign the Count column from Id with MyProc without a cursor.
I know, something like this does not work:
UPDATE d
SET Count = (SELECT COUNT(*) FROM (EXEC MyProc d.Id))
FROM #data AS d
Is there a syntax I do not know or is a cursor the only option to achieve this?
PS: It is a code quality and performance problem for me. Calling the stored procedure would be the easiest way without repeating 50 lines of SQL but a cursor slows it down.
I believe you can make use of the below query :
IF OBJECT_ID('dbo.data') IS NOT NULL DROP TABLE data;
IF OBJECT_ID('dbo.MyFunct') IS NOT NULL DROP FUNCTION dbo.MyFunct;
GO
CREATE TABLE data
(
ID int,
[Count] int
);
INSERT data VALUES (1,5), (1,10), (2,3), (4,6);
GO
UPDATE d
SET d.[Count] = f.CNT
FROM
(SELECT ID,COUNT(id) AS CNT FROM data GROUP BY ID) f
INNER JOIN data d ON f.ID = d.ID
I couldn't find a way to use Stored procedure. Needed you can use Table valued function:
CREATE FUNCTION dbo.MyFunct(#id INT)
RETURNS #i TABLE
(ID INT , CNT INT)
AS
BEGIN
INSERT INTO #i
SELECT ID,COUNT(id) AS CNT FROM data GROUP BY ID
RETURN
END;
GO
UPDATE d
SET d.[Count] = f.CNT
FROM dbo.MyFunct(1) f INNER JOIN data d ON f.ID = d.ID
To do what you say, you need a function, not a procedure.
CREATE FUNCTION dbo.myFunc (#Id INT)
RETURNS INT
AS
BEGIN
UPDATE someTable
SET someCol = 'someValue'
WHERE id = #Id;
RETURN ##ROWCOUNT;
END
GO
Then call the function in your update statement;
UPDATE d
SET d.Count = dbo.myFunc(d.Id)
FROM #data AS d;
However, row-based operations is bad practice. You should always strive to perform set-based operations, but as I don't know what your procedure does, I cannot provide more than a wild guess to what you should do (not using a procedure at all):
DECLARE #data TABLE (Id INT);
UPDATE x
SET x.someCol = 'SomeVal'
OUTPUT INSERTED.id INTO #data
FROM someTable AS x
INNER JOIN #data AS d
ON d.Id = x.Id;
WITH cte (Id, myCount) AS (
SELECT d.Id
,COUNT(d.Id) AS myCount
FROM #data AS d
GROUP BY d.Id
)
UPDATE d
SET d.[Count] = c.myCount
FROM #data AS d
INNER JOIN cte AS c
ON c.Id = d.Id;
I don't fully understand what you're trying to do but I think your solution will involve ##ROWCOUNT; Observe:
-- Sample data and proc...
----------------------------------------------------------------------
IF OBJECT_ID('tempdb..#data') IS NOT NULL DROP TABLE #data;
IF OBJECT_ID('dbo.MyProc') IS NOT NULL DROP PROC dbo.MyProc;
GO
CREATE TABLE #data
(
id int,
[Count] int
);
INSERT #data VALUES (1,5), (1,10), (2,3), (4,6);
GO
CREATE PROC dbo.MyProc(#id int)
AS
BEGIN
SELECT 'some value'
FROM #data
WHERE #id = id;
END;
GO
Data BEFORE:
id Count
----------- -----------
1 5
1 10
2 3
4 6
A routine that uses ##ROWCOUNT
DECLARE #someid int = 1; -- the value you're passing to your proc
EXEC dbo.MyProc 1;
DECLARE #rows int = ##ROWCOUNT; -- this is what you need.
UPDATE #data
SET [Count] = #rows
WHERE id = #someid;
Data AFTER
id Count
----------- -----------
1 2
1 2
2 3
4 6

Update Trigger For Multiple Rows

I am trying to Insert data in a table named "Candidate_Post_Info_Table_ChangeLogs" whenever a record is updated in another table named "Candidate_Personal_Info_Table". my code works fine whenever a single record is updated but when i try to updated multiple rows it gives error:
"Sub query returned more then 1 value".
Following is my code :
ALTER TRIGGER [dbo].[Candidate_PostInfo_UPDATE]
ON [dbo].[Candidate_Post_Info_Table]
AFTER UPDATE
AS
BEGIN
IF ##ROWCOUNT = 0
RETURN
DECLARE #Candidate_Post_ID int
DECLARE #Candidate_ID varchar(50)
DECLARE #Action VARCHAR(50)
DECLARE #OldValue VARCHAR(MAX)
DECLARE #NewValue VARCHAR(MAX)
DECLARE #Admin_id int
IF UPDATE(Verified)
BEGIN
SET #Action = 'Changed Verification Status'
SET #Candidate_Post_ID = (Select ID From inserted)
SET #Candidate_ID = (Select Identity_Number from inserted)
SET #NewValue = (Select Verified From inserted)
SET #OldValue = (Select Verified From deleted)
IF(#NewValue != #OldValue)
BEGIN
INSERT INTO Candidate_Post_Info_Table_ChangeLogs(Candidate_Post_ID, Candidate_ID, Change_DateTime, action, NewValue, OldValue, Admin_ID)
VALUES(#Candidate_Post_ID, #Candidate_ID, GETDATE(), #Action, #NewValue, #OldValue, '1')
END
END
END
i have searched stack overflow for this issue but couldn't get any related answer specific to this scenario.
When you insert/update multiple rows into a table, the Inserted temporary table used by the system holds all of the values from all of the rows that were inserted or updated.
Therefore, if you do an update to 6 rows, the Inserted table will also have 6 rows, and doing something like this:
SET #Candidate_Post_ID = (Select ID From inserted)
Will return an error, just the same as doing this:
SET #Candidate_Post_ID = (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6)
From the looks of things, you tried to do this with an iterative approach. Set-based is better. Maybe consider doing it like this in the body of your TRIGGER (without all of the parameters...):
IF UPDATE(Verified)
BEGIN
INSERT INTO Candidate_Post_Info_Table_ChangeLogs
(
Candidate_Post_ID
,Candidate_ID
,Change_DateTime
,action
,NewValue
,OldValue
,Admin_ID
)
SELECT
I.ID
,I.Identity_Number
,GETDATE()
,'Changed Verification Status'
,I.Verified
,O.Verified
,'1'
FROM Inserted I
INNER JOIN Deleted O
ON I.ID = O.ID -- Check this condition to make sure it's a unique join per row
WHERE I.Verified <> O.Verified
END
A similar case was solved in the following thread using cursors.... please check it
SQL Server A trigger to work on multiple row inserts
Also the below thread gives the solution based on set based approach
SQL Server - Rewrite trigger to avoid cursor based approach
*Both the above threads are from stack overflow...

T SQL Looping on insert or update

I have two tables.
Table A and Table B. The columns are same.
create table TableA (
id int
, name varchar
, last datetime
)
create table TableB (
id int
, name varchar
, last datetime
)
I m populating table A with mass data. and i would like to either insert or update the data in table A into table B.
I d like to take the data from table A and either insert into table B if id and name doenst match or update if the id and name does match.
I tried some ETL tool but the result was very slow. I have indexing on id and name, I wanted to try this with SQL.
I have the following but not working correct:
SELECT #id = ID,
#name = name,
#LSDATE = LastSeen_DateTime
FROM DBO.A
IF EXISTS (SELECT ID, name FROM DBO.A
WHERE #ID = ID AND #name = Name)
begin
-- update
end
else
begin
--insert
end
i guess i need to put this in a loop and not quite sure how I can make this run.
Thanks.
Its probably faster to do it two statements one update and one insert rather than a loop
This statement updates all B rows using the data from A where the ID is the same but the name is different
Update
Update
tableB
SET
name = a.Name
From
tableB a
INNER JOIN tableA a
on b.ID = a.ID
and A.Name <> b.Name
This statement inserts all B rows into A where the id doesn't exist in A
INSERT
INSERT INTO
tableB
( ID,
Name
)
SELECT
a.ID
a.Name
FROM
tableA b
WHERE
not exists (Select A.ID From tableB a WHERE a.ID = b.ID)
Updated (reversed it from A into B rather than B into A)
If you were using SQL Server 2008 (or Oracle or DB2), then you could use a merge statement.
MERGE B
USING A AS source
ON (B.ID = source.ID and B.Name = source.Name)
WHEN MATCHED THEN
UPDATE SET Last = source.Last
WHEN NOT MATCHED BY TARGET THEN
INSERT (ID, Name, Last) VALUES (source.ID, source.Name, source.Last)
-- the following is optional, if you remove it, add a semicolon to the end of the above line.
OUTPUT $action,
inserted.ID AS SourceID, inserted.Name AS SourceName,
inserted.Last AS SourceLast,
deleted.ID AS TargetID, deleted.Name AS TargetName,
deleted.Last AS TargetLast ;
The bit with the "output $action" will display what rows are getting updated and what rows are getting updated.
weasel words: I recognize this isn't exactly what you were looking for, but since others may search this topic, it may be helpful for others in the future.
DECLARE #id int
DECLARE #name nvarchar
DECLARE #last datetime
DECLARE TableA_Cursor CURSOR FOR
select id
, name
, last
from TableA;
OPEN TableA_Cursor;
FETCH NEXT from TableA_Cursor
INTO #id, #name, #last;
WHILE ##FETCH_STATUS = 0
BEGIN
IF (EXISTS select 1 from TableB b where b.Id = #id)
update TableB
set Name = #name
, Last = #last
ELSE
insert into TableB (Id, Name, Last)
values (#id, #name, #last)
FETCH NEXT from TableA_Cursor
INTO #id, #name, #last
END
CLOSE TableA_Cursor;
DEALLOCATE TableA_Cursor;
There may be some syntax error, particularly around the IF condition, but you may get the point.