Triggers execution after group update with "in (...)" statement in query - sql

I have AFTER UPDATE trigger for in table:
ALTER TRIGGER [dbo].[table1]
ON [dbo].[table]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #primaryKey bigint
SELECT #PrimaryKey = PK FROM Inserted
if EXISTS(select * from [dbo].[table1] where PK=#PrimaryKey)
begin
update [dbo].[table1] set [Action] = 'U' where PK=#PrimaryKey
end
else
begin
insert into [dbo].[table1] ([PK], [Action], StampIn)
values (#PrimaryKey, 'U', GETDATE())
end
END
When I do "update SOME_DB.dbo.TABLE set FIELD='NEW VALUE' where PK in (3,4,5)", I found that only one row was added to the table1 with PK "3". Which means that trigger was executed only once in table.
But I need to have all the rows in table1 with PKs that were updated.
Could you please help me to solve my problem?
Thank you.

SQL triggers use the inserted view to identify all the rows being inserted. Your logic is only looking at one of the rows; hence it does not do what you expect. So:
BEGIN
SET NOCOUNT ON;
update t1
set [Action] = 'U'
from table1 t1 join
inserted i
on i.primarykey = t1.pk ;
insert into [dbo].[table1] ([PK], [Action], StampIn)
select i.primarykey, 'U', getdate()
from inserted i
where not exists (select 1 from dbo.table1 t1 where t1.pk = i.primarykey);
END;
You don't actually need the conditional logic, because the join and where clauses take care of that.

Related

How to prevent insert when condition is met

I have a two tables
CREATE TABLE table1 (
ID int not null,
Status int not null,
TimeStamp datetime2(3)
CONSTRAINT pkId PRIMARY KEY (ID)
);
and
CREATE TABLE Status (
StatusID int not null,
StatusName varchar(64) not null
CONSTRAINT pStatus PRIMARY KEY (StatusID)
);
with inserted values
INSERT INTO table1 (ID,StatusID,TimeStamp) VALUES
(1,1,'2021-06-15 07:30:31'),
(2,2,'2021-07-15 07:30:31'),
(3,3,'2021-08-15 07:30:31'),
(4,4,'2021-09-15 08:30:31'),
(5,5,'2021-09-15 07:30:31'),
(6,5,'2021-09-15 07:30:31'),
(7,4,'2021-09-15 07:30:31'),
(8,2,'2021-09-15 07:30:31'),
(9,1,'2021-09-15 07:30:31');
and
INSERT INTO dbo.Status (StatusID,StatusName) VALUES
(1,'wants to enroll'),
(2,'granted enrollment'),
(3,'declined enrollment'),
(4,'attending the course'),
(5,'finished the course');
I wrote a trigger but it prevent all insert when the StatusID < 4 and what I want is to prevent insert into TimeStamp only when the condition is StatusID < 4. I use SQL Server.
CREATE TRIGGER dbo.Prevent_Insert4
ON dbo.table1
FOR INSERT
AS
SET NOCOUNT ON
IF EXISTS(SELECT *
FROM dbo.table1 t
JOIN inserted i ON t.ID=i.ID
WHERE t.StatusID<4)
BEGIN
RAISERROR('You can not update when StatusId value < 4', 16, 1)
ROLLBACK TRAN
SET NOCOUNT OFF
RETURN
END
SET NOCOUNT OFF
Thank you in advance.
Sounds like you just need a CHECK constraint
ALTER TABLE table1
ADD CONSTRAINT CHK_Status
CHECK(Status >= 4 OR TimeStamp IS NULL);
If you really, really wanted to use triggers for this (not recommended) then it should look like this
CREATE OR ALTER TRIGGER dbo.Prevent_Insert4
ON dbo.table1
FOR INSERT, UPDATE
AS
SET NOCOUNT ON;
IF EXISTS(SELECT 1
FROM inserted i
WHERE i.StatusID < 4
AND i.TimeStamp IS NOT NULL)
BEGIN
THROW 50001, 'You can not set TimeStamp when StatusId value < 4', 1;
-- no need for rollback, will happen automatically if you use THROW
END;
go
With your code you are doing rollback of inserted row when condition is met. If I understood you correctly you just to want override timestamp with null value if StatusId < 4.
If so, you should write that instead of rollback
IF EXISTS(SELECT *
FROM dbo.table1 t
JOIN inserted i ON t.ID=i.ID
WHERE t.StatusID<4)
BEGIN
UPDATE t SET t.TimeStamp = NULL
FROM inserted i
JOIN dbo.table1 t ON i.ID = t.ID
WHERE t.StatusID<4
END

Not showing all records in After Insert trigger in SQL Server

I have a table table1 with some data:
create table table1
(
c1 varchar(20),
c2 varchar(20)
)
insert into table1 values('1','A')
insert into table1 values('2','B')
insert into table1 values('3','C')
insert into table1 values('4','D')
insert into table1 values('5','E')
insert into table1 values('6','F')
Now I created another table with the same structure called table2 :
create table table2
(
c1 varchar(20),
c2 varchar(20)
)
Then I created an After Insert trigger on table2 :
CREATE TRIGGER trgAfterInsert
ON [dbo].[table2]
FOR INSERT
AS
declare #c1 varchar(20);
declare #c2 varchar(20);
declare #audit_action varchar(100);
select #c1 = i.c1 from inserted i;
select #c2 = i.c2 from inserted i;
set #audit_action = 'Inserted Record -- After Insert Trigger.';
insert into table2_Audit(c1, c2, Audit_Action, Audit_Timestamp)
values(#c1, #c2, #audit_action, getdate());
PRINT 'AFTER INSERT trigger fired.'
GO
There is a problem that when i copy all data of table1 to tabe2 then in Audit table only one record show.It not show all inserted record.
I use this query for copy the record in table2:-
insert into table2(c1,c2) select c1,c2 from table1
Your trigger fires once for each insert statement issued against this table. not one for each row inserted in the table. Hence if more than one row is inserted you trigger definition should be able to handle more than one row.
Instead of using variables to capture values from inserted table and then insert them in table two, simply select from the inserted table and insert the data into Table2_audit.
A trigger to handle this would look something like........
CREATE TRIGGER trgAfterInsert ON [dbo].[table2]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
insert into table2_Audit (c1,c2,Audit_Action,Audit_Timestamp)
SELECT C1
, C2
, 'Inserted Record -- After Insert Trigger.'
, GETDATE()
FROM inserted ;
PRINT 'AFTER INSERT trigger fired.'
END
GO
Trigger is fired once per entire operation, change your code to:
CREATE TRIGGER trgAfterInsert ON [dbo].[table2]
FOR INSERT
AS
BEGIN
DECLARE #audit_action VARCHAR(100) = 'Inserted Record -- After Insert Trigger.';
INSERT INTO table2_Audit(c1,c2,Audit_Action,Audit_Timestamp)
SELECT i.c1, i.c2, #audit_action, GETDATE()
FROM inserted i;
END
Second don't use PRINT inside trigger;
More info
The behavior you are seeing is by design. DML triggers in SQL Server
are statement level triggers - they are fired once after
INSERT/UPDATE/DELETE/MERGE irrespective of how many rows are affected
by the DML statement. So you should write your logic in the trigger to
handle multiple rows in the inserted/deleted tables

Insert trigger not inserting new rows in other table

Can anyone point out why this insert trigger is not inserting the new rows into the IVRTestB table?
If Object_ID('MyTrig3', 'TR') IS Not Null
Drop Trigger MyTrig3;
GO
Alter Trigger MyTrig3
On dbo.IVRTest
After Insert, update
AS
Begin
SET NOCOUNT ON;
Insert Into [dbo].[IVRTestB]
(IVRAID, IVRName, DayNumber, OpenFlag)
Select
'i.IVRAID', 'i.IVRName', 'i.DayNumber', 'i.OpenFlag'
From inserted i
INNER JOIN dbo.IVRTestB b
On i.IVRAID = b.IVRAID
END
By putting every column of Inserted into single quotes, you're effectively inserting string literals into your destination table - not the column values!
Use this code instead:
INSERT INTO [dbo].[IVRTestB] (IVRAID, IVRName, DayNumber, OpenFlag)
SELECT
i.IVRAID, i.IVRName, i.DayNumber, i.OpenFlag -- *NO* single quotes here!!!!
FROM
inserted i
-- change this WHERE clause to insert those rows that AREN'T alredy in IVRTestB !
WHERE
i.IVRAID NOT IN (SELECT DISTINCT IVRAID FROM dbo.IVRTestB)
Alter Trigger MyTrig3
On dbo.IVRTest
After Insert
AS
Begin
SET NOCOUNT ON;
IF EXISTS ( SELECT
1
FROM
INSERTED
WHERE
INSERTED.DayNumber IS NULL )
Insert Into
[dbo].[IVRTestB]
(IVRAID,
IVRName,
DayNumber,
OpenFlag)
Select
i.IVRAID,
i.IVRName,
i.DayNumber,
i.OpenFlag
From inserted i
WHERE
INSERTED.DayNumber IS NULL
END
Mohan,
You change nearly worked, I just had to change the not nulls to is not null and the alias for inserted to I
Alter Trigger MyTrig3
On dbo.IVRTest
After Insert
AS
Begin
SET NOCOUNT ON;
IF EXISTS ( SELECT
1
FROM
INSERTED
WHERE
INSERTED.DayNumber IS NOT NULL )
Insert Into
[dbo].[IVRTestB]
(IVRAID,
IVRName,
DayNumber,
OpenFlag)
Select
i.IVRAID,
i.IVRName,
i.DayNumber,
i.OpenFlag
From inserted i
WHERE
i.DayNumber IS NOT NULL
END

sql server - trigger to copy rows from 2 tables

I have a trigger that is called when there's an Update, Insert or Delete on a table that is joined to another table by a PK/FK 1 to 1 relationship.
Currently I'm copying rowX from TableA when a U, I or D occurs. I want it to also copy rowX from TableB at the same time.
How to I do this?
USE [Database]
GO
/****** Object: Trigger [dbo].[archiveTable] Script Date: 14/01/2014 3:48:08 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[archiveTable] ON [dbo].[TableA]
AFTER INSERT,UPDATE,DELETE
AS
DECLARE #HistoryType char(1) --"I"=insert, "U"=update, "D"=delete
DECLARE #Id INT
SET #HistoryType=NULL
SET #Id=NULL
IF EXISTS (SELECT * FROM INSERTED)
BEGIN
IF EXISTS (SELECT * FROM DELETED)
BEGIN
--UPDATE
SET #HistoryType='U'
END
ELSE
BEGIN
--INSERT
SET #HistoryType='I'
END
--handle insert or update data
INSERT INTO [database2].[dbo].[tableA]
(column1, column2, ...)
SET #Id=INSERTED.column5
SELECT
GETDATE(), #HistoryType,
column1, column2, ...
FROM INSERTED
JOIN tableB ON INSERTED.column5 = table5.column1
INSERT INTO [database2].[dbo].[tableB]
(column1, column2, column3, ...)
SELECT
GETDATE(), #HistoryType,
column1, column2, column3, ....
FROM jobdtl WHERE column1 = INSERTED.column5
END
ELSE IF EXISTS(SELECT * FROM DELETED)
BEGIN
--DELETE
SET #HistoryType='D'
same as above except for delete
END
I'm guessing I need an inner join somewhere or do I need a variable to get the #job_id so it knows to copy the relevant info from the second table?
edit - from the looks of it I need to somehow use SCOPE_IDENTITY() however it's not taking the job_id of the transaction it's taking the actual ID of the transaction (ie. 1, 2, 3, etc. whereas I need it to be dynamic as job_id may be 54, 634, 325, etc.)
To run multiple statements in a trigger, enclose the statements in a BEGIN END block.
You may define multiple triggers for a particular event.
To refer to inserted/updated data, use "inserted" pseudo table. For deleted rows, use "deleted" pseudo table.
It's not clear from your question whether you want to insert into TableA/TableB or if these are the tables for which the triggers are defined. (The SELECT statement lacks a from clause)
I think, you can use the UNION clause to insert two rows from different table at one time.
I have made a demo query you just need to change it to your query.
create table #a
(
id int,
name varchar(10)
)
insert into #a
select 1,'ax'
union
select 1,'bx'
select * from #a
drop table #a
This is just the sample query with the logic, and i hope you understand it.

My Trigger not work

I have trigger after insert in Table1 will insert in Table2 , after update columnA in Table1 will update in Table2 Status_Column where Table1.id=table2.id, after delete row in table1 will delete row in table2 where table1.id=table2.id.
create TRIGGER mytrigger
ON Table1
after UPDATE,INSERT,DELETE
AS
Begin
if update (columnA)
BEGIN
SET NOCOUNT ON;
declare #ID int
select #ID = ID from DELETED
UPDATE Table2
SET T2.Status_Column ='Updated'
From Table2 T2
inner join Table1 T1 on T1.ID = T2.ID
where T1.ID = #ID
End
else if exists (select * from inserted)
Begin
declare #table2_ID int
select #table2_ID = ID from inserted
insert into table2 (ID,Status_Column) values (#table2_ID,'New')
End
else if exists (select ID from deleted)
Begin
delete from table2
from table2 T2 , Deleted d
where t2.id= d.id
End
End
Only the delete statement working :(
Your fundamental flaw is that you seem to expect the trigger to be fired once per row - this is NOT the case in SQL Server. Instead, the trigger fires once per statement, and the pseudo table Deleted might contain multiple rows.
Given that that table might contain multiple rows - which one do you expect will be selected here??
select #ID = ID from DELETED
It's undefined - you might get the values from arbitrary rows in Deleted.
You need to rewrite your entire trigger with the knowledge the Deleted WILL contain multiple rows! You need to work with set-based operations - don't expect just a single row in Deleted !