I'm trying to create a database trigger in SQL Server 2008 for a tblCustomer table. I need it to add a new row into the tblChanges table every time there is an insert, update, or delete within the tblCustomer table.
Specifically, I need it to insert the CustomerId (PK) that was changed, the dateTime that it the changed occurred, and the type of change (insert, update, delete).
I've got something along these lines so far but can't figure out the rest:
CREATE TRIGGER change_trigger
AFTER INSERT OR UPDATE OR DELETE
ON tblCustomer
DECLARE log_action varchar(30)
BEGIN
IF INSERTING THEN
log_action := 'I';
ELSEIF UPDATING THEN
log_action := 'U';
ELSEIF DELETEING THEN
log_action := 'D';
ELSE
DBMS_OUTPUT.PUT_LINE('undefined');
END IF;
INSERT INTO tblChanges(ChanedPK, ChangedTime, ChangedType)
VALUES ...
I'm unsure if any of the above SQL is correct as I haven't tried running it yet and my knowledge of SQL is limited. Any help completing the code and correcting errors would be appreciated.
Try something along these lines:
CREATE TRIGGER change_trigger AFTER INSERT, UPDATE, DELETE
ON tblCustomer
AS
BEGIN
DECLARE log_action varchar(30)
IF EXISTS (SELECT * FROM INSERTED) AND NOT EXISTS(SELECT * FROM DELETED)
SET log_action = 'INSERT'
IF EXISTS NOT (SELECT * FROM INSERTED) AND EXISTS(SELECT * FROM DELETED)
SET log_action = 'DELETE'
IF EXISTS (SELECT * FROM INSERTED) AND EXISTS(SELECT * FROM DELETED)
SET log_action = 'UPDATE'
-- Your other code goes here
END
Maybe you should read up un T-SQL and DML triggers in SQL Server first. MSDN and many other sites provide excellent examples on how to go about things.
Related
DROP TRIGGER IF EXISTS N2Trigger
CREATE TRIGGER N2Trigger
ON dbo.Date
FOR INSERT, DELETE
AS
BEGIN
SELECT 'Inserted Datebase' as MESSAGE
SELECT 'Deleted Database' as MESSAGE
END
DELETE FROM dbo.[Date] WHERE ID = 1
Here is my code I just want when I use insert statement return 'Inserted Datebase' as MESSAGE
When I use delete statement return 'Deleted Database' as MESSAGE
The easiest way to check what action fired the trigger is to inspect the inserted and deleted pseudo-tables. If the trigger is only on DELETE/INSERT and not on update, then the logic is simply:
CREATE TRIGGER dbo.trFlarb ON dbo.flarb
FOR INSERT, DELETE
AS
BEGIN
IF EXISTS (SELECT 1 FROM inserted)
BEGIN
SELECT 'Inserted.';
END
IF EXISTS (SELECT 1 FROM deleted)
BEGIN
SELECT 'Deleted.';
END
END
Example db<>fiddle
Now, of course, Marc is right: triggers aren't for returning or printing output. This is just a demonstration that you can use those checks to then perform whatever logic you need to perform in the event of either action.
That said, if you have two distinctly different things you want to do depending on whether it's an insert or a delete, why not just create two separate triggers?
CREATE TRIGGER dbo.tr_I_Flarb ON dbo.flarb
FOR INSERT
AS
BEGIN
SELECT 'Inserted.';
END
GO
CREATE TRIGGER dbo.tr_D_Flarb ON dbo.flarb
FOR DELETE
AS
BEGIN
SELECT 'Deleted.';
END
GO
Note that SELECT will only "work" on your system if you haven't turned on the disallow results from triggers Server Configuration Option. Again, you should try to explain what you really want to do in the event of an insert or update, because the end goal can't be to print or return "Inserted" or "Deleted."
My objective is inserting the first insert, but not letting the second to pass, because NIC is duplicated. I don't know why, but it isn't letting the first one pass without having other NIC to compare if it already exists one equal.
I know I can prevent duplicates with "unique", but I was trying to do with a trigger :/
Create table Utentes
(
numUtente nchar(3),
Name nchar(40) not null,
NIC nchar(8) not null,
Constraint PK_Utente Primary Key(numUtente)
)
create trigger tr_Duplicate
on Utentes
after insert
as
declare #NIC nchar(8)
select #NIC = NIC from inserted
if exists(select * from Utentes where NIC = #NIC)
begin
print 'NIC already in database'
rollback
end
go
insert into Utentes (numUtente, Name, NIC)
values ('123', 'asd', '12345678')
insert into Utentes (numUtente, Name, NIC)
values ('124', 'asd', '12345678')
select * from Utentes
Result:
NIC already in database
Msg 3609, Level 16, State 1, Line 1392
The transaction ended in the trigger. The batch has been aborted.
You should really use a constraint. An "after insert" trigger will actually put the second row in the table . . . and hopefully no one is using NOLOCK for reading it.
In any case, you have to actually count the rows and look for multiple occurrences. It would be something like this:
Create trigger tr_Duplicate on Utentes after INSERT as
begin
if exists (select 1
from utentes u join
inserted i
on u.nic = i.nic
group by u.nic
having count(*) > 1
)
begin
print 'NIC already in database';
rollback;
end;
end;
With an instead of trigger, you would not add new rows into the table if one already exists:
create trigger tr_Duplicate on Utentes after INSERT as
begin
if exists (select 1
from utentes u join
inserted i
on u.nic = i.nic
)
begin
print 'NIC already in database';
rollback;
end;
else
begin
insert into utentes
select i.*
from inserted i;
end;
end;
I would second the sentiment against the use of triggers and would also suggest using UNIQUE constraints. In my humble opinion, I would rather search for a solution in the ETL layer, grouping records as they are inserted. With triggers you will get the aforementioned concurrency and consistency issues, as well as potentially swelling your tempdb or T-log if the table ever gets big enough to take some time to process.
Is it a good idea to add checksum on each record in SQL Server (updated by application only) to prevent and check whether the record was updated manually using an external script? If not, what is the best approach for this?
Use triggers to prevent changes (roll them back). Or use them to audit changes (log the changes into a seperate table).
-- Add trigger to prevent data changes
CREATE TRIGGER [active].[myTriggerOfInfinitePower] ON [active].[mySpecialSparkleTable]
FOR INSERT, UPDATE, DELETE AS
BEGIN
-- Detect inserts
IF EXISTS (select * from inserted) AND NOT EXISTS (select * from deleted)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR ('inserts are not allowed on that table', 15, 1);
RETURN;
END
-- Detect deletes
IF EXISTS (select * from deleted) AND NOT EXISTS (select * from inserted)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR ('deletes are not allowed on that table', 15, 1);
RETURN;
END
-- Detect updates
IF EXISTS (select * from inserted) AND EXISTS (select * from deleted)
BEGIN
ROLLBACK TRANSACTION;
RAISERROR ('updates are not allowed on that table', 15, 1);
RETURN;
END
END;
GO
Source: use triggers to prevent changes
Regarding checksums:
Adding a checksum column (a) won't "prevent" a record change via SQL Script (etc.) and (b) someone running such a script could always update the checksum (in theory - unless you do some secret squirrel hashing).
The trigger is supposed to rollback if the statement in the SELECT exists. The thing is, it doesn't. When I only run the SELECT from the trigger, it shows that a row exists. But when I try DELETE with the same values as I have hard coded in the trigger, the trigger fires but it does not do a rollback. Anyone got any ideas what might be wrong?
CREATE TRIGGER trg_del ON Projektbefattningar
FOR DELETE
AS
SELECT #befNr = Befattningsnr, #pNr = pNr, #EtappNr = Etappnr FROM deleted
-- Not currently using these. Using hard coded values to illustrate my problem
IF EXISTS (
SELECT *
FROM projektbefattningar
WHERE Befattningsnr = 2 AND pNr = 1 and Etappnr = 1 AND Anställningsnr is not null
)
BEGIN
RAISERROR('Could not delete, Anställningsnr is not null', 16, 1)
--THROW 50001, 'Could not delete, Anställningsnr is not null', 1;
ROLLBACK TRANSACTION;
END
GO
When you use FOR DELETE you consider that this TRIGGER will fired after the delete statement is complete. To Verify a condition before DELETE you need to use INSTEAD OF DELETE. Your Trigger declaration will be:
CREATE TRIGGER trg_del ON Projektbefattningar
INSTEAD OF DELETE
AS
Now To Confirm your delete statement you need to include the delete alghouth it will not be deleted.
The final procedure will be like this:
CREATE TRIGGER trg_del ON Projektbefattningar
INSTEAD OF DELETE
AS
SELECT #befNr = Befattningsnr, #pNr = pNr, #EtappNr = Etappnr FROM deleted
-- Not currently using these. Using hard coded values to illustrate my problem
IF EXISTS (
SELECT *
FROM projektbefattningar
WHERE Befattningsnr = 2 AND pNr = 1 and Etappnr = 1 AND Anställningsnr is not null
)
BEGIN
RAISERROR('Could not delete, Anställningsnr is not null', 16, 1)
--THROW 50001, 'Could not delete, Anställningsnr is not null', 1;
ROLLBACK TRANSACTION;
END
ELSE
BEGIN
DELETE FROM YourTableToDelete Where idOfYourTable in ( Select idOfYourTable from Deleted )
END
GO
Try this
CREATE TRIGGER trg_del ON Projektbefattningar
FOR DELETE
AS
SELECT #befNr = Befattningsnr
,#pNr = pNr
,#EtappNr = Etappnr
FROM deleted
-- Not currently using these. Using hard coded values to illustrate my problem
IF EXISTS (
SELECT 1
FROM deleted
WHERE Befattningsnr = 2
AND pNr = 1
AND Etappnr = 1
AND Anställningsnr IS NOT NULL
)
BEGIN
RAISERROR (
'Could not delete, Anställningsnr is not null'
,16
,1
)
--THROW 50001, 'Could not delete, Anställningsnr is not null', 1;
ROLLBACK TRANSACTION;
END
GO
I have a trigger for auditing purchase and sales table. The trigger is on INSERT and DELETE. To check if its "Insert", the condition is
IF (SELECT COUNT(*) FROM INSERTED) > 0
BEGIN
END
How do I check if its an Update command inside the Trigger?
Arun
The "tables" that are in play in an update trigger are still called inserted and deleted, and the old values of the rows are in the deleted table, and the new values are in the inserted table.
You can use logic along these lines to detect whether you are in a insert, update or a delete:
CREATE TRIGGER MyTrigger
ON MyTable
AFTER INSERT,DELETE,UPDATE -- you need to add the "update" here,
-- in order to catch updates as well
AS
BEGIN
Declare #insertedCount int
Declare #deletedCount int
select #insertedCount = COUNT(*) from inserted
select #deletedCount = COUNT(*) from deleted
if (#insertedCount != 0) -- we have some new values
if (#deletedCount = 0) -- do we have old values?
print 'Inserting'
else
print 'Updating'
else
print 'Deleting'
END
An INSERT,DELETE trigger will not fire for updates.
.........................
CREATE an INSERT, UPDATE, DELETE all in one table
IF ##ROWCOUNT = 0
BEGIN
RETURN;
END;
IF EXISTS(SELECT * FROM inserted)
BEGIN
IF EXISTS(SELECT * FROM deleted)
BEGIN
-- for UPDATE
END
ELSE
BEGIN
-- for INSERT
END
END
ELSE
BEGIN
-- for DELETE
END;
Once you've updated your trigger (as Mitch says) to also apply to UPDATEs, you need to check for updates first:
IF EXISTS(select * from inserted) and EXISTS(select * from deleted)
BEGIN
--Update
END
Note that I've switch to EXISTS rather than COUNT(*). Exists more properly describes what we want to establish - that rows exist in the table. We don't care how many rows are in there.
I would like to thank "SWeko" for the clue.
Now, this what I did and the Trigger worked.
CREATE TRIGGER dbo.Sample
ON TableName
FOR INSERT, UPDATE, DELETE
AS
IF (SELECT COUNT(*) FROM INSERTED) > 0
BEGIN
IF (SELECT COUNT(*) FROM DELETED) > 0
BEGIN
-- UPDATE (TABLE).
END
ELSE
BEGIN
-- INSERT (TABLE).
END
END
ELSE
-- DELETE (TABLE).
BEGIN
END