September 20, 2005 at 3:02 pm
I have too little experience writing Triggers and I need some direction.
I have a simple Function which multiplies a Dollar amount times a percentage [ dbo.DollarsUpdate ]. Basically, I know I can get the ID value I need from "inserted" during an Insert, but what about an Update?
Also, I have this sectioned out for an Insert and an Update of the particular field of interest [ Dollars ]. Is that the best way to handle this or should I just do a one fell swoop update regardless of which field is altered?
TIA.
------------------------------------------------------------------------------------
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[Dollars]') AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[Dollars]
GO
CREATE TRIGGER Dollars
ON dbo.TableName
AFTER INSERT, UPDATE
AS
DECLARE @ID integer,
@TextValue varchar(4),
@Fiscal_Year char(4)
-- IF INSERT( Dollars) -- Errors here...
BEGIN
SELECT @ID = (SELECT [ID] FROM inserted)
SELECT @TextValue = (SELECT TextValue FROM TableName WHERE [ID] = @ID)
SELECT @Fiscal_Year = (SELECT m.Fiscal_Year FROM AnotherTable m INNER JOIN TableName a ON( m.OtherID = a.OtherID) WHERE [ID] = @ID)
BEGIN TRANSACTION InsertDollars
UPDATE TableName SET
Dollars = dbo.DollarsUpdate( @ID, @TextValue, @Fiscal_Year)
WHERE [ID] = @ID
COMMIT TRANSACTION InsertDollars
END
IF UPDATE( Dollars)
BEGIN
SELECT @ID = (SELECT [ID] FROM inserted)
SELECT @TextValue = (SELECT TextValue FROM TableName WHERE [ID] = @ID)
SELECT @Fiscal_Year = (SELECT m.Fiscal_Year FROM AnotherTalbe m INNER JOIN TableName a ON( m.OtherID = a.OtherID) WHERE [ID] = @ID)
BEGIN TRANSACTION UpdateDollars
UPDATE TableName SET
Dollars = dbo.DollarsUpdate( @ID, @TextValue, @Fiscal_Year)
WHERE [ID] = @ID
COMMIT TRANSACTION UpdateDollars
END
I wasn't born stupid - I had to study.
September 20, 2005 at 3:19 pm
Hey Bull, how you doing .
Inserted contains the value when an insert is done and when an update is done (contains new values)
Deleted keeps the values of deleted rows and the old values of and update.
Those are TABLES. Join those tables back to the main table so that you take action only on modified rows, and update everything at once.
Also why are you not using calculated fields in this case??
September 20, 2005 at 3:26 pm
I have tried using both "inserted" and "deleted" for the update section of this Trigger and neither changes the column [Dollars].
I must be doing something wrong, but I cannot see what it might be... Nor am I sure how to test a Trigger.
(Not sure what you mean by "calculated fields". Dollar is a calculated field... :blush
I wasn't born stupid - I had to study.
September 20, 2005 at 3:27 pm
Farrell,
You are better off separating this into two triggers one for insert and one for update. IF UPDATE() is meant for UPDATE Triggers. The trigger should ALWAYS be coded to handle MULTIPLE rows.
Some examples:
CREATE TRIGGER Dollars
ON dbo.TableName
AFTER UPDATE
AS
if @@rowcount = 0 Return
SET NOCOUNT ON
IF UPDATE( Dollars)
BEGIN
UPDATE T SET
Dollars = dbo.DollarsUpdate( New.ID, T.TextValue, Other.Fiscal_Year)
FROM
TableName T
join
Inserted new on T.id = new.id
join
Deleted old on new.id = old.ID
join
AnotherTableName Other on Other.ID = new.id and Other.OtherID = T.OtherID --???
WHERE Old.Dollars <> New.Dollars -- If this column can be null you needto add two more checks
END
[Edited] : I just copied and pasted from the wrong Place
* Noel
September 20, 2005 at 3:30 pm
The "inserted" table is available on INSERT,UPDATE triggers
the Deleted table is available on UPDATE,DELETED triggers.
You need to make sure your triggers will handle dataSets. This trigger does not, it is structured to handle only single inserts. If you inserted multiple rows in a single insert (Insert, Select) your data will not be right. you say "So, I only insert 1 record at a time" this may be true, but some day you may not, and this trigger will screw U.
For the insert try an instead of for the insert.
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[tr_i_Dollars]') AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[tr_I_Dollars]
GO
CREATE TRIGGER tr_I_Dollars
ON dbo.TableName
INSTEAD OF INSERT
AS
INSERT INTO dbo.TableName (ID, TextValue, Dollars, ..., ... All other fields here)
Select ID, TextValue, dbo.DollarsUpdate( ID, TextValue, Fiscal_Year), ..., ...)
From INSERTED A
INNER JOIN AnotherTable m on m.OtherID = a.OtherID
GO
For updates does the dollars field actually get updated, or just the textual value?
Try this for update trigger.
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[tr_U_Dollars]') AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[tr_U_Dollars]
GO
CREATE TRIGGER tr_U_Dollars
ON dbo.TableName
AFTER UPDATE
AS
if UPDATE(TextValue)
Begin
UPDATE A
set Dollars = dbo.DollarsUpdate( ID, TextValue, Fiscal_Year)
From TableName a
INNER JOIN Inserted b on a.id = b.id
INNER JOIN AnotherTable m on m.OtherID = a.OtherID
end
GO
September 20, 2005 at 3:34 pm
Noeld,
Yet again, almost the same answer,
Yet you beat me to the punch.
had to rewrite mine, damn time outs.
September 20, 2005 at 3:35 pm
You trigger is coded in a complicated and procedural manner where a single statement that does the update whenever the dependent values change would be simplier.
Try this:
CREATE TRIGGER Dollars ON dbo.TableName AFTER UPDATE
AS
IF @@rowcount = 0 return
set nocount on
set xact_abort on
UPDATE TableName
SET Dollars = dbo.DollarsUpdate( inserted.Id, inserted.TextValue, AnotherTable.Fiscal_Year)
from AnotherTable
JOIN inserted
on AnotherTable.OtherID = inserted.OtherID
JOIN deleted
in deleted.id = inserted.id
wheredeleted.TextValue != inserted.TextValue
ORdeleted.OtherID != inserted.OtherID
-- handle change between null and not null
or (deleted.TextValue is null
and inserted.TextValue is not null)
or (deleted.TextValue is not null
and inserted.TextValue is null)
SQL = Scarcely Qualifies as a Language
September 20, 2005 at 3:36 pm
Yep Got Hit by timeouts more than once today too
* Noel
September 20, 2005 at 3:38 pm
the IF UPDATE() will speed up things in case where no action is needed
* Noel
September 20, 2005 at 4:04 pm
Thank you all! Unfortunately, it is not working when I do an Update, (have not tried the Insert yet). Please note: The actual column Updated is not Dollars, it is another $ column I have entitled Amount. I have tried to write this so none of our actual fields and table names are used, if I missed something and a column has its old name, please forgive me... (Also, I am not sure what is going with the TextValue IS/NOT NULL check...).
This is what I have re-written:
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[DollarsUpdate]')
AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[DollarsUpdate]
GO
CREATE TRIGGER DollarsUpdate
ON dbo.TableName
AFTER UPDATE
AS
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
IF UPDATE( Amount)
BEGIN
BEGIN TRANSACTION UpdateDollars
UPDATE TableName SET
Dollars = dbo.RMS_Dollars( New.[ID], TableName.TextValue, OtherTable.Fiscal_Year)
FROM TableName
INNER JOIN inserted New ON( TableName.[ID] = New.[ID])
INNER JOIN deleted Old ON( TableName.[ID] = Old.[ID])
INNER JOIN OtherTable ON( TableName.OtherID = OtherTable.OtherID)
WHERE Old.Dollars <> New.Dollars
AND( Old.TextValue IS NULL AND New.TextValue IS NOT NULL) -- handle change between null and not null
OR( Old.TextValue IS NOT NULL AND New.TextValue IS NULL)
COMMIT TRANSACTION UpdateDollars
END
------------------------------------------------------------------------------------------
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[DollarsInsert]')
AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[DollarsInsert]
GO
CREATE TRIGGER DollarsInsert
ON dbo.TableName
AFTER INSERT
AS
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
BEGIN
BEGIN TRANSACTION InsertDollars
UPDATE TableName SET
Dollars = dbo.RMS_Dollars( New.[ID], TableName.TextValue, OtherTable.Fiscal_Year)
FROM TableName
INNER JOIN inserted New ON( TableName.[ID] = New.[ID])
INNER JOIN deleted Old ON( TableName.[ID] = Old.[ID])
INNER JOIN OtherTable ON( TableName.OtherID = OtherTable.OtherID)
WHERE Old.Dollars <> New.Dollars
AND( Old.TextValue IS NULL AND New.TextValue IS NOT NULL) -- handle change between null and not null
OR( Old.TextValue IS NOT NULL AND New.TextValue IS NULL)
COMMIT TRANSACTION InsertDollars
END
I wasn't born stupid - I had to study.
September 20, 2005 at 4:38 pm
Unfortunatelly debugging triggers sucks.
First of all you do not need transactions in the trigger.
Transactions are implicit in triggers if trigger fails, the transaction will rollback including the original insert/update
I removed the stuff you had in the where clause, and your join to the deleted table, It adds confusion, and It could be part of why its not updating. Other than having issues in your function it should work. but of course I could be, and am often wrong.
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[DollarsUpdate]')
AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[DollarsUpdate]
GO
CREATE TRIGGER DollarsUpdate
ON dbo.TableName
AFTER UPDATE
AS
IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
IF UPDATE( Amount)
BEGIN
UPDATE TableName SET
Dollars = dbo.RMS_Dollars( New.[ID], TableName.TextValue, OtherTable.Fiscal_Year)
FROM TableName
INNER JOIN inserted New ON( TableName.[ID] = New.[ID])
INNER JOIN OtherTable ON( TableName.OtherID = OtherTable.OtherID)
-- For debugging purposes put a select statement to see if the results are evaluating correctly
-- Remove this if if it appears to be returning correctly
Select Dollars, dbo.RMS_Dollars( New.[ID], TableName.TextValue, OtherTable.Fiscal_Year) as new dollars
FROM TableName
INNER JOIN inserted New ON( TableName.[ID] = New.[ID])
INNER JOIN OtherTable ON( TableName.OtherID = OtherTable.OtherID)
END
September 20, 2005 at 5:47 pm
Farrell,
Besides What Ray Already mentioned about the wrapping Transacrtions command being unnecessary your search filter must be:
WHERE Old.Dollars <> New.Dollars
OR( Old.TextValue IS NULL AND New.TextValue IS NOT NULL) -- handle change between null and not null
OR( Old.TextValue IS NOT NULL AND New.TextValue IS NULL)
Cheers
* Noel
September 21, 2005 at 8:42 am
Alright. I've changed these as suggested. Note: I also removed the IF @@ROWCOUNT = 0 RETURN. And I changed the Update/Insert constraint to use [Amount] instead of Dollars ( WHERE Old.[Amount] <> New.[Amount] )
The Insert Trigger does not work. The Update Trigger uses the old Amount value to calculate the new Dollar value. (It is closer than before, but it obviously is not working AFTER the Update...).
TIA. (And thanks for keeping with me on this one...)
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[DollarsUpdate]')
AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[DollarsUpdate]
GO
CREATE TRIGGER DollarsUpdate
ON dbo.TableName
AFTER UPDATE
AS
--IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
IF UPDATE( [Amount])
BEGIN
UPDATE TableName SET
Dollars = dbo.Dollars( New.[ID], TableName.TextValue, OtherTable.Fiscal_Year)
FROM TableName
INNER JOIN inserted New ON( TableName.[ID] = New.[ID])
INNER JOIN deleted Old ON( TableName.[ID] = Old.[ID])
INNER JOIN OtherTable ON( TableName.appl_id = OtherTable.appl_id)
WHERE Old.[Amount] <> New.[Amount]
END
------------------------------------------------------------------------------------------
IF EXISTS( SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[DollarsInsert]')
AND OBJECTPROPERTY(id, N'IsTrigger') = 1)
DROP TRIGGER [dbo].[DollarsInsert]
GO
CREATE TRIGGER DollarsInsert
ON dbo.TableName
AFTER INSERT
AS
--IF @@ROWCOUNT = 0 RETURN
SET NOCOUNT ON
BEGIN
UPDATE TableName SET
Dollars = dbo.Dollars( New.[ID], TableName.TextValue, OtherTable.Fiscal_Year)
FROM TableName
INNER JOIN inserted New ON( TableName.[ID] = New.[ID])
INNER JOIN OtherTable ON( TableName.appl_id = OtherTable.appl_id)
WHERE TableName.[Amount] <> New.[Amount]
END
------------------------------------RESULTS----------------------------------------------
SELECT a.[ID], a.[Amount], a.Dollars, a.TextValue, m.Fiscal_Year
FROM TableName a
INNER JOIN OtherTable m ON( a.OtherID = m.OtherID)
WHERE a.[id] = 714199
/* [ID] [Amount] Dollars TextValue Fiscal_Year
714199 252,960.00 0 Value 2004
749404 259,352.00 0 Value 2005 */
UPDATE TableName SET
Dollars = 100
WHERE [ID] = 714199
SELECT a.[ID], a.[Amount], a.Dollars, a.TextValue, m.Fiscal_Year
FROM TableName a
INNER JOIN OtherTable m ON( a.OtherID = m.OtherID)
WHERE a.[id] = 714199
/* [ID] [Amount] Dollars TextValue Fiscal_Year
714199 100.00 11,624.00 Value 2004
749404 259,352.00 0 Value 2005 */
I wasn't born stupid - I had to study.
September 21, 2005 at 9:05 am
On the update trigger you are using IF UPDATE([Amount]):
But this statement
UPDATE TableName SET
Dollars = 100
WHERE [ID] = 714199
Updates Dollar not Amount therefore nothing happens
On the insert Trigger just remove the WHERE Clause!!
* Noel
September 21, 2005 at 9:14 am
The Update works perfectly. (I originally wrote the function using the Original table name we will inevitbably use this function upon, then I made a test table with another name. When I changed that in the function, the Update trigger works perfectly. The function uses [Amount] and a percentage from another table to calculate the Dollars value).
I will drop the WHERE clause in the Insert Trigger and see what that gets me.
Thanks!!
I wasn't born stupid - I had to study.
Viewing 15 posts - 1 through 15 (of 15 total)
You must be logged in to reply to this topic. Login to reply