Create two or more triggers in a row by executing a query with SQL Server - sql

Which is the right syntax in order to create two or more triggers in a row by executing a query with SQL Server?
I tried the following one:
GO
CREATE TRIGGER TRG_MyTable_UpdatedAt
ON dbo.MyTable
AFTER UPDATE
AS
UPDATE dbo.MyTable
SET UpdatedAt = GETDATE()
WHERE ID IN (SELECT DISTINCT ID FROM Inserted)
END
GO
CREATE TRIGGER TRG_MyTable_CreatedAt
ON dbo.MyTable
AFTER INSERT
AS
UPDATE dbo.MyTable
SET CreatedAt = GETDATE()
WHERE ID IN (SELECT DISTINCT ID FROM Inserted)
END
But it returns an error: Incorrect syntax near 'END'.

CREATE TRIGGER TRG_MyTable_UpdatedAt
ON dbo.MyTable
AFTER UPDATE
AS
BEGIN
UPDATE dbo.MyTable
SET UpdatedAt = GETDATE()
WHERE ID IN (SELECT DISTINCT ID FROM Inserted)
END
GO
CREATE TRIGGER TRG_MyTable_CreatedAt
ON dbo.MyTable
AFTER INSERT
AS
BEGIN
UPDATE dbo.MyTable
SET CreatedAt = GETDATE()
WHERE ID IN (SELECT DISTINCT ID FROM Inserted)
END
GO

You're missing the matching BEGIN, hence the error being near END. For example;
CREATE TRIGGER TRG_MyTable_UpdatedAt
ON dbo.MyTable
AFTER UPDATE
AS
BEGIN
UPDATE dbo.MyTable
SET UpdatedAt = GETDATE()
WHERE ID IN (SELECT DISTINCT ID FROM Inserted)
END

You forgot to write 'Begin'
CREATE TRIGGER TRG_MyTable_UpdatedAt
ON dbo.MyTable
AFTER UPDATE
AS
Begin
UPDATE dbo.MyTable
SET UpdatedAt = GETDATE()
WHERE ID IN (SELECT DISTINCT ID FROM Inserted)
END
GO
CREATE TRIGGER TRG_MyTable_CreatedAt
ON dbo.MyTable
AFTER INSERT
AS
Begin
UPDATE dbo.MyTable
SET CreatedAt = GETDATE()
WHERE ID IN (SELECT DISTINCT ID FROM Inserted)
END
GO

Related

Mass update on table. Error 'Msg 512 - Subquery returned more than 1 value. ...'

I am trying to execute a mass update on table based on a condition.
UPDATE POHeader
SET POStatus = 'Cancelled'
WHERE POStatus = 'Draft'
AND Created <= DATEADD(DD, DATEDIFF(DD, '', GETDATE())-29, '')
I even tried these which also return the same error:
UPDATE POHeader
SET POStatus = 'Cancelled'
WHERE POID in (18364, 26401, 27284, 28575, 30532, 30599, 33650,
33780, 33783, 33785, 33895, 35644, 35647, 35678,
37400, 37546, 38750, 38758, 40568, 42045, ......)
Please help as the amount to update is reaching thousands and doing it 1 by 1 is not an option.
UPDATE Trigger
ALTER TRIGGER [dbo].[POHeader_UPDATE]
ON [dbo].[POHeader]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[POHeaderLog]
([ActionDate]
,[Action]
,[POID]
,[CustomerName]
,[CustomerCode]
, ...)
VALUES
(GETDATE()
,'Update'
,(select INSERTED.POID from INSERTED)
,(select INSERTED.CustomerName from INSERTED)
,(select INSERTED.CustomerCode from INSERTED)
, ...)
END
The problem is with the trigger code. SQL Server's trigger operate a statement level, so there will be multiple rows in inserted. if the update statement that fired the trigger affected more than one row - hence the error that you are getting.
You can rewrite the trigger code using the insert ... select syntax:
ALTER TRIGGER [dbo].[POHeader_UPDATE]
ON [dbo].[POHeader]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[POHeaderLog]
([ActionDate]
,[Action]
,[POID]
,[CustomerName]
,[CustomerCode]
, ...)
SELECT
GETDATE()
,'Update'
,POID
,CustomerName
CustomerCode,
, ...
FROM inserted;
END

SQL Server after update trigger

I have a problem with this trigger. I would like it to update the requested information
only to the row in question (the one I just updated) and not the entire table.
CREATE TRIGGER [dbo].[after_update]
ON [dbo].[MYTABLE]
AFTER UPDATE
AS
BEGIN
UPDATE MYTABLE
SET mytable.CHANGED_ON = GETDATE(),
CHANGED_BY=USER_NAME(USER_ID())
How do I tell the trigger that this applies only to the row in question?
Here is my example after a test
CREATE TRIGGER [dbo].UpdateTasadoresName
ON [dbo].Tasadores
FOR UPDATE
AS
UPDATE Tasadores
SET NombreCompleto = RTRIM( Tasadores.Nombre + ' ' + isnull(Tasadores.ApellidoPaterno,'') + ' ' + isnull(Tasadores.ApellidoMaterno,'') )
FROM Tasadores
INNER JOIN INSERTED i ON Tasadores.id = i.id
The inserted special table will have the information from the updated record.
Try this (update, not after update)
CREATE TRIGGER [dbo].[xxx_update] ON [dbo].[MYTABLE]
FOR UPDATE
AS
BEGIN
UPDATE MYTABLE
SET mytable.CHANGED_ON = GETDATE()
,CHANGED_BY = USER_NAME(USER_ID())
FROM inserted
WHERE MYTABLE.ID = inserted.ID
END
you can call INSERTED, SQL Server uses these tables to capture the data of the modified row before and after the event occurs.I assume in your table the name of the key is Id
I think the following code can help you
CREATE TRIGGER [dbo].[after_update]
ON [dbo].[MYTABLE]
AFTER UPDATE
AS
BEGIN
UPDATE dbo.[MYTABLE]
SET dbo.[MYTABLE].CHANGED_ON = GETDATE(),
dbo.[MYTABLE].CHANGED_BY = USER_NAME(USER_ID())
FROM INSERTED
WHERE INSERTED.Id = dbo.[MYTABLE].[Id]
END
You should be able to access the INSERTED table and retrieve ID or table's primary key. Something similar to this example ...
CREATE TRIGGER [dbo].[after_update] ON [dbo].[MYTABLE]
AFTER UPDATE AS
BEGIN
DECLARE #id AS INT
SELECT #id = [IdColumnName]
FROM INSERTED
UPDATE MYTABLE
SET mytable.CHANGED_ON = GETDATE(),
CHANGED_BY=USER_NAME(USER_ID())
WHERE [IdColumnName] = #id
Here's a link on MSDN on the INSERTED and DELETED tables available when using triggers: http://msdn.microsoft.com/en-au/library/ms191300.aspx
It is very simple to do that,
First create a copy of your table that your want keep the log for
For example you have Table dbo.SalesOrder with columns SalesOrderId, FirstName,LastName, LastModified
Your Version archieve table should be dbo.SalesOrderVersionArchieve with columns SalesOrderVersionArhieveId, SalesOrderId, FirstName,LastName, LastModified
Here is the how you will set up a trigger on SalesOrder table
USE [YOURDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Karan Dhanu
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE TRIGGER dbo.[CreateVersionArchiveRow]
ON dbo.[SalesOrder]
AFTER Update
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.SalesOrderVersionArchive
SELECT *
FROM deleted;
END
Now if you make any changes in saleOrder table it will show you the change in VersionArchieve table
try this solution.
DECLARE #Id INT
DECLARE #field VARCHAR(50)
SELECT #Id= INSERTED.CustomerId
FROM INSERTED
IF UPDATE(Name)
BEGIN
SET #field = 'Updated Name'
END
IF UPDATE(Country)
BEGIN
SET #field = 'Updated Country'
END
INSERT INTO CustomerLogs
VALUES(#Id, #field)
// OR
-- If you wish to update existing table records.
UPDATE YOUR_TABLE SET [FIELD]=[VALUE] WHERE {CONDITION}
I didn't checked this with older version of sql server but this will work with sql server 2012.
Try this script to create a temporary table TESTTEST and watch the order of precedence as the triggers are called in this order: 1) INSTEAD OF, 2) FOR, 3) AFTER
All of the logic is placed in INSTEAD OF trigger and I have 2 examples of how you might code some scenarios...
Good luck...
CREATE TABLE TESTTEST
(
ID INT,
Modified0 DATETIME,
Modified1 DATETIME
)
GO
CREATE TRIGGER [dbo].[tr_TESTTEST_0] ON [dbo].TESTTEST
INSTEAD OF INSERT,UPDATE,DELETE
AS
BEGIN
SELECT 'INSTEAD OF'
SELECT 'TT0.0'
SELECT * FROM TESTTEST
SELECT *, 'I' Mode
INTO #work
FROM INSERTED
UPDATE #work SET Mode='U' WHERE ID IN (SELECT ID FROM DELETED)
INSERT INTO #work (ID, Modified0, Modified1, Mode)
SELECT ID, Modified0, Modified1, 'D'
FROM DELETED WHERE ID NOT IN (SELECT ID FROM INSERTED)
--Check Security or any other logic to add and remove from #work before processing
DELETE FROM #work WHERE ID=9 -- because you don't want anyone to edit this id?!?!
DELETE FROM #work WHERE Mode='D' -- because you don't want anyone to delete any records
SELECT 'EV'
SELECT * FROM #work
IF(EXISTS(SELECT TOP 1 * FROM #work WHERE Mode='I'))
BEGIN
SELECT 'I0.0'
INSERT INTO dbo.TESTTEST (ID, Modified0, Modified1)
SELECT ID, Modified0, Modified1
FROM #work
WHERE Mode='I'
SELECT 'Cool stuff would happen here if you had FOR INSERT or AFTER INSERT triggers.'
SELECT 'I0.1'
END
IF(EXISTS(SELECT TOP 1 * FROM #work WHERE Mode='D'))
BEGIN
SELECT 'D0.0'
DELETE FROM TESTTEST WHERE ID IN (SELECT ID FROM #work WHERE Mode='D')
SELECT 'Cool stuff would happen here if you had FOR DELETE or AFTER DELETE triggers.'
SELECT 'D0.1'
END
IF(EXISTS(SELECT TOP 1 * FROM #work WHERE Mode='U'))
BEGIN
SELECT 'U0.0'
UPDATE t SET t.Modified0=e.Modified0, t.Modified1=e.Modified1
FROM dbo.TESTTEST t
INNER JOIN #work e ON e.ID = t.ID
WHERE e.Mode='U'
SELECT 'U0.1'
END
DROP TABLE #work
SELECT 'TT0.1'
SELECT * FROM TESTTEST
END
GO
CREATE TRIGGER [dbo].[tr_TESTTEST_1] ON [dbo].TESTTEST
FOR UPDATE
AS
BEGIN
SELECT 'FOR UPDATE'
SELECT 'TT1.0'
SELECT * FROM TESTTEST
SELECT 'I1'
SELECT * FROM INSERTED
SELECT 'D1'
SELECT * FROM DELETED
SELECT 'TT1.1'
SELECT * FROM TESTTEST
END
GO
CREATE TRIGGER [dbo].[tr_TESTTEST_2] ON [dbo].TESTTEST
AFTER UPDATE
AS
BEGIN
SELECT 'AFTER UPDATE'
SELECT 'TT2.0'
SELECT * FROM TESTTEST
SELECT 'I2'
SELECT * FROM INSERTED
SELECT 'D2'
SELECT * FROM DELETED
SELECT 'TT2.1'
SELECT * FROM TESTTEST
END
GO
SELECT 'Start'
INSERT INTO TESTTEST (ID, Modified0) VALUES (9, GETDATE())-- not going to insert
SELECT 'RESTART'
INSERT INTO TESTTEST (ID, Modified0) VALUES (10, GETDATE())--going to insert
SELECT 'RESTART'
UPDATE TESTTEST SET Modified1=GETDATE() WHERE ID=10-- gointo to update
SELECT 'RESTART'
DELETE FROM TESTTEST WHERE ID=10-- not going to DELETE
SELECT 'FINISHED'
SELECT * FROM TESTTEST
DROP TABLE TESTTEST
First off, your trigger as you already see is going to update every record in the table. There is no filtering done to accomplish jus the rows changed.
Secondly, you're assuming that only one row changes in the batch which is incorrect as multiple rows could change.
The way to do this properly is to use the virtual inserted and deleted tables: http://msdn.microsoft.com/en-us/library/ms191300.aspx
Trigger
special kind of stored procedure
automatically execured/fired when some event Insert/Update/Delete Occures
use when we want some event to happen automatically on certain desirable scenarios
triggers makes use of 2 tables inserted/deleted table in ssms(memory)
ONLY availabe in context of trigger(CANNOT ACCESS Outside the Trigger
when we insert/delete using trigger, a copy of row is maintained in the inserted/deleted table
inserted table - contains updated data |
deleted table - contains old data
Trigger to Update "ModifiedOn" Date Automatically when record in table is modified(UPDATED)
CREATE TRIGGER [dbo].[Trg_TableName_UpdateModifiedOn]
ON [dbo].[TableName]
AFTER UPDATE
AS
BEGIN
UPDATE [dbo].[TableName]
SET [ModifiedOn] = GetDate()
FROM [inserted]
WHERE [inserted].[PrimaryKey] = [dbo].[TableName].[PrimaryKey];
END;
CREATE TRIGGER [dbo].[after_update] ON [dbo].[MYTABLE]
AFTER UPDATE
AS
BEGIN
DECLARE #ID INT
SELECT #ID = D.ID
FROM inserted D
UPDATE MYTABLE
SET mytable.CHANGED_ON = GETDATE()
,CHANGED_BY = USER_NAME(USER_ID())
WHERE ID = #ID
END

SQL: How do I get the last updated row to use in a trigger

So I got this trigger that looks like this
CREATE TRIGGER trLoadingOvertime
ON trailerScheme
FOR UPDATE AS
IF (SELECT COUNT(*) FROM trailerScheme
WHERE DATEDIFF(mi, trailerScheme.expectedFinishTime, trailerScheme.finishTime) > 15) > 0
BEGIN
INSERT INTO errorTable2
SELECT trailerSchemeID FROM inserted
END
and between BEGIN and END I need to insert values into errorTable2 from the trailerScheme that was just updated.
I hope someone can help me
Update:
When I use this code it just give me NULL
DECLARE #id INT
SELECT
#id = deleted.trailerSchemeID
FROM
inserted
INNER JOIN
deleted
ON inserted.trailerSchemeID = deleted.trailerSchemeID
INSERT INTO errorTable2 VALUES(#id)
Are you looking for this?
CREATE TRIGGER trLoadingOvertime
ON trailerScheme
FOR UPDATE AS
INSERT INTO errorTable2
SELECT trailerSchemeID
FROM inserted
WHERE DATEDIFF(mi, expectedFinishTime, finishTime) > 15
Here is SQLFiddle demo.
Figured it out my self. Here is the correct code:
DROP TRIGGER trLoadingOvertime
GO
CREATE TRIGGER trLoadingOvertime
ON trailerScheme
FOR INSERT AS
IF EXISTS (SELECT expectedFinishTime, finishTime
FROM inserted
WHERE DATEDIFF(mi, expectedFinishTime, finishTime) > 15)
BEGIN
DECLARE #id INT
DECLARE #exFTime DATETIME
DECLARE #fTime DATETIME
DECLARE #delay INT
SET #id = (SELECT trailerSchemeID FROM inserted)
SET #exFTime = (SELECT expectedFinishTime FROM inserted)
SET #fTime = (SELECT finishTime FROM inserted)
SET #delay = (SELECT DATEDIFF(mi, #exFTime, #fTime))
INSERT INTO errorTable VALUES(#id, #delay)
END
I was trying to get inserted, but the problem was that this trigger was called by an update and therefore the inserted was empty.

How do I create and set fields for insert_date and last_modified_date in sql server 2008?

I'd like to record the insert date and an update date on a table.
What is the best way to do this in SQL Server 2008?
For Insert Date you can use the following trigger:
CREATE TRIGGER INSERT_DATE ON TABLE1
FOR INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE TABLE1
SET CreatedOn = GETDATE()
FROM TABLE1 A
INNER JOIN Inserted INS ON (INS.Id = A.Id)
SET NOCOUNT OFF
END
and for Update you can use the following trigger:
CREATE TRIGGER Update ON TABLE1
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE TABLE1
SET UpdatedOn = GETDATE()
FROM TABLE1 A
INNER JOIN Inserted INS ON (A.Id = INS.Id)
SET NOCOUNT OFF
END
For the insert date column, you can set the column default to GETDATE() (or GETUTCDATE()).
For the update date, you would need to use a trigger to set the column to the current date whenever there's an update.

Update trigger insert Null

I am trying to create trigger on SQL Server 2008. I want that if i update field in tabele log that the new value update field in another table Doc. This is the code for trigger:
Create TRIGGER dbo.DocSt
ON dbo.log
AFTER UPDATE
IF (SELECT COUNT(*) FROM inserted) > 0
BEGIN
IF (SELECT COUNT(*) FROM deleted) > 0
BEGIN
UPDATE [dbo].[Doc]
SET
[ID_user] = (select ID_user from inserted)
WHERE
IDN= (select id_doc from inserted)
END
END
When I update field in table log triger update table Doc but it insert NULL.
What i am doing wrong? Thanks!
This code won't ever work - what happens in your UPDATE statement updates 10 rows?? What does this select give you:
SET [ID_user] = (select ID_user from inserted)
You're trying to set a single value to a whole return set from a SELECT statement - that won't work, obviously.
You need to create an UPDATE statement that joins with the Inserted pseudo-table:
CREATE TRIGGER dbo.DocSt
ON dbo.log AFTER UPDATE
UPDATE [dbo].[Doc]
FROM Inserted i
SET [ID_user] = i.ID_User
WHERE IDN = i.id_doc
That way, for each entry in Inserted, you're joining your table dbo.Doc to it and update the ID_user column.