Executing a stored procedure as a result of merge statement - sql

I am using a MERGE statement in a stored procedure that looks like this:
MERGE Sections AS sTarget
USING #Sections AS sSource
ON sTarget.SectionID = sSource.SectionID
WHEN NOT MATCHED THEN
<INSERT STATEMENT>
WHEN MATCHED THEN
UPDATE SET
IsActive = CASE WHEN sSource.IsDeleted = 1 THEN 0 ELSE 1 END;
^
A
Where #Sections is a parameter passed to the stored procedure.
Now I want to execute a stored procedure called spDeleteSection that accepts SectionID as a parameter and (soft) deletes all the divisions and subdivisions under the section and returns 0. I want to execute this procedure at 'A'.
Can this be done? If so how? If it cannot be done where am I supposed to execute this to get the same result?
Note :
#Sections is a user defined table
SectionID is obtained form sSource.SectionID

You can capture each merged row using an OUTPUT clause into a table variable.
Then simply join that table in a DELETE.
Do not be tempted to execute something row-by-row over this table variable, it's likely to be slow. Do a single joined delete.
DECLARE #output TABLE (SectionID int PRIMARY KEY, action nvarchar(10))
MERGE Sections AS sTarget
USING #Sections AS sSource
ON sTarget.SectionID = sSource.SectionID
WHEN NOT MATCHED THEN
<INSERT STATEMENT>
WHEN MATCHED THEN
UPDATE SET
IsActive = CASE WHEN sSource.IsDeleted = 1 THEN 0 ELSE 1 END
OUTPUT sTarget.SectionID, $action
INTO #output (SectionID, action);
;
DELETE d
FROM Division d
JOIN #output o ON o.SectionID = d.SectionID
WHERE o.action = 'UPDATE';
If necessary, you can dump the correct rows from this table variable into a Table Valued Parameter and execute a separate procedure with that.

Related

Microsoft SQL Server - default value provided by stored procedure

Is it possible to have a non-null column where the value is generated at insert by calling a stored procedure the parameters of which are values passed to insert into the row?
For example, I have table User:
| username | name | surname | id |
Insert looks like this:
INSERT INTO USER (username, name, surname)
VALUES ('myusername', 'myname', 'mysurname');
The id column is populated with an (integer) value retrieved by calling stored procedure mystoredproc with parameters myusername, myname, mysurname.
A further question is, would this stored procedure be called on each row, or can it be called in a grouped fashion. For example, I'd like my stored procedure to take the name and append a random integer to it so that that if I insert 100 users with the name 'David', they will get the same id and the stored procedure will be called only once. A bit of a bad example on the second point.
Good day,
Is it possible to have a non-null column where the value is generated at insert by calling a stored procedure
Option 1: please check if this work for you
Specify Default Value for the Column and use "NOT NULL"
create trigger on the table AFTER INSERT
Inside the trigger, you can use the virtual table "inserted" in order to get the inserted values.
Using these values (using the inserted table) you can update the column using the logic you need for all the rows at once
** there is no need to use external SP probably, but you can execute SP from trigger if needed
** All executed by a trigger is in the same transaction as the original query.
would this stored procedure be called on each row
NO! The trigger will be executed once for all rows you insert in the same statement. The inserted table includes all the rows which were inserted. In your update section (step 4) you can update all the rows which were inserted in once and no need to execute something for each row
** If you do use external SP which is executed from the trigger then you can pass it all the inserted table as one using Table-Valued Parameter
------------------- update ---------------
Here is a full example of using this logic:
drop table if exists T;
CREATE TABLE T (id int identity(2,2), c int NOT NULL default 1)
GO
CREATE TRIGGER tr ON T AFTER INSERT
AS BEGIN
SET NOCOUNT ON;
UPDATE T SET T.c = T2.C + 1
FROM inserted T2
INNER JOIN T T1 ON T1.id = T2.id
END
INSERT T(c) values (1) -- I insert the value 1 but the trigger will change it to 1+1=2
select * from T
GO
-- test multiple rows:
INSERT T(c) values (10),(20),(30),(40)
select * from T
GO
DECLARE #rc INT = 0,
#UserID INT = ABS(CHECKSUM(NEWID())) % 1000000 + 1;
WHILE #rc = 0
BEGIN
IF NOT EXISTS (SELECT 1 FROM dbo.Users WHERE UserId= #UserId)
BEGIN
INSERT dbo.Users(UserId) WHERE Username = #UserName SELECT #UserId;
SET #rc = 1;
END
ELSE
BEGIN
SELECT #UserId = ABS(CHECKSUM(NEWID())) % 1000000 + 1,
#rc = 0;
END
END

Update or otherwise create an entry in table

I want to update or create a new entry in a table with normal SQL (not MySQL).
My table has columns: T_ID, ITERATION, COMMENT
I know that I can update my table with:
UPDATE TABLE
SET ITERATION = ITERATION + 1
WHERE T_ID = 1;
But if no entry with T_ID = 1 exists I want to create an entry with ITERATION = 1 and T_ID = 1 and as COMMENT nothing.
Is this possible by using a normal SQL statement?
Is this possible by using a normal SQL statement?
No - there is no "standard" SQL construct for an "upsert" (or Merge) in one statement. Different SQL systems have implemented mechanism to perform an "update if exists" operation, such as MySQL's ON DUPLICATE KEY UPDATE or Oracle and SQL Server's MERGE syntax, but nothing in "standard" SQL.
You would want to do it in a stored procedure. Something like this where you're checking to see if anything exists where T_ID = 1. Then based off that conclusion you'll either update or insert.
BEGIN TRAN
IF EXISTS (SELECT * FROM table WHERE T_ID = 1)
BEGIN
UPDATE table SET ITERATION = ITERATION + 1 WHERE T_ID = 1
END
ELSE
BEGIN
INSERT INTO table (T_ID, ITERATION, COMMENT)
VALUES (1, 1, '')
END
COMMIT TRAN

Need to wait on specific column state change in SQL

I need to check for the status change of a certain column in the table
I can do it using while loop, where i can get the column value and check the value and break from the loop if value is changed.
i am using SQL server 2008.
is there a better way?
Here is the sample sql query
declare #status int = 1
select #status = status from MyTable with (nolock) where Id = 100034
while #status <> 3
begin
WAITFOR DELAY '00:01'
select #status = status from MyTable with (nolock) where Id = 100034
end
Have you considered to use a trigger instead of an stored procedure? This is exactly, what are triggers for.
CREATE TRIGGER reactOnStatus3
ON MyTable
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
IF Status = 3
EXEC DoTheMagicStoredProcedure;
END;

Getting the value of a primary key from a merge

I have a stored proc that has a merge .. into statement. I am trying to get the value of the primary key out of the stored proc.
SET #NewCompanyID = #CompanyID
....
merge company as c
using ( select #CompanyID) as compAlt (company_id )
on compAlt.company_id = c.company_id
when matched
then
update set ....
when not matched
then
insert ... ;
SET #NewCompanyID = ##identity;
If the merge statement runs the update, i want to set #NewCompanyID to whatever value was passed into the stored proc (#CompanyID). If the insert statement was executed, then I want to pass the ##identity. How do I do this?
I added before merge into
DECLARE #SummaryOfChanges TABLE(Change VARCHAR(20));
then
OUTPUT $action INTO #SummaryOfChanges;
SELECT #Change = Change
FROM #SummaryOfChanges; /* there is always one update or insert */
IF #Change = 'INSERT'
BEGIN
....
END
This worked fine.

How to set a value with the return value of a stored procedure

I have a stored procedure SP1 that receives a parameter #param1 and does a bunch of things with it and returns a value.
Now I have a table consisting of two columns, say C1 and C2. Initially, C1 has different values on each row while C2 is 0 on every row. Now I want to update C2 with the value returned by SP1 with its corresponding C1 value as #param1. I was hoping something like:
update Table1 set C2 = (exec SP1 #param1=C1)
just like receiving a return value from a function in most programming languages.
I haven't learned any SQL, but I did a lot of research not finding anything. So any help would be appreciated.
Create an OUTPUT parameter inside your stored procedure and use that Parameter to store the value and then use that parameter inside your Update statement. Something like this....
DECLARE #OutParam Datatype;
EXECUTE SP1 #param1=C1, #OUT_Param = #OutParam OUTPUT --<--
--Now you can use this OUTPUT parameter in your Update statement.
UPDATE Table1
SET C2 = #OutParam
UPDATE
After reading your comments I think this is what you are trying to do pass value of C1 Column from Table Table1 to Stored Procedure and then Update the Relevant C2 Column of Table1 with the returned value of stored procedure.
For this best way to do is to Create a Table Type Parameter and pass the values of C1 as a table. See here for a detailed answer about how to pass a table to a stored procedure.
I havent tested it But in this situation I guess you could do something like this.. I dont recomend this method if you have a large table. in that case you are better off with a table type parameter Procedure.
-- Get C1 Values In a Temp Table
SELECT DISTINCT C1 INTO #temp
FROM Table1
-- Declare Two Varibles
--1) Return Type of Stored Procedure
--2) Datatype of C1
DECLARE #C1_Var DataType;
DECLARE #param1 DataType;
WHILE EXISTS(SELECT * FROM #temp)
BEGIN
-- Select Top 1 C1 to #C1_Var
SELECT TOP 1 #C1_Var = C1 FROM #temp
--Execute Proc and returned Value in #param1
EXECUTE SP1 #param1 = #C1_Var
-- Update the table
UPDATE Table1
SET C2 = #param1
WHERE C1 = #C1_Var
-- Delete from Temp Table to entually exit the loop
DELETE FROM #temp WHERE C1 = #Var
END
You can't use stored procedures like that.
What you will have to do is moving your stored procedure code into an UDF function:
CREATE FUNCTION FN1 (#funcParam1 <param type>)
RETURNS <return type>
AS
BEGIN
DECLARE #return <return type>;
-- Do whatever you have to do here
RETURN #return;
END
From then on you could do this:
UPDATE Table1 SET C2 = FN1(C1)
Please be aware though that this kind of practice can be bad on performance if the function is non-deterministic. Read about deterministic and non-deterministic functions for more information.