TSQL Trigger Not Saving Variables and/or not Executing Properly - sql-server

I've having trouble getting a TSQL trigger to even work correctly. I've run it through the debugger and it's not setting any of the variables according to SQL Server Management Studio. The damnedest thing is that the trigger itself is executing correctly and there are no errors when it is executed (just says 'execution successful').
The code is as follows (it's a work in progress.... just getting my self familiar):
USE TestDb
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'OfficeSalesQuotaUpdate' AND type = 'TR')
DROP TRIGGER OfficeSalesQuotaUpdate
GO
CREATE TRIGGER OfficeSalesQuotaUpdate
ON SalesReps
AFTER UPDATE, DELETE, INSERT
AS
DECLARE #sales_difference int, #quota_difference int
DECLARE #sales_original int, #quota_original int
DECLARE #sales_new int, #quota_new int
DECLARE #officeid int
DECLARE #salesrepid int
--UPDATE(Sales) returns true for INSERT and UPDATE.
--Not for DELETE though.
IF ((SELECT COUNT(*) FROM inserted) = 0)
SET #salesrepid = (SELECT SalesRep FROM deleted)
ELSE
SET #salesrepid = (SELECT SalesRep FROM inserted)
--If you address the #salesrepid variable, it does not work. Doesn't even
--print out the 'this should work line.
PRINT 'This should work...' --+ convert(char(30), #salesrepid)
IF (#salesrepid = NULL)
PRINT 'SalesRepId is null'
ELSE
PRINT 'SalesRepId is not null'
PRINT convert(char(50), #salesrepid)
SET #officeid = (SELECT RepOffice
FROM SalesReps
WHERE SalesRep = #salesrepid)
SELECT #sales_original = (SELECT Sales FROM deleted)
SELECT #sales_new = (SELECT Sales FROM inserted)
--Sales can not be null, so we'll remove this later.
--Use this as a template for quota though, since that can be null.
IF (#sales_new = null)
BEGIN
SET #sales_new = 0
END
IF (#sales_original = 0)
BEGIN
SET #sales_original = 0
END
SET #sales_difference = #sales_new - #sales_original
UPDATE Offices
SET Sales = Sales + #sales_difference
WHERE Offices.Office = #officeid
GO
So, any tips? I've completely stumped on this one. Thanks in advance.

Your main problem seems to be that there is a difference between #foo = NULL and #foo IS NULL:
declare #i int
set #i = null -- redundant, but explicit
if #i = null print 'equals'
if #i is null print 'is'
The 'This should work' PRINT statement doesn't work because concatenating a NULL with a string gives a NULL, and PRINT NULL doesn't print anything.
As for actually setting the value of #salerepid, it seems most likely that the inserted and/or deleted table is in fact empty. What statements are you using to test the trigger? And have you printed out the COUNT(*) value?
You should also consider (if you haven't already) what happens if someone changes more than one row at once. Your current code assumes that only one row is changed at a time, which may be a reasonable assumption in your environment, but it can easily break if someone bulk loads data or does other 'batch processing'.
Finally, you should always mention your MSSQL version and edition; it can be relevant for some syntax questions.

You should replace the body of the trigger with something like this:
;WITH Totals AS (
SELECT RepOffice,SUM(Sales) as Sales FROM inserted GROUP BY RepOffice
UNION ALL
SELECT RepOffice,-SUM(Sales) FROM deleted GROUP BY RepOffice
), SalesDelta AS (
SELECT RepOffice,SUM(Sales) as Delta FROM Totals GROUP BY RepOffice
)
UPDATE o
SET Sales = Sales + sd.Delta
FROM
Offices o
inner join
SalesDelta sd
on
o.Office = sd.RepOffice
This will adequately cope with multiple rows in inserted and deleted. I'm assuming SalesRep is the primary key of the SalesReps table.
Updated above, to cope with UPDATE changing the RepOffice of a particular Sales Rep (which the original doesn't, presumable, get correct either)

Just a suggestion...have you tried putting BEGIN and END to encapsulate the 'AS' part of your trigger?

Related

Issue with SQL INSERT Trigger

I'm reaching out for some help on this trigger I'm trying to get working.
Basically this is what I'm trying to do.
We have DMS software that writes to a Database and on a particular INSERT value I want the trigger to fire.
This is an example of an INSERT statement that will get processed.
INSERT INTO DOCSADM.ACTIVITYLOG (CR_IN_USE,ACTIVITY_DESC,BILLED_ON,BILLABLE,PAGES,KEYSTROKES,
TYPE_TIME,ELAPSED_TIME,TYPIST,AUTHOR,START_DATE,ACTIVITY_TYPE,REF_DOCUMENT,REF_LIBRARY,APPLICATION,VERSION_LABEL,DOCNUMBER,SYSTEM_ID)
VALUES ('','DOCSFusion','1753-01-01','',0,0,0,0,1920,1920,'2020-08-26T10:17:56',**115**,0,-1,1173,'',75,3252)
but I only want the trigger to fire when we see a value of 115 for the bold section in the INSERT statement (the Activity_type value).
For all other values that re not 115 I don't want to do anything.
This is what I have so far:
CREATE TRIGGER BW_TRIGGER
ON DOCSADM.ACTIVITYLOG
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--Declare some variable and set it as a value of 115.
--Example:
DECLARE #AlogType int = (SELECT I.ACTIVITY_TYPE FROM DOCSADM.ACTIVITYLOG A, INSERTED I) --This is the value you are looking for regarding the DM client/Matter actitivty type.
DECLARE #AlogDesc varchar(32) = (Select i.ACTIVITY_DESC from docsadm.ACTIVITYLOG A, INSERTED I)
--Next, you should have a fork or path in your trigger to determine how it proceeds.
--Path 1: The #AlogType value matches the inserted value so you want to process the rest of the trigger. Example path – “ProcessTrigger:”
--Path 2: The #AlogType value does NOT match the inserted value, you want to exit the trigger. Example Path – “ExitTrigger:”
IF #AlogType <> 115
GOTO TriggerExit;
ELSE
Begin
/*Create first temp table to collect insert values*/ --This table will have the SysID Value and the corresponding docnumber for the items you want.
--You can add whatever other values you think you need.
CREATE TABLE #TempSet1
(
AlogsysID INT,
Docnum INT,
AlogDate Varchar(64),
AlogTypist INT,
AlogAuthor INT,
AlogDesc varchar(32),
ALOGVER varchar(10),
ALOG_MATTER INT
)
INSERT INTO #TempSet1 (AlogsysID,Docnum,AlogDate,AlogTypist,AlogAuthor, ALOG_MATTER)
--SELECT  You SELECT STATEMENT WILL GO HERE MODIFIED TO POPULATE THE TABLE WITH THE DOCNUMBERS YOU WANT!!
select top 1 System_id, docnumber, LAST_ACCESS_DATE, TYPIST, AUTHOR, MATTER from docsadm.PROFILE where EXISTS (SELECT CLIENT.SYSTEM_ID FROM DOCSADM.CLIENT INNER JOIN DOCSADM.MATTER ON MATTER.CLIENT_ID = CLIENT.SYSTEM_ID
WHERE MATTER.SYSTEM_ID =#AlogDesc OR INH_LUP_SEC_FROM IS NULL OR INH_LUP_SEC_FROM = 0) AND MATTER=#AlogDesc
/*Set variable #SysID as the LASTKEY value -1. This will be used to set the SysID column on the #TempSet table*/
--DECLARE #SysID INT = (SELECT LASTKEY FROM DOCSADM.SEQ_SYSTEMKEY) -1;
/*Set the SysID value for every row on the #TempSet1 table as the #SysID variable +1*/
--UPDATE #TempSet1
--SET #SysID = AlogsysID = #SysID + 1
--Your #TempSet should now be set with ALL of the System_IDs and Docnumbers necessary for your insert!!!!—
--Verify this by doing a select against the #TempSet1 Table
SELECT * FROM #TempSet1;
--Next you need to set the SystemID to the correct value for future processing. To do this, we need to get a total count from the #TempSet table.
/*Set a variable to update the NEXTKEY value on the DOCSADM.SEQ_SYSTEMKEY table. The NEXTKEY value is used for the SYSTEM_ID field*/
--DECLARE #SeqUpdateCount INT = (SELECT COUNT(*) FROM #TempSet1);
/*Update the LASTKEY Value on the SEQ_SYSTEMKEY table to the next available value for DM.*/
--UPDATE DOCSADM.SEQ_SYSTEMKEY SET LASTKEY = LASTKEY+#SeqUpdateCount
--If you have all the values you need in your temp table, you can now insert them into the ACTIVITYLOG table.
--INSERT INTO DOCSADM.ACTIVITY
--(SYSTEM_ID, DOCNUMBER, START_DATE, version, EXT,)
--SELECT
--AlogSysID,Docnum,GETUTCDATE(),BLAH, BLAH
--FROM #TableSet1
INSERT INTO DOCSADM.ACTIVITYLOG
(SYSTEM_ID,
DOCNUMBER,
START_DATE,
TYPIST,
AUTHOR,
ACTIVITY_DESC,
VERSION_LABEL,
ACTIVITY_TYPE)
SELECT
AlogsysID, Docnum,AlogDate,AlogTypist, AlogAuthor, ALOG_MATTER, '',115
FROM #TempSet1;
--Now you need to Drop the Temp Table
DROP TABLE #TempSet1
--Go to the other half of your path above to exit the trigger.
END
TriggerExit:
END
Go
but when I try to run any INSERT statement on this table I get this error message. It doesn't matter if the activity_type has a value of 115 or not
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I know the issue is with this section of the trigger:
INSERT INTO #TempSet1 (AlogsysID,Docnum,AlogDate,AlogTypist,AlogAuthor, ALOG_MATTER)
--SELECT  You SELECT STATEMENT WILL GO HERE MODIFIED TO POPULATE THE TABLE WITH THE DOCNUMBERS YOU WANT!!
SELECT TOP 1
System_id
, docnumber
, LAST_ACCESS_DATE
, TYPIST
, AUTHOR
, MATTER
FROM docsadm.PROFILE
WHERE EXISTS (SELECT CLIENT.SYSTEM_ID
FROM DOCSADM.CLIENT
INNER JOIN DOCSADM.MATTER
ON MATTER.CLIENT_ID = CLIENT.SYSTEM_ID
WHERE MATTER.SYSTEM_ID =#AlogDesc
OR INH_LUP_SEC_FROM IS NULL
OR INH_LUP_SEC_FROM = 0)
AND MATTER=#AlogDesc
It's the SELECT statement that is causing it to fail.
I know that this statement will bring back multiple rows but I only need the value from one of them so I can use this value for my INSERT. I though having the "select top 1" would do this for me but it's not working like I think it should. What am I missing?
If I had to guess I would say your problem is here:
DECLARE #AlogType int = (SELECT I.ACTIVITY_TYPE FROM DOCSADM.ACTIVITYLOG A, INSERTED I) --This is the value you are looking for regarding the DM client/Matter actitivty type.
DECLARE #AlogDesc varchar(32) = (Select i.ACTIVITY_DESC from docsadm.ACTIVITYLOG A, INSERTED I)
How are ACTIVITYLOG and INSERTED joined in the above ? without a where it would be a CROSS JOIN. Why do you even drag ACTIVITYLOG into it, you can simply use INSERTED. Also please try to stop using implicit joins ( I can see that later down the script you use the proper, more verbose join syntax)
TRY:
DECLARE #AlogType int = (SELECT I.ACTIVITY_TYPE FROM INSERTED I) --This is the value you are looking for regarding the DM client/Matter actitivty type.
DECLARE #AlogDesc varchar(32) = (Select i.ACTIVITY_DESC from INSERTED I)
Be careful that this will work with single inserts only. When you do batched inserts the INSERTED is a table containing multiple rows and you will run into issues again.

Trigger causes error (subquery return more than one value) on Bulk Insert

Alter Trigger [dbo].[DiscountUpdate]
on [dbo].[t_PromoDtl]
Instead of insert
as
begin
Declare #Barcode nvarchar(25);
Declare #disper decimal(18,0);
Declare #status int;
Declare #BranchID nvarchar(15);
set #Barcode = (Select barcodeFull from inserted); ---/// I think error happens in here.
set #disper = (Select disPer from inserted); ---/// I think error happens in here.
set #status = (Select p.status from inserted p); ---/// I think error happens in here.
begin
if #status = 2
begin
update t_Prd
set PrdDiscnt = #disper
where BarcodeFull = #Barcode;
end
else
begin
update t_Prd
set PrdDiscnt = 0
where BarcodeFull = #Barcode;
end
end
end
Here is my C# code..
using (var sqlBulk3 = new SqlBulkCopy(_connectionString, SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.CheckConstraints))
{
using (SqlConnection con6 = new SqlConnection(_connectionString))
{
con6.Open();
SqlCommand cmdtt = new SqlCommand("Truncate Table t_PromoDtl", con6);
cmdtt.CommandType = CommandType.Text;
cmdtt.ExecuteNonQuery();
con6.Close();
}
sqlBulk3.DestinationTableName = "t_PromoDtl";
sqlBulk3.WriteToServer(PromoDtl);
}
When Bulk insert starts, the trigger throws this error:
Sub query returns more than one value....
I looked at this trigger which updates t_Prd table instead of insert on t_PromoDtl table.
set #Barcode = (Select barcodeFull from inserted); ---/// I think error happens in here.
set #disper = (Select disPer from inserted); ---/// I think error happens in here.
set #status = (Select p.status from inserted p); ---/// I think error happens in here.
You seem to assume that the SQL Server trigger will be fired separately for each row - this is NOT the case - the trigger is fired only once for a statement. And if this is a BULK INSERT, then the Inserted pseudo table will contain multiple rows - so your statements like
set #Barcode = (Select barcodeFull from inserted);
are in fact the source of the problem - which one of the 250 rows inserted are you selecting here? It's not determined - you'll get back one arbitrary row - and what happens to the other 249 rows also inserted?? They're just plain ignored and not handled.
You need to rewrite your entire trigger logic to be set-based and handle the fact that the Inserted pseudo table will most likely contain multiple rows.
Try something like this:
ALTER TRIGGER [dbo].[DiscountUpdate]
ON [dbo].[t_PromoDtl]
INSTEAD OF INSERT
AS
BEGIN
-- update "dbo.T_Prd.PrdDiscnt" to "disPer" when status is 2
UPDATE p
SET PrdDiscnt = i.disPer
FROM dbo.T_Prd p
INNER JOIN Inserted i ON i.BarcodeFull = p.BarcodeFull
WHERE i.Status = 2;
-- update "dbo.T_Prd.PrdDiscnt" to "0" when status is not 2
UPDATE p
SET PrdDiscnt = 0
FROM dbo.T_Prd p
INNER JOIN Inserted i ON i.BarcodeFull = p.BarcodeFull
WHERE i.Status <> 2;
I'm assuming here that BarcodeFull is your primary key column that uniquely identifies each row in your table - if that's not the case, you might need to adapt the JOIN condition to match your situation.

SQL Trigger Inconsistently firing

I have a SQL Trigger on a table that works... most of the time. And I cannot figure out why sometimes the fields are NULL
The trigger works by Updateing the LastUpdateTime whenever something is modified in the field, and the InsertDatetime when first Created.
For some reason this only seems to work some times.
ALTER TRIGGER [dbo].[DateTriggerTheatreListHeaders]
ON [dbo].[TheatreListHeaders]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS(SELECT * FROM DELETED)
BEGIN
UPDATE ES
SET InsertDatetime = Getdate()
,LastUpdateDateTime = Getdate()
FROM TheatreListHeaders es
JOIN Inserted I ON es.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER
END
IF UPDATE(LastUpdateDateTime) OR UPDATE(InsertDatetime)
RETURN;
IF EXISTS (
SELECT
*
FROM
INSERTED I
JOIN
DELETED D
-- make sure to compare inserted with (same) deleted person
ON D.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER
)
BEGIN
UPDATE ES
SET InsertDatetime = ISNULL(es.Insertdatetime,Getdate())
,LastUpdateDateTime = Getdate()
FROM TheatreListHeaders es
JOIN Inserted I ON es.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER
END
END
A much simpler and efficient approach to do what you are trying to do, would be something like...
ALTER TRIGGER [dbo].[DateTriggerTheatreListHeaders]
ON [dbo].[TheatreListHeaders]
AFTER INSERT,UPDATE
AS
BEGIN
SET NOCOUNT ON;
--Determine if this is an INSERT OR UPDATE Action .
DECLARE #Action as char(1);
SET #Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED)
AND EXISTS(SELECT * FROM DELETED)
THEN 'U' -- Set Action to Updated.
WHEN EXISTS(SELECT * FROM INSERTED)
THEN 'I' -- Set Action to Insert.
END);
UPDATE ES
SET InsertDatetime = CASE WHEN #Action = 'U'
THEN ISNULL(es.Insertdatetime,Getdate())
ELSE Getdate()
END
,LastUpdateDateTime = Getdate()
FROM TheatreListHeaders es
JOIN Inserted I ON es.UNIQUETHEATRELISTNUMBER = I.UNIQUETHEATRELISTNUMBER;
END
"If update()" is poorly defined/implemented in sql server IMO. It does not do what is implied. The function only determines if the column was set by a value in the triggering statement. For an insert, every column is implicitly (if not explicitly) assigned a value. Therefore it is not useful in an insert trigger and difficult to use in a single trigger that supports both inserts and updates. Sometimes it is better to write separate triggers.
Are you aware of recursive triggers? An insert statement will execute your trigger which updates the same table. This causes the trigger to execute again, etc. Is the (database) recursive trigger option off (which is typical) or adjust your logic to support that?
What are your expectations for the insert/update/merge statements against this table? This goes back to your requirements. Is the trigger to ignore any attempt to set the datetime columns directly and set them within the trigger always?
And lastly, what exactly does "works sometimes" actually mean? Do you have a test case that reproduces your issue. If you don't, then you can't really "fix" the logic without a specific failure case. But the above comments should give you sufficient clues. To be honest, your logic seems to be overly complicated. I'll add that it also is logically flawed in the way that it set insertdatetime to getdate if the existing value is null during an update. IMO, it should reject any update that attempts to set the value to null because that is overwriting a fact that should never change. M.Ali has provided an example that is usable but includes the created timestamp problem. Below is an example that demonstrates a different path (assuming the recursive trigger option is off). It does not include the rejection logic - which you should consider. Notice the output of the merge execution carefully.
use tempdb;
set nocount on;
go
create table zork (id integer identity(1, 1) not null primary key,
descr varchar(20) not null default('zippy'),
created datetime null, modified datetime null);
go
create trigger zorktgr on zork for insert, update as
begin
declare #rc int = ##rowcount;
if #rc = 0 return;
set nocount on;
if update(created)
select 'created column updated', #rc as rc;
else
select 'created column NOT updated', #rc as rc;
if exists (select * from deleted) -- update :: do not rely on ##rowcount
update zork set modified = getdate()
where exists (select * from inserted as ins where ins.id = zork.id);
else
update zork set created = getdate(), modified = getdate()
where exists (select * from inserted as ins where ins.id = zork.id);
end;
go
insert zork default values;
select * from zork;
insert zork (descr) values ('bonk');
select * from zork;
update zork set created = null, descr = 'upd #1' where id = 1;
select * from zork;
update zork set descr = 'upd #2' where id = 1;
select * from zork;
waitfor delay '00:00:02';
merge zork as tgt
using (select 1 as id, 'zippity' as descr union all select 5, 'who me?') as src
on tgt.id = src.id
when matched then update set descr = src.descr
when not matched then insert (descr) values (src.descr)
;
select * from zork;
go
drop table zork;

Field level audit on SQL Server using Trigger

I am trying to write a trigger which would audit a table's every field - a row's old value and new value in a table. If any of the field has been modified, I need to save the fields old value and the new value along with field name in an audit table, as a new entry.
create trigger Trg_Institution_FieldAudit on Table1 AFTER UPDATE AS
DECLARE #OldName VARCHAR(30)
DECLARE #CurrentName VARCHAR(30)
DECLARE #OldId VARCHAR(30)
DECLARE #CurrentId VARCHAR(30)
DECLARE #modifiedBy VARCHAR(30)
If update(Name)
BEGIN
select #OldName = Name from deleted
select #CurrentName = Name from Inserted
select #OldId = ID from deleted
select #currentId = ID from Inserted
select #modifiedBy = modifiedBy from deleted
--INSERT statement for Name field alone
END;
This works fine for a small number of fields, but I have a lot of fields (more than 60), and I am not achieving the performance that is required, because of a lot of if conditions. Is there a better way of doing this? On top of this, there are concurrent updates that are happening to around 3 million records in this table, which makes a lot of things go wrong :(
EDIT: Only ONE row will get updated by an UPDATE statement
Oh my. Please avoid using a cursor whenever possible! You can easily use an insert statement with a select referencing the inserted and deleted tables. Below is a sample from one of my update triggers.
DECLARE #AuditTime DATETIME
SET #AuditTime = GetDate()
IF UPDATE([AccountManager])
INSERT INTO Audit.AuditHistory (AuditId, AuditDate, AuditTableName, EntityKey, AuditFieldName, OldValue, NewValue, FieldDisplayText, OldDisplayText, NewDisplayText, ModifiedBy)
SELECT NewId(),
#AuditTime,
'[tblOpportunity]',
cast(d.[GOTSID] AS varchar),
'[AccountManager]',
cast(d.[AccountManager] AS varchar(250)),
cast(i.[AccountManager] AS varchar(250)),
'Account Manager',
isnull(cast(d.[AccountManager] AS varchar(250)), ''),
isnull(cast(i.[AccountManager] AS varchar(250)), ''),
isnull(i.[ModifiedBy], '')
FROM deleted d
INNER JOIN inserted i ON d.GOTSID = i.GOTSID
WHERE d.[AccountManager] <> i.[AccountManager]
OR (d.[AccountManager] IS NOT NULL
AND i.AccountManager IS NULL)
OR (d.[AccountManager] IS NULL
AND i.AccountManager IS NOT NULL)
#marc_s is right, you have to re-construct your trigger and tables. here take example.
you need to put where condition in select #OldName = Name from deleted.
e.g.-
**
CREATE TRIGGER Trg_Institution_FieldAudit ON Table1 FOR UPDATE
AS
DECLARE #OldName VARCHAR(30)
DECLARE #CurrentName VARCHAR(30)
IF UPDATE (Name)
BEGIN
SET #OldName = Table1.Name FROM deleted
WHERE Table1.Name = deleted.Name;
SET #CurrentName = Table1.Name FROM inserted
WHERE Table1.Name = inserted.Name ;
--INSERT statement for old and new values.
END
GO**
After looking for an alternative for FOR EACH in SQL Server, I found that a CURSOR can be used. It serves the purpose, but need somebody to validate this.
CREATE TRIGGER Trg_Institution_FieldAudit_1 ON dbo.Institution FOR UPDATE as
-- DECLARE Variables
DECLARE institution_cursor CURSOR DYNAMIC FOR SELECT * FROM DELETED
OPEN institution_cursor FETCH NEXT FROM institution_cursor INTO -- #variables here
WHILE (##FETCH_STATUS = 0)
BEGIN
IF UPDATE(COL1)
BEGIN
INSERT INTO AuditTable VALUES (COL1, #prev, #next);
END;
FETCH NEXT FROM institution_cursor INTO -- #Variables here
END
CLOSE institution_cursor
DEALLOCATE institution_cursor

Trigger is re-triggered by internal select on same table

I'm having this very strange issue that took me hours just to discover. My code worked perfectly during test with static #Start and #End variables defined, but within the trigger, these are being updated unexpectedly and the cause is the line in the first block of code that inserts values into #tbl.
I suspect this must be because I am selecting values from the very same table the trigger is for. However, since this is only a select and not an update I would have never expected this behavior.
I need to accomplish the following: When a task is updated, I need to check all other tasks for the Chore to see if they are completed and if so, perform inner logic.
I would deeply appreciate some insight here. I've been stuck on this for days now. :(
CREATE TRIGGER ut_Task_Update_PC ON tblTasks
AFTER UPDATE
AS
--Global Vars
declare #Start int, #End int, #ChoreID int
set #ChoreID = (select ChoreID from inserted)
set #Start = (select PC from inserted) -- PC = PercentComplete
set #End = (select PC from deleted)
print('1A: Start = '+cast(#Start as varchar(12))+', End = '+cast(#End as varchar(12)))
--Logic
if (#Start=100) and (#End=100) begin
print('Do nothing: Case 1.')
end else if (#Start=100) and (#End<100) begin
print('Do nothing: Case 2.')
end else if (#Start>0) and (#End<100) begin
print('Do nothing: Case 3.')
end else begin
declare #tbl table (PC int)
print('2A: Start = '+cast(#Start as varchar(12))+', End = '+cast(#End as varchar(12)))
--PROBLEM HERE: inserting explicit value = ok; inserting from same table
-- referenced by trigger somehow triggers another update but only once.
--insert #tbl values (1)
insert #tbl select isnull(PC,0) as [PC] from tblTasks
where ChoreID = #ChoreID and IsCancelled = 0
print('2B: Start = '+cast(#Start as varchar(12))+', End = '+cast(#End as varchar(12)))
if not exists (select null from #tbl where PC = 0)
and (select count(*) from #tbl) > 0 begin --DESIRED CASE
--Never reached
end
end
If the problem is not immediate obvious, you might try the below code for testing.
--Testing: use this sample table and add the above trigger
-- I trimmed away a LOT of extraneous code, but this should
-- reflect the same problems I'm having
CREATE TABLE tblTask(
TaskID int IDENTITY(1,1) NOT NULL,
ChoreID int NOT NULL,
PC int,
IsCancelled bit
)

Resources