Getting the value of a primary key from a merge - sql

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.

Related

Executing a stored procedure as a result of merge statement

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.

Output Always 1

I have a stored procedure and am using a Merge Statement to Insert and Update. This aspect is working as I require.
However, the output when inserting the record is always 1 and I cannot see why? I would be grateful if someone could review this procedure and let me know what I could be doing wrong,.
CREATE PROCEDURE [dbo].[FileAdd]
#FileId int,
#FileData varbinary(max),
#ContentType Varchar(100),
#OperatorId int
AS
BEGIN
--In Memory Table to
DECLARE #MergeOutput TABLE
(
Id INT
);
--Merge needs a table to Merge with so using a CTE
WITH CTE AS (
SELECT #FileId as FileId)
--Merge
MERGE INTO [dbo].[Files] as T
USING CTE AS S
ON T.FileId = S.FileId
WHEN NOT MATCHED THEN
INSERT (
FileData,
ContentType,
OperatorIdCreated,
OperatorIdUpdated
)
VALUES(
#FileData,
#ContentType,
#OperatorId,
#OperatorId
)
WHEN MATCHED THEN
UPDATE SET
FileData = #FileData,
ContentType= #ContentType,
OperatorIdUpdated = #OperatorId,
Updated = GetDate()
OUTPUT
INSERTED.FileId
INTO #MergeOutput;
SELECT * FROM #MergeOutput;
END
GO
The reason you are getting 1 is because that is what is being UPDATED or INSERTED. When it's the UPDATED value, then it is the value are passing into #FileID.
With the OUTPUT clause:
INSERTED Is a column prefix that specifies the value added by the
insert or update operation.
Thus, what ever value is UPDATED (which is #FileID) or INSERTED (which will be whatever your FileID table logic is) this will be returned in your code. If you are always getting 1, then you must me always updating the column for FileID = 1.
Changing your bottom to inserted.* would show you this, as it would OUTPUT the updated row.
Check the demo here.

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

SQL Server : get the row Identity

I have this as the first part of a stored procedure:
DECLARE #_id as int
if exists(select 1 from JTrack_Visitors WHERE cookie = #cookie)
begin
UPDATE JTrack_Visitors
SET LastSeen = #_now
WHERE cookie = #cookie
end
else
begin
INSERT INTO JTrack_Visitors(Cookie, CreatedOn, LastSeen)
VALUES (#cookie, #_now, #_now)
end
How can I set #_id to be the identity of the row either being inserted or updated? I need that value to use later in the stored procedure.
Thanks.
You can make use of OUTPUT clause in your both statements, in either case populate a table variable and later retrieve value from that table variable.
DECLARE #_id as int;
DECLARE #ID_Table TABLE(ID INT);
IF EXISTS (select 1 from JTrack_Visitors WHERE cookie = #cookie)
BEGIN
UPDATE JTrack_Visitors
SET LastSeen = #_now
OUTPUT inserted.ID INTO #ID_Table(ID)
WHERE cookie = #cookie
END
ELSE
BEGIN
INSERT INTO JTrack_Visitors(Cookie,CreatedOn,LastSeen)
OUTPUT inserted.ID INTO #ID_Table(ID)
VALUES (#cookie,#_now,#_now)
END
SELECT #_id = ID FROM #ID_Table;
Depending on the SQL Server version you're using you could use the MERGE statement including the OUTPUT clause to get the values you're looking for.
This would also eliminate the need to check for existence and either update or insert.

How use inserted\deleted table in stored procedure?

I creating triggers for several tables. The triggers have same logic. I will want to use a common stored procedure.
But I don't know how work with inserted and deleted table.
example:
SET #FiledId = (SELECT FiledId FROM inserted)
begin tran
update table with (serializable) set DateVersion = GETDATE()
where FiledId = #FiledId
if ##rowcount = 0
begin
insert table (FiledId) values (#FiledId)
end
commit tran
You can use a table valued parameter to store the inserted / deleted values from triggers, and pass it across to the proc. e.g., if all you need in your proc is the UNIQUE FileID's:
CREATE TYPE FileIds AS TABLE
(
FileId INT
);
-- Create the proc to use the type as a TVP
CREATE PROC commonProc(#FileIds AS FileIds READONLY)
AS
BEGIN
UPDATE at
SET at.DateVersion = CURRENT_TIMESTAMP
FROM ATable at
JOIN #FileIds fi
ON at.FileID = fi.FileID;
END
And then pass the inserted / deleted ids from the trigger, e.g.:
CREATE TRIGGER MyTrigger ON SomeTable FOR INSERT
AS
BEGIN
DECLARE #FileIds FileIDs;
INSERT INTO #FileIds(FileID)
SELECT DISTINCT FileID FROM INSERTED;
EXEC commonProc #FileIds;
END;
You can
select * into #Inserted from inserted
select * into #Deleted from deleted
and then
use these two temp tables in your stored proc
The tables inserted and deleted are only available inside the trigger. You can only use them in run-time. They will then contain the affected rows.
Also, your code might not work as expected if there is not exactly one row inserted.