Get trigger inserted row id's in stored procedure - sql-server

I have a stored procedure which updates multiple rows in a column as mentioned below:
update tableA
set isactive = 0
where email = ''
TableA has update trigger to insert into tableALog if isactive is changed.
Now in my stored procedure after update statement is executed I need to get tableALog primary key values.
I have used following ways bu no luck.
##identity - failed because it returns last updated value. But I need list of primary key values inserted in log table
OUTPUT inserted. - cannot use this because it always works on current scope in stored procedure. But I need table values which are inserted from trigger.
Please let me know if you have any suggestions.

This started as a comment but it's getting too long, so...
Sharing data between stored procedures and the triggers they activate is tricky,
Since triggers can't take parameters not can they return values.
From my experience, The best way to achieve such a thing involve adding a column to the table the trigger is set on to identify the records the trigger is working on, and adding a table (could be a temporary table) for the trigger to output the data and the stored procedure to read from.

Related

SQL Server trigger to auto-populate entire column

If I have a table called AccuralMonth with two columns AccuralID and AccuralMonth.
and another table called Employee with a column called AccuralMonth too.
I want the function that when a number is entered into the AccuralMonth Column of the AccuralMonth table, it auto-populates all entries in the Employee table.
I have tried using a trigger but it did not work. I got an error when trying to insert value as it would of set a primary key value to null.
Create trigger MergeAccurual on [dbo].[AccuralMonth]
after insert,DELETE,UPDATE
as
begin
MERGE [dbo].[Employee] as E
USING [dbo].[AccuralMonth] AS am
ON E.[accrualmonth] = am.[AccuralMonth]
WHEN MATCHED THEN
UPDATE SET E.[accrualmonth] = am.[AccuralMonth]
WHEN NOT MATCHED BY TARGET THEN
INSERT([accrualmonth]) VALUES(am.[AccuralMonth]);
end
Any Ideas? Or even a better way to implement a way to set all values in AccuralMonth in Employees to a certain value as I do realize this is poor design.
I'm using these tables for a asp.net MVC web app

IF EXISTS and update/delete in stored procedure

I'm currently writing a stored procedure to update an entire table or on attribute level.
I look if its supposed to update entire table or not. If yes then update entire, if not then look on attribute level whether it's supposed to be a new row, update row, or delete row.
To my help I have three tables: one that says if whole table is to be updated, a new/update table, and a delete table. They consist of data telling what table has changed and when the change occured as well a GUID.
Any ideas on how to write this? Preferred would also be dynamic code but that is not a must. For instance something like:
IF EXISTS (SELECT * from DB1.Schema1.Table1 where UpdateAll = 1) --update all rows, all attributes
BEGIN
'truncate/drop table? update whole table from another table from DB2?'
END
IF EXISTS (SELECT * from DB1.Schema1.Table1 where UpdateAll = 0) --Only on attribute level
BEGIN
'Update table in DB1 based on Delete, or new/update table. And this step
is on attribute level (not update all attributes as step above)'
END
First try to explore the MERGE statement in SQL server: here
If it doesn't full fill your requirement then go with dynamic query execution using sp_executesql: here

Creating a history table without using triggers

I have a TABLE A with 3000 records with 25 columns. I want to have a history table called Table A history holding all the changes updates and deletes for me to look up any day. I usually use cursors. Now thought using triggers which I was not asked to. Do you have any other suggestions? Many thanks!
If your using tsql /SQL server and you can't use triggers, which is the only sure way to get every change, maybe use a stored procedure that is scheduled in job to run every x amount of time, the stored procedure using a MERGE statement with the two tables to get new records or changes. I would not suggest this if you need every single change without question.
CREATE TABLE dbo.TableA (id INT, Column1 nvarchar(30))
CREATE TABLE dbo.TableA_History (id INT, Column1 nvarchar(30), TimeStamp DateTime)
(this code isn't production, just the general idea)
Put the following code inside a stored procedure and use a Sql Server Job with a schedule on it.
MERGE INTO dbo.TableA_History
USING dbo.TableA
ON TableA_History.id = TableA.id AND TableA_History.Column1 = TableA.Column1
WHEN NOT MATCHED BY TARGET THEN
INSERT (id,Column1,TimeStamp) VALUES (TableA.id,TableA.Column1,GETDATE())
So basically if the record either doesn't exist or doesn't match meaning a column changed, insert the record into the history table.
It is possible to create history without triggers in some case, even if you are not using SQL Server 2016 and system-versioned table are not available.
In some cases, when you can identify for sure which routines are modifying your table, you can create history using OUTPUT INTO clause.
For example,
INSERT INTO [dbo].[MainTable]
OUTPUT inserted.[]
,...
,'I'
,GETUTCDATE()
,#CurrentUserID
INTO [dbo].[HistoryTable]
SELECT *
FROM ... ;
In routines, when you are using MERGE I like that we can use $action:
Is available only for the MERGE statement. Specifies a column of type
nvarchar(10) in the OUTPUT clause in a MERGE statement that returns
one of three values for each row: 'INSERT', 'UPDATE', or 'DELETE',
according to the action that was performed on that row.
It's very handy that we can add the user which is modifying the table. Using triggers you need to use session context or session variable to pass the user. In versioning table you need to add additional column to the main table in order to log the user as it only logs the current table columns (at least for now).
So, basically it depends on your data and application. If you have many sources of CRUD over the table, the trigger is the most secure way. If your table is very big and heavily used, using MERGE is not good as it my cause blocking and harm performance.
In our databases we are using all of the methods depending on the situation:
triggers for legacy
system-versioning for new development
direct OUTPUT in the history, when sure that data is modified only by given set of routines

How to get a table identity inserted by instead of insert trigger?

I have a problem described as follows: I have a table with one instead of insert trigger:
create table TMessage (ID int identity(1,1), dscp varchar(50))
GO
Alter trigger tr_tmessage on tmessage
instead of insert
as
--Set NoCount On
insert into tmessage
select dscp from inserted
GO
Alter proc P1
As
--Set NoCount On
insert into tmessage
(dscp)
values('some data')
Select SCOPE_IDENTITY()
GO
When I execute P1 it returns Null for SCOPE_IDENTITY() instead of the identity of the table. I even tried Output clause in the insert statement in the proc. but again the output table Identity field that gets filled from inserted in the Output clause is 0 in this case.
Any help would be appreciated.
Well, you've got yourself quite a pickle there.
From the one hand, you need the instead of insert trigger on your table, but from the other hand, you want to get the identity that this trigger generates back to the stored procedure that activated it.
Since there is no way to send parameters to and from triggers, you will have to do one of 3 things:
Find some way to eliminate the need for that instead of trigger.
This is my best recommendation.
Break your stored procedure to 2 parts: One part will do everything until the insert into statement (including it), thus activating the instead of insert trigger, and the other part that will do all operations needed after the trigger. this way you can use the scope_identity() inside the instead of insert trigger and send it's return value to the second stored procedure as a parameter.
Note: this design means you have to insert records one by one. should you try to insert more then one record, scope_identity() will only return the identity of the last row inserted by the trigger.
Find some way of passing data between the stored procedure and the instead of trigger. Since triggers can't except or return parameters, you will have to use either a temporary table or a regular table. This suggested solution is only suggested as a last resort, since it will complicate your code and probably cause some performance issues as well. Also, you will have to find a way to hold execution of the stored procedure until the instead of trigger will finish it's work. I can give you some pointers on how to share data between the procedure and the trigger, but I really suggest not to choose this solution.
SCOPE_IDENTITY() return the value from current scope and that is stored procedure outside from stored procedure it will be null.use ##identity to get last inserted identity.

Need to update second table with insert or update to primary table

I have two tables : a,b and I have written down stored procedure for insert and deletion of record from table b.
I am trying to use a trigger on table a with the following code:
CREATE TRIGGER trgAfterUpdate
ON [dbo].[Inventory]
FOR UPDATE
AS
EXEC dbo.usp_open_update_generateorder
GO
But this doesn't seem to work. There is nothing wrong with the sp as when executed separately it is working fine.
Instead of using FOR UPDATE try AFTER INSERT, UPDATE

Resources