One caveat of using the inserted and deleted tables is that they can both be empty. Are there any other catchya’s I should be aware of? For instance, can the inserted table contain new records as well as updated records?
I am relying on this logic to detect the action in a trigger:
IF EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'U'
IF EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'I'
IF NOT EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'D'
IF NOT EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'X'
EDIT:
This is my solution to the audit trail problem. It has been tested on one MERGE statement that inserts, fake updates, real updates, and deletes.
ALTER TRIGGER [dbo].[LogInsertEditDelete]
ON [dbo].[<TableToAudit>]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--You will need to change @table to match the table to be audited
DECLARE @table VARCHAR(50)
SELECT @table = '<TableToAudit>'
-- date and user
DECLARE @updatedBy VARCHAR(50),
@timestamp DateTime
SELECT @updatedBy = SYSTEM_USER,
@timestamp = GETDATE()
-- Action, U = update, I = insert, D = delete
DECLARE @insertedCount int,
@deletedCount int
SET @insertedCount = (SELECT COUNT(*) FROM inserted)
SET @deletedCount = (SELECT COUNT(*) FROM deleted)
-- handle no action
IF @insertedCount = 0 AND @deletedCount = 0 RETURN
-- handle update
IF @insertedCount <> 0 AND @deletedCount <> 0
BEGIN
INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1)
SELECT
'U',
@table,
@timestamp,
@updatedBy,
CONVERT(VARCHAR(255), i.Id)
FROM
(SELECT Id, BINARY_CHECKSUM(*) Version FROM inserted) i
INNER JOIN
(SELECT Id, BINARY_CHECKSUM(*) Version FROM deleted) d
ON i.Id = d.Id
WHERE
i.Version <> d.Version
RETURN
END
-- handle deletes and inserts
INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1)
SELECT
CASE
WHEN i.Id IS NOT NULL AND d.Id IS NULL THEN 'I'
WHEN i.Id IS NULL AND d.Id IS NOT NULL THEN 'D'
END,
@table,
@timestamp,
@updatedBy,
CONVERT(VARCHAR(255), COALESCE(i.Id, d.Id))
FROM inserted i
FULL OUTER JOIN
deleted d
ON i.Id = d.Id
WHERE i.Id IS NULL OR
d.Id IS NULL
END
This solution is not generic as the primary keys need to be spelled out for each table.
After making my comment I got curious as to what would be in the inserted and deleted tables in a MERGE. Here is an example:
Something similar would happen if you were doing an insert or update.
If you need to know for each record, you might want to use a join and a case statment to figure out the status for each record rather than using scalar variables. Remember triggers operate on the whole set of records inserted/updated/deleted not one record at a time. So the use of scalar variables is often a clue that you are not doing things correctly. If you give us more of an example of what your trigger is going to do, then it would be easier to help you solve your problem as the current approach seems as if it will not cover all records.
Based on your comment above, perhaps this will give you some ideas on what to try within the trigger:
When testing your trigger, you will need the following test cases at a minimum:
You may need more depending on everything your trigger is going to do.