SQL trigger not working in Azure Elastic DB - sql

I have setup an Elastic Agent from Azure.
The elastic agent uses a db to maintain and work its logic in Azure.
The list of table which it creates is long. But this tables are what I am working on.
jobs_internal.job_exectutions (holds each run of the elastic job)
dbo.custom_status (table created to track activity of executions)
Table where DML operations will insert to dbo.custom_status schema
CREATE TABLE [dbo].[custom_status](
[Id] [int] IDENTITY(1,1) NOT NULL,
[job_id] [uniqueidentifier] NOT NULL,
[job_name] [nvarchar](128) NULL,
[job_execution_id] [uniqueidentifier] NOT NULL,
[start_time] [datetime] NULL,
[end_time] [datetime] NULL,
[lifecycle] [nvarchar](50) NULL,
[message] [nvarchar](max) NULL,
[exception] [nvarchar](max) NULL,
[column_state] [nvarchar](20) NOT NULL,
[entry_time] [datetime] NULL,
CONSTRAINT [PK_dbo.custom_status] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[custom_status] ADD DEFAULT (getdate()) FOR [entry_time]
GO
When I introduce trigger on the jobs_internal.job_executions on AFTER UPDATE, INSERT, DELETE
CREATE TRIGGER [jobs_internal].[TR_status_message]
ON [jobs_internal].[job_executions]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN TRY
DECLARE #activity NVARCHAR(20);
IF EXISTS (SELECT * FROM INSERTED) AND EXISTS (SELECT * FROM DELETED)
BEGIN
SET #activity = 'UPDATE';
INSERT INTO [dbo].[custom_status]
([job_id]
,[job_name]
,[job_execution_id]
,[start_time]
,[end_time]
,[lifecycle]
,[message]
,[exception]
,[column_state])
SELECT
org_job_exe.[job_id]
,(SELECT name
FROM jobs_internal.jobs
WHERE job_id = org_job_exe.job_id)
,org_job_exe.[job_execution_id]
,org_job_exe.[start_time]
,org_job_exe.[end_time]
,org_job_exe.[lifecycle]
,(SELECT message
FROM jobs_internal.job_task_executions
WHERE job_execution_id = org_job_exe.job_execution_id)
,(SELECT exception
FROM jobs_internal.job_task_executions
WHERE job_execution_id = org_job_exe.job_execution_id)
,#activity
FROM INSERTED as org_job_exe;
PRINT 'UPDATE operation failed in custom message trigger';
END;
IF EXISTS (SELECT * FROM deleted) AND NOT EXISTS (SELECT * FROM inserted)
BEGIN
SET #activity = 'DELETE';
INSERT INTO [dbo].[custom_status]
([job_id]
,[job_name]
,[job_execution_id]
,[start_time]
,[end_time]
,[lifecycle]
,[message]
,[exception]
,[column_state])
SELECT
org_job_exe.[job_id]
,(SELECT name
FROM jobs_internal.jobs
WHERE job_id = org_job_exe.job_id)
,org_job_exe.[job_execution_id]
,org_job_exe.[start_time]
,org_job_exe.[end_time]
,org_job_exe.[lifecycle]
,(SELECT message
FROM jobs_internal.job_task_executions
WHERE job_execution_id = org_job_exe.job_execution_id)
,(SELECT exception
FROM jobs_internal.job_task_executions
WHERE job_execution_id = org_job_exe.job_execution_id)
,#activity
FROM DELETED as org_job_exe;
PRINT 'DELETE operation failed in custom message trigger';
END;
BEGIN
SET #activity = 'INSERT';
INSERT INTO [dbo].[custom_status]
([job_id]
,[job_name]
,[job_execution_id]
,[start_time]
,[end_time]
,[lifecycle]
,[message]
,[exception]
,[column_state])
SELECT
org_job_exe.[job_id],
'test'
--,(SELECT name
--FROM jobs_internal.jobs
--WHERE job_id = org_job_exe.job_id)
,org_job_exe.[job_execution_id]
,org_job_exe.[start_time]
,org_job_exe.[end_time]
,org_job_exe.[lifecycle]
,'test'
--,(SELECT message
--FROM jobs_internal.job_task_executions
--WHERE job_execution_id = org_job_exe.job_execution_id)
,'test'
,#activity
FROM INSERTED as org_job_exe;
PRINT 'INSERT operation failed in custom message trigger';
END;
END TRY
BEGIN CATCH
PRINT 'Custom message trigger failed';
THROW;
END CATCH;
The trigger works but stops all the other CRUD operations/ DML operations on the base table stops causing the elastic jobs to not work and pile up in the queue.
There is already trigger created by ElasticJob server on this table and that is not stopping DML operations for the table
Hence, I tried it on a view
jobs.job_executions with DML of INSTEAD OF
ALTER TRIGGER [jobs].[TR_job_alert]
ON [jobs].[job_executions]
INSTEAD OF UPDATE
AS
BEGIN
--just to test normal is being triggered
insert custom_status
values (NEWID(),
'testing',
NEWID(),
null,
null,
'test',
null,
null,
'test',
GETDATE());
--INSERT INTO [dbo].[custom_status]
-- ([job_id]
-- ,[job_name]
-- ,[job_execution_id]
-- ,[start_time]
-- ,[end_time]
-- ,[lifecycle]
-- ,[message]
-- ,[exception]
-- ,[column_state])
--SELECT
-- org_job_exe.[job_id]
-- ,org_job_exe.[job_name]
-- ,org_job_exe.[job_execution_id]
-- ,org_job_exe.[start_time]
-- ,org_job_exe.[end_time]
-- ,org_job_exe.[lifecycle]
-- ,org_job_exe.[last_message]
-- ,null
-- ,#activity
--FROM inserted as org_job_exe
--BEGIN
-- SET #activity = 'INSERT';
-- INSERT INTO [dbo].[custom_status]
-- ([job_id]
-- ,[job_name]
-- ,[job_execution_id]
-- ,[start_time]
-- ,[end_time]
-- ,[lifecycle]
-- ,[message]
-- ,[exception]
-- ,[column_state])
-- SELECT
-- org_job_exe.[job_id]
-- ,(SELECT name
-- FROM jobs_internal.jobs
-- WHERE job_id = org_job_exe.job_id)
-- ,org_job_exe.[job_execution_id]
-- ,org_job_exe.[start_time]
-- ,org_job_exe.[end_time]
-- ,org_job_exe.[lifecycle]
-- ,(SELECT message
-- FROM jobs_internal.job_task_executions
-- WHERE job_execution_id = org_job_exe.job_execution_id)
-- ,(SELECT exception
-- FROM jobs_internal.job_task_executions
-- WHERE job_execution_id = org_job_exe.job_execution_id)
-- ,#activity
-- FROM inserted as org_job_exe
--END;
END;
GO
The trigger on the view never fires!
I know little bit of bulk DML operations won't fire triggers.
Is there something wrong with my trigger.
The fast trigger if it fires it will block all the other operations on table
The second trigger never fires on the view.

Try removing the PRINT statements. Long shot but maybe they are causing a hang that stops future processing.

Related

The DELETE statement conflicted with the REFERENCE constraint "FK_BlogCommentReply_BlogCommentId"

I have a blog and keeping it simple for discussion, the main tables are Blog, BlogComment, BlogCommentReply and User.
A blog belongs to a User.
A comment belongs to a blog/user.
A comment reply belongs to a blog/comment/user.
I want to delete a User - UserId = 5.
My delete user stored procedure deletes (wrapped in a transaction) in this order:
BlogCommentReply
BlogComment
Blog (if the user is the author of the blog only)
User
It deletes the entry(s) in the BlogCommentReply for the User, then attempts to delete the entry(s) in the BlogComment for the User.
I then get the error:
The DELETE statement conflicted with the REFERENCE constraint "FK_BlogCommentReply_BlogCommentId".
The conflict occurred in database "DBGbngDev", table "dbo.BlogCommentReply", column
'BlogCommentId'.
It still thinks there is an entry in the BlogCommentReply table.
Here is the execute of the stored procedure with debug selects in it to show that it deletes the BlogCommentReply entry but then fails on the delete of the BlogComment.
Table definitions:
----------------------- BlogComment table (parent to blogCommentReply)
CREATE TABLE [dbo].[BlogComment](
[BlogCommentId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[BlogId] [int] NOT NULL,
[BlogCommentContent] [varchar](max) NOT NULL,
[LikeCount] [int] NOT NULL,
[DisLikeCount] [int] NOT NULL,
[DateTimeOfBlogComment] [datetime] NOT NULL,
CONSTRAINT [PK_BlogComment] PRIMARY KEY CLUSTERED
(
[BlogCommentId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[BlogComment] WITH CHECK ADD CONSTRAINT [FK_BlogComment_BlogId] FOREIGN
KEY([BlogId])
REFERENCES [dbo].[Blog] ([BlogId])
GO
ALTER TABLE [dbo].[BlogComment] CHECK CONSTRAINT [FK_BlogComment_BlogId]
GO
ALTER TABLE [dbo].[BlogComment] WITH CHECK ADD CONSTRAINT [FK_BlogComment_UserId] FOREIGN
KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
GO
ALTER TABLE [dbo].[BlogComment] CHECK CONSTRAINT [FK_BlogComment_UserId]
GO
----------------------- BlogCommentReply table (child)
CREATE TABLE [dbo].[BlogCommentReply](
[BlogCommentReplyId] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[BlogCommentId] [int] NOT NULL,
[BlogId] [int] NOT NULL,
[BlogCommentReplyContent] [varchar](max) NOT NULL,
[LikeCount] [int] NOT NULL,
[DisLikeCount] [int] NOT NULL,
[DateTimeOfBlogCommentReply] [datetime] NOT NULL,
CONSTRAINT [PK_BlogCommentReply] PRIMARY KEY CLUSTERED
(
[BlogCommentReplyId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_BlogCommentId]
FOREIGN KEY([BlogCommentId])
REFERENCES [dbo].[BlogComment] ([BlogCommentId])
GO
ALTER TABLE [dbo].[BlogCommentReply] CHECK CONSTRAINT [FK_BlogCommentReply_BlogCommentId]
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_BlogId] FOREIGN
KEY([BlogId])
REFERENCES [dbo].[Blog] ([BlogId])
GO
ALTER TABLE [dbo].[BlogCommentReply] CHECK CONSTRAINT [FK_BlogCommentReply_BlogId]
GO
ALTER TABLE [dbo].[BlogCommentReply] WITH CHECK ADD CONSTRAINT [FK_BlogCommentReply_UserId] FOREIGN
KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
GO
ALTER TABLE [dbo].[BlogCommentReply] CHECK CONSTRAINT [FK_BlogCommentReply_UserId]
GO
Extracted parts of the delete stored procedure (#a_UserId = 5):
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 6. Get the number of blog comments replys for the user.
-- Note: there may not be any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SELECT #BlogCommentReplysCount = Count(*)
FROM dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR
IF #ReturnCode <> 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the select of BlogCommentReply. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT '#BlogCommentReplysCount: ' + CAST(#BlogCommentReplysCount AS VARCHAR) + '. '
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 7. Delete the BlogCommentReply entries for the user.
-- Note: the user may not have made any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DELETE dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR,
#RowCount = ##ROWCOUNT
IF #ReturnCode <> 0 AND #RowCount > 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the delete of BlogCommentReply. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT 'Deleted BlogCommentReply'
SELECT '#ReturnCode: ' + CAST(#ReturnCode AS VARCHAR) + '. '
SELECT '#RowCount: ' + CAST(#RowCount AS VARCHAR) + '. '
SELECT '#a_UserId: ' + CAST(#a_UserId AS VARCHAR) + '. '
SELECT Count(*) as AFTERDELETE
FROM dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 8. Get the number of blog comments for this user.
-- Note: there may not be any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SELECT #BlogCommentsCount = Count(*)
FROM dbo.BlogComment
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR
IF #ReturnCode <> 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the select of BlogComment. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT '#BlogCommentsCount: ' + CAST(#BlogCommentsCount AS VARCHAR) + '. '
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Step 9. Delete the BlogComment entries for the user.
-- Note: the user may not have made any.
--+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SELECT Count(*) as BEFORE
FROM dbo.BlogComment
WHERE ( UserId = #a_UserId )
SELECT 'Going to delete BlogComment'
DELETE dbo.BlogComment
WHERE ( UserId = #a_UserId )
SELECT #ReturnCode = ##ERROR,
#RowCount = ##ROWCOUNT
IF #ReturnCode <> 0 AND #RowCount > 0
BEGIN
ROLLBACK TRANSACTION
SELECT #Message = 'Critical Error - procedure DeleteBlog failed during the delete of BlogComment. User id: ' + CAST(#a_UserId AS VARCHAR) + '. '
RAISERROR (#Message, 16, 1)
END
SELECT 'Deleted BlogComment'
I think it's better to have your complete stored procedure script to check the error, but for the mean time I guess the problem is here:
DELETE dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
Ok, it's deleting all of comment replies which the related user (i.e. #a_UserId = 5) has.
But look at the rest of your script:
DELETE dbo.BlogComment
WHERE ( UserId = #a_UserId )
It's deleting all of user's comments, WHILE they may have some other comment replies for another users and they are not deleted. So I think the problem is here.
Maybe it's better to change the first deletion part to:
DELETE dbo.BlogCommentReply
WHERE ( UserId = #a_UserId )
DELETE dbo.BlogCommentReply
FROM [dbo].[BlogComment]
WHERE dbo.BlogCommentReply.BlogCommentId=[dbo].[BlogComment].BlogCommentId
AND [dbo].[BlogComment].UserId=#a_UserId
It's very simple you are deleting comment's reply of a specific user while other users might have reply under the same comment.
I you really want to delete all related info of a user , you have to go with different approach like finding all replies under that user's comment and delete those.

Run large compare in Batches

I need to compare large tables with millions of rows and insert the differences into a log table.
The problem is that stored proc grows (LDF), and the database disk space is limited.
I know that commit will write the LDF to the MDF.
How can I perform the following compare in batches, and commit every hundred thousand rows?
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO dbo.CustomerLog
( OnlineStore ,
PhoneNumber ,
ChangeType
)
SELECT 'Online Store a' ,
AreaCode + PhoneNumber ,
'Added'
FROM dbo.StoreAList a
WHERE NOT EXISTS ( SELECT 1
FROM dbo.StoreAListCompare b
WHERE ( b.AreaCode + b.PhoneNumber ) = ( a.AreaCode
+ a.PhoneNumber ) );
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH;
CREATE TABLE [dbo].[StoreAList](
[ListID] [bigint] IDENTITY(1,1) NOT NULL,
[AreaCode] [char](3) NOT NULL,
[PhoneNumber] [char](7) NOT NULL,
[RecordDate] [datetime] NULL CONSTRAINT [DF_StoreAList_RecordDate] DEFAULT (getdate())
) ON [PRIMARY]
CREATE TABLE [dnc].[StoreAListCompare](
[ListID] [BIGINT] IDENTITY(1,1) NOT NULL,
[AreaCode] [CHAR](3) NOT NULL,
[PhoneNumber] [CHAR](7) NOT NULL,
[RecordDate] [DATETIME] NULL DEFAULT (GETDATE())
) ON [PRIMARY]
You can use the ##rowcount system variable and do the insert in batches until ##rowcount hits 0.
Note the added AND NOT EXISTS in dbo.CustomerLog...
Ex:
DECLARE #BATCHSIZE INT=100000
WHILE #BATCHSIZE>0
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO dbo.CustomerLog
( OnlineStore ,
PhoneNumber ,
ChangeType
)
SELECT TOP(#BATCHSIZE)
'Online Store a' ,
AreaCode + PhoneNumber ,
'Added'
FROM dbo.StoreAList a
WHERE NOT EXISTS ( SELECT 1
FROM dbo.StoreAListCompare b
WHERE ( b.AreaCode + b.PhoneNumber ) = ( a.AreaCode + a.PhoneNumber ) )
AND NOT EXISTS (SELECT 1
FROM dbo.CustomerLog CL
WHERE 'Online Store a'=CL.OnlineStore
AND AreaCode + PhoneNumber=CL.PhoneNumber
AND 'Added'=CL.ChangeType);
SET #BATCHSIZE=##ROWCOUNT
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
SET #BATCHSIZE=0
END CATCH;
END

Log table using trigger

I need to create trigger which insert into dbo.Log table values:
- in action_type : how many records was UPDATED ,DELETED or INSERTED
- in datetime_of_action ofcourse timestamp of action
For now I have this:
DROP TABLE dbo.Log
CREATE TABLE dbo.Log (
logid INT NOT NULL IDENTITY,
action_type NVARCHAR(50) NOT NULL,
datetime_of_action DATETIME NOT NULL,
CONSTRAINT PK_Log PRIMARY KEY(logid));
CREATE TRIGGER trig2
ON Sales.Customers
FOR UPDATE , DELETE, INSERT
AS
BEGIN
......................
END
SELECT * FROM dbo.Log
And this is a script of Sales.Customers table:
CREATE TABLE [Sales].[Customers](
[custid] [int] IDENTITY(1,1) NOT NULL,
[companyname] [nvarchar](40) NOT NULL,
[contactname] [nvarchar](30) NOT NULL,
[contacttitle] [nvarchar](30) NOT NULL,
[address] [nvarchar](60) NOT NULL,
[city] [nvarchar](15) NOT NULL,
[region] [nvarchar](15) NULL,
[postalcode] [nvarchar](10) NULL,
[country] [nvarchar](15) NOT NULL,
[phone] [nvarchar](24) NOT NULL,
[fax] [nvarchar](24) NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED
(
[custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
If anyone knows how do this I am open on your advice.
Use this query:
CREATE TRIGGER trig2
ON Sales.Customers
AFTER UPDATE , DELETE, INSERT
AS
BEGIN
IF EXISTS (select * From inserted)
BEGIN
IF EXISTS (select * From deleted)
BEGIN
INSERT INTO dbo.Log (action_typ, datetime_of_action )
VALUES(CAST(SELECT COUNT(*) FROM deleted) as VARCHAR(50) + ' records have been updated',GETDATE())
END
ELSE
BEGIN
INSERT INTO dbo.Log (action_typ, TOTAL, datetime_of_action )
VALUES(CAST(SELECT COUNT(*) FROM inserted) as VARCHAR(50) + ' records have been iniserted',GETDATE())
END
ELSE
BEGIN
INSERT INTO dbo.Log (action_typ, TOTAL, datetime_of_action )
VALUES(CAST(SELECT COUNT(*) FROM deleted) as VARCHAR(50) + ' records have been deleted',GETDATE())
END
END
CREATE TRIGGER trig2
ON Sales.Customers
FOR UPDATE , DELETE, INSERT
AS
BEGIN
SET NOCOUNT ON;
-- First determine the action
DECLARE #Action NVARCHAR(50);
SET #Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED) AND EXISTS(SELECT * FROM DELETED)
THEN N'Update: ' + CAST( (SELECT COUNT(*) FROM INSERTED) AS NVARCHAR(10))
WHEN EXISTS(SELECT * FROM INSERTED) AND NOT EXISTS(SELECT * FROM DELETED)
THEN N'Insert: ' + CAST( (SELECT COUNT(*) FROM INSERTED) AS NVARCHAR(10))
WHEN EXISTS(SELECT * FROM DELETED) AND NOT EXISTS(SELECT * FROM INSERTED)
THEN N'Delete: ' + CAST( (SELECT COUNT(*) FROM DELETED) AS NVARCHAR(10))
ELSE NULL -- Skip. It may have been a "failed delete".
END)
INSERT INTO dbo.[Log] (action_type, datetime_of_action)
VALUES (#Action , GETDATE());
END

updated record is inserting into the history table not the old record

i have two tables Test and TestHistory
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
INSERT INTO TEST ([ID],[Name],[Status],[CreatedDate])values (1,'Mohan','A',GETDATE())
Created Trigger :
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
Declare #ID INT;
Declare #Name varchar(10);
Declare #Status CHAR(2);
Declare #CreatedDate DATETIME;
Select #ID = I.ID from INSERTED I
Select #Name = I.Name from INSERTED I
Select #Status = I.Status from INSERTED I
Select #CreatedDate = I.CreatedDate from INSERTED I
INSERT INTO [dbo].[Test_history]
([ID]
,[Name]
,[Status]
,[CreatedDate]
)
SELECT #ID,
#Name,
#Status,
GETDATE()
FROM INSERTED I
WHERE #ID = [ID]
When I'm updating the record like
Update [TEST] SET Status = 'I' then the old record with Status = 'A' should inserted but the what ever i'm updating it has been inserting into Testhistory table not the old record
where i'm doing wrong and how to insert old value
like if i updating Status = 'I' and in history table Status = 'A' shoul be inserted
You need to INSERT from DELETED not from INSERTED.
See examples here Understanding SQL Server inserted and deleted tables for DML triggers.
Like Karl mentioned, you need to refer deleted table for old updated row information. Apart from that your existing code doesn't work when you update more than a single row. What you need is something like this.
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
INSERT INTO [dbo].[Test_history]
(
[ID],
[Name],
[Status],
[CreatedDate]
)
SELECT ID,
Name,
Status,
GETDATE()
FROM deleted d

Inserted clause returns 0 when used with triggers

I'm trying to get the last inserted rows Id from an inserts statement on the following table using SQL server 2012
[dbo].[Table](
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED
(
[TableId] ASC
)
I'm also using an audit triggers on that table that are as follows:
trigger [dbo].[trigger_Table_auditColumnAutoInsert]
on [dbo].[Table]
instead of insert
/**************************************************************
* INSTEAD OF trigger on table [dbo].[Table] responsible
for automatically inserting audit column data
**************************************************************/
as
begin
set nocount on
declare #currentTime datetime2
set #currentTime = GETUTCDATE()
insert into [dbo].[Table]
(
Name,
CreatedBy,
CreatedDate,
ModifiedBy,
ModifiedDate
)
select
Name,
ISNULL(CreatedBy, system_user),
#currentTime,
NULL,
NULL
from inserted
select SCOPE_IDENTITY() as [TableId]
goto EOP -- end of procedure
ErrorHandler:
if (##trancount <> 0) rollback tran
EOP:
end
I used different approaches, but nothing 'SAFE' seems to work.
Using scope identity returns null
insert into dbo.[Table](Name) Values('foo')
select SCOPE_IDENTITY()
Using OUTPUT INSERTED always returns 0 for the identity coloumns; although it returns the other inserted values:
declare #tmpTable table
(
TableId int,
Name nvarchar (50)
)
INSERT INTO [dbo].[Table]([Name])
output inserted.TableId, inserted.Name into #tmpTable
VALUES('foo')
select * from #tmpTable
TableId Name
0 foo
I know of another solution to get the inserted Id from the triggers itself, by executing a dynamic sql command as follows:
declare #tmpTable table (id int)
insert #tmpTable (id )
exec sp_executesql N'insert into dbo.[Table](Name) Values(''foo'')'
select id from #tmpTable
I couldn't figure out why in the first 2 cases it is not working; why the SCOPE_IDENTITY() does not work although the triggers execute in the same transaction? And also why the INSERTED clause returns 0 for the identity column.
It appears that the following requirements apply to your audit column data:
Use the insert value supplied for CreatedBy, or use SYSTEM_USER by default.
Always use GETUTCDATE() for CreatedDate.
If the INSTEAD OF trigger (rather than an AFTER trigger) is not essential to your requirements, then you can use DEFAULT constraints on your audit columns and an AFTER INSERT trigger to enforce requirement #2.
CREATE TABLE [dbo].[Table]
(
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NOT NULL CONSTRAINT [DF_Table_CreatedBy] DEFAULT SYSTEM_USER,
[CreatedDate] [datetime2](7) NOT NULL CONSTRAINT [DF_Table_CreatedDate] DEFAULT GETUTCDATE(),
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED ([TableId] ASC)
)
GO
CREATE TRIGGER Trigger_Table_AfterInsert ON [dbo].[Table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE [dbo].[Table] SET [CreatedDate]=GETUTCDATE()
FROM [dbo].[Table] AS T
INNER JOIN INSERTED AS I ON I.[TableId]=T.[TableId]
END
GO
Then, both SCOPE_IDENTITY() and OUTPUT INSERTED techniques to get the new TableId value work as expected.
If the INSTEAD OF trigger is essential to your implementation, then SELECT ##IDENTITY is an alternative to SCOPE_IDENTITY.