SQL Trigger after update on a single column in different table - sql

i'm creating a SQL trigger which I need to insert a record into a different table after updating it on the first table.
I got the following tables.
Queue table:
Bin table
Till now i've the following SQL query which doesn't compile.
CREATE TRIGGER dbo.MoveBinToQueue
ON dbo.Bins
AFTER UPDATE
AS
IF UPDATE(Status_StatusId)
BEGIN
INSERT INTO dbo.Queues
(
Prio,
Time
Bin_BinId,
Goal_GoalId
)
SELECT
20,
GETDATE(),
inserted.Status_StatusId,
inserted.Goal_GoalId
)
END

You need a FROM clause:
CREATE TRIGGER dbo.MoveBinToQueue
ON dbo.Bins
AFTER UPDATE
AS
BEGIN
IF UPDATE(Status_StatusId)
BEGIN
INSERT INTO dbo.Queues (Prio, Time, Bin_BinId, Goal_GoalId )
SELECT 20, GETDATE(), inserted.Status_StatusId, inserted.Goal_GoalId
FROM inserted;
END
END;

Related

How to get a inserted id in other table from inside a trigger?

I have 3 tables tbl_Users, tbl_Protocol and tbl_ProtocolDetails and inside of my trigger on Users, I have to inserted into Protocol and then insert into ProtocolDetails, but I don't know how work the inserted scope.
Something like that:
CREATE TRIGGER tg_Users ON tbl_Users
AFTER INSERT, UPDATE AS
BEGIN
DECLARE #UserId = Int
DECLARE #ProtocolId = Int
DECLARE #UserDetail = NVARCHAR(255)
SELECT
#UserId = user_id,
#UserDetail = user_detail + '#' + user_explanation
FROM INSERTED
INSERT INTO tbl_Protocol (user_id, inserted_date)
VALUES (#UserId, GetDate())
-- Return Inserted Id from tbl_Protocol into #ProtocolDetail then
INSERT INTO tbl_ProtocolDetails (protocol_id, protocol_details)
VALUES (#ProtocolId, #UserDetail)
END
Your trigger has a MAJOR flaw in that you seems to expect to always have just a single row in the Inserted table - that is not the case, since the trigger will be called once per statement (not once for each row), so if you insert 20 rows at once, the trigger is called only once, and the Inserted pseudo table contains 20 rows.
Therefore, code like this:
Select #UserId = user_id,
#UserDetail = user_detail + '#' + user_explanation
From INSERTED;
will fail, since you'll retrieve only one (arbitrary) row from the Inserted table, and you'll ignore all other rows that might be in Inserted.
You need to take that into account when programming your trigger! You have to do this in a proper, set-based fashion - not row-by-agonizing-row stlye!
Try this code:
CREATE TRIGGER tg_Users ON tbl_Users
AFTER INSERT, UPDATE AS
BEGIN
-- declare an internal table variable to hold the inserted "ProtocolId" values
DECLARE #IdTable TABLE (UserId INT, ProtocolId INT);
-- insert into the "tbl_Protocol" table from the "Inserted" pseudo table
-- keep track of the inserted new ID values in the #IdTable
INSERT INTO tbl_Protocol (user_id, inserted_date)
OUTPUT Inserted.user_id, Inserted.ProtocolId INTO #IdTable(UserId, ProtocolId)
SELECT user_id, SYSDATETIME()
FROM Inserted;
-- insert into the "tbl_ProtocolDetails" table from both the #IdTable,
-- as well as the "Inserted" pseudo table, to get all the necessary values
INSERT INTO tbl_ProtocolDetails (protocol_id, protocol_details)
SELECT
t.ProtocolId,
i.user_detail + '#' + i.user_explanation
FROM
#IdTable t
INNER JOIN
Inserted i ON i.user_id = t.UserId
END
There is nothing in this trigger that would handle a multiple insert/update statement. You will need to either use one scenario that will handle multiple records or check how many records were effected with a IF ##ROWCOUNT = 1 else statement. In your example, I would just use something like
insert into tbl_Protocol(user_id, inserted_date)
select user_id, user_detail + '#' + user_explanation
From INSERTED;
As for your detail table, I see Marc corrected his answer to include the multiple lines and has a simple solution or you can create a second trigger on the tbl_Protocol. Another solution I have used in the past is a temp table for processing when I have very complicated triggers.

SQL Server : After Insert Trigger for update another Table

I wrote this trigger to insert data into another table after data insert into the CHECKINOUT table.
But it doesn't insert data into the Att_process table. No errors is showing up in SQL Server. Can you help me to figure this out problem?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER [dbo].[trgAfterInsert] ON [dbo].[CHECKINOUT]
AFTER INSERT
AS
DECLARE #uid INT;
DECLARE #checkin DATETIME;
SELECT #uid = i.USERID
FROM [CHECKINOUT] i;
SELECT #checkin = i.CHECKTIME
FROM [CHECKINOUT] i;
IF(DATEPART(HOUR, #checkin) < 12)
BEGIN
INSERT INTO Att_process (USERID, checkin_time)
VALUES (#uid, #checkin);
END;
ELSE
BEGIN
INSERT INTO Att_process (USERID, checkout_time)
VALUES (#uid, #checkin);
END;
Two main problems:
you're not looking at the Inserted pseudo table which contains the newly inserted rows
you're assuming the trigger is called once per row - this is not the case, the trigger is called once per statement and the Inserted pseudo table will contain multiple rows - and you need to deal with that
So try this code instead:
CREATE TRIGGER [dbo].[trgAfterInsert] ON [dbo].[CHECKINOUT]
AFTER INSERT
AS
-- grab all the rows from the "Inserted" pseudo table
-- and insert into the "checkin_time" column, if the value
-- of the HOUR is less than 12
INSERT INTO Att_process (USERID, checkin_time)
SELECT
i.USERID, i.CHECKTIME
FROM
Inserted i
WHERE
DATEPART(HOUR, i.CHECKTIME) < 12
-- grab all other rows (HOUR is greater than or equal to 12)
-- and insert into the "checkout_time" column
INSERT INTO Att_process (USERID, checkout_time)
SELECT
i.USERID, i.CHECKTIME
FROM
Inserted i
WHERE
DATEPART(HOUR, i.CHECKTIME) >= 12
As many SQL developers did, you had experienced the same error with SQL Server triggers. You have missed the case when multiple insert statements are executed on the table.
Triggers are executed only once for the SQL statement, not for each row.
So the code in the trigger should be able to cover all tasks for all rows affected by the SQL statement.
Here is a sample how you can alter your trigger code.
ALTER TRIGGER [dbo].[trgAfterInsert] ON [dbo].[CHECKINOUT]
AFTER INSERT
AS
INSERT INTO Att_process (USERID, checkin_time, checkout_time)
select
USERID,
case when DATEPART(HOUR, CHECKTIME) < 12 then CHECKTIME else null end,
case when DATEPART(HOUR, CHECKTIME) < 12 then null else CHECKTIME end
FROM inserted
For obtaining stable results, please use the Inserted and Deleted internal tables which are available within trigger codes.
You can further check sample SQL Trigger to log changes for how Deleted and Inserted tables are used.
I hope that helps you,
One last note, if you can use SQL Output clause to insert data into two tables at the same time.
But of course triggers work on table base, so where ever the insert statement is executed triggers work. If you use Output clause, you should guarantee that the only SQL statement which inserts data into that table will be it to maintain consistency between two tables.
Try this, since you are not using 'inserted' magic table to extract last inserted data.
DECLARE #uid INT;
DECLARE #checkin DATETIME;
SELECT #uid = USERID FROM inserted
SELECT #checkin = CHECKTIME FROM inserted

UPDATE and INSERT should fire trigger only once

Is there any way to combine an update and an insert statements in a way that they fires a trigger only once?
I have one particular table that has (and currently needs) a trigger AFTER INSERT, UPDATE, DELETE. Now I want to update one row and insert another row and have the trigger fire only once for that.
Is this at all possible?
I already tried a MERGE-Statement without success: The trigger fires once for the update- and once for the insert-part.
Well, problem solved for me. I did NOT find a way to combine the statements into one fire-event of the trigger. But the trigger behaves in an interesting way, that was good enough for me: Both calls to the trigger do already have access to the fully updated data.
Just execute the following statements and you will see what I mean.
CREATE TABLE Foo (V INT)
GO
CREATE TRIGGER tFoo ON Foo AFTER INSERT, UPDATE, DELETE
AS
SELECT 'inserted' AS Type, * FROM inserted
UNION ALL
SELECT 'deleted', * FROM deleted
UNION ALL
SELECT 'actual', * FROM Foo
GO
DELETE FROM Foo
INSERT Foo VALUES (1)
;MERGE INTO Foo
USING (SELECT 2 AS V) AS Source ON 1 = 0
WHEN NOT MATCHED BY SOURCE THEN DELETE
WHEN NOT MATCHED BY TARGET THEN INSERT (V) VALUES (Source.V);
As a result, the trigger will be called twice for the MERGE. But both times, "SELECT * FROM Foo" delivers the fully updated data already: There will be one row with the value 2. The value 1 is deleted already.
This really surprised me: The insert-trigger is called first and the deleted row is gone from the data before the call to the delete-trigger happens.
Only the values of "inserted" and "deleted" correspond to the delete- or insert-statement.
You could try something like this:
The trigger would check for the existence of #temp table.
If it doesn't exist, it creates it with dummy data. It then checks if the recent values contain the same user (SPID) that is running now and if the last time it was triggered was within 20 seconds.
If these are true then it will PRINT 'Do Nothing' and drop the table, otherwise it will do your trigger statement.
At the end of your trigger statement it inserts into the table the SPID and current datetime.
This temp table should last as long as the SPID connection, if you want it to last longer make it a ##temp or a real table.
IF OBJECT_ID('tempdb..#temp') IS NULL
begin
Create table #temp(SPID int, dt datetime)
insert into #temp values (0, '2000-01-01')
end
If ##SPID = (select top 1 SPID from #temp order by dt desc)
and Convert(datetime,Convert(varchar(19),GETDATE(),121)) between
Convert(datetime,Convert(varchar(19),(Select top 1 dt from #temp order by dt desc),121)) and
Convert(datetime,Convert(varchar(19),DateAdd(second, 20, (select top 1 dt from #temp order by dt desc)),121))
begin
PRINT 'Do Nothing'
Drop table #temp
end
else
begin
--trigger statement
Insert into #temp values (##SPID, GETDATE())
end

Updating a Table after some values are inserted into it in SQL Server 2008

I am trying to write a stored procedure in SQL Server 2008 which updates a table after some values are inserted into the table.
My stored procedure takes the values from a DMV and stores them in a table. In the same procedure after insert query, I have written an update query for the same table.
Insert results are populated fine, but the results of updates are getting lost.
But when I try to do only inserts in the stored procedure and I execute the update query manually everything is fine.
Why it is happening like this?
there should not be a problem in this.
below code working as expected.
create procedure dbo.test
as
begin
create table #temp (
name varchar(100) ,
id int
)
insert #temp
select name ,
id
from master..sysobjects
update #temp
set name='ALL Same'
from #temp
select * from #temp
drop table #temp
end
go
Best approach is to use Trigger, sample of AFTER UPDATE trigger is below:
ALTER TRIGGER [dbo].[tr_MyTriggerName]
ON [dbo].[MyTableName] AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
--if MyColumnName is updated the do..
IF UPDATE (MyColumnName)
BEGIN
UPDATE MyTableName
SET AnotherColumnInMyTable = someValue
FROM MyTableName
INNER JOIN Inserted
ON MyTableName.PrimaryKeyColumn = Inserted.PrimaryKeyColumn
END
END

Table insertion by Trigger

Here's the table:
Order [OrderId], [DateTimePlaced], [ProductId], [Quantity]
Invoice [InvoiceId], [invoice_DateTime], [order_Id]
I want SQL Server automatically inserts the data into the Invoice table while I manually insert the data into Order table.
Here is my code:
create trigger invoice_Sync on Order
after insert
as
begin
insert into Invoice (invoice_DateTime,order_Id) select ID,Name,Address
select getdate(),OrderId
from inserted
go
However, after I executed this query, SQL Server gave me an error "The select list for the INSERT statement contains more items than the insert list"
My situation is: The invoice Id is set to be automatically incremented, So I think I should not manually put the Invoice Id into this trigger.
Any suggestions?
You have an extra SELECT there:
create trigger invoice_Sync on Order
after insert
as
begin
insert into Invoice (invoice_DateTime,order_Id)
--select ID,Name,Address, Why is this here?
select getdate(),OrderId
from inserted
end
go