Trigger: Looping though multiple rows in the deleted table?

  • Hi,

    I'm trying to create a trigger that will delete a row from a second table once i delete a row from the first table, I have this code,

    CREATE TRIGGER On_Delete

    ON tblActivityArchive

    AFTER DELETE

    AS

    BEGIN

    DECLARE @v_id9 nvarchar(50)

    SET @v_id9 = (SELECT ActivityRef FROM deleted)

    DELETE FROM tblJobLinesArchive

    WHERE ActivityRef = @v_id9

    END

    This trigger works great at deleting the row from the second table but when multiple rows are deleted

    it does not work?

    Any ideas how to handle more than 1 row deletes?

    Thanks 🙂

    Andy

  • Yes... SQL Server Triggers are different than a lot of other RDBMS. In Oracle, for instance, you would do just as you have done except your would add "FOR EACH ROW" and the RBAR you wrote would work just fine.

    In SQL Server, the INSERTED and DELETED objects are tables that contain EVERYTHING that was INSERTED or DELETED and you must write the code in a fashion to handle ALL rows instead of one at a time.

    CREATE TRIGGER On_Delete

    ON tblActivityArchive

    AFTER DELETE

    AS

    BEGIN

    DELETE dbo.tblJobLinesArchive

    FROM dbo.tblJobLinesArchive a

    INNER JOIN Deleted d

    ON a.ActivityRef = d.ActivityRef

    END

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Hi,

    Thanks for the quick reply, that looks great!!

    Except, when i try run parse it as a query I get

    this error.

    Msg 156, Level 15, State 1, Procedure On_Delete, Line 9

    Incorrect syntax near the keyword 'WHERE'.

    But the code looks perfect so i'm at a little bit of a loss

    as to why this is a problem (Can you tell i'm still new to

    this 🙂

    Thanks!!

    Andy

  • The WHERE should be an ON

    CREATE TRIGGER On_Delete

    ON tblActivityArchive

    AFTER DELETE

    AS

    BEGIN

    DELETE dbo.tblJobLinesArchive

    FROM dbo.tblJobLinesArchive a

    INNER JOIN Deleted d ON a.ActivityRef = d.ActivityRef

    END

    Gail Shaw
    Microsoft Certified Master: SQL Server, MVP, M.Sc (Comp Sci)
    SQL In The Wild: Discussions on DB performance with occasional diversions into recoverability

    We walk in the dark places no others will enter
    We stand on the bridge and no one may pass
  • Brilliant, Thanks alot for your help guys 😀

    I spent ages trying to figure this out!!

  • It looks like you're happy, so just to mention in passing that there are circumstances where 'ON DELETE CASCADE' would be an alternative. Here's an example...

    --Structure

    CREATE TABLE t1 (Id INT PRIMARY KEY)

    CREATE TABLE t2 (Id INT PRIMARY KEY, t1Id INT)

    ALTER TABLE t2 ADD CONSTRAINT FK_t2_t1Id__t1_Id FOREIGN KEY (t1Id) REFERENCES t1(Id) ON DELETE CASCADE

    --Data

    INSERT t1 SELECT 1 UNION SELECT 2

    INSERT t2 SELECT 1, 1 UNION SELECT 2, 1 UNION SELECT 3, 2

    SELECT * FROM t1

    SELECT * FROM t2

    /*

    Id

    -----------

    1

    2

    Id t1Id

    ----------- -----------

    1 1

    2 1

    3 2

    */

    --Delete

    DELETE FROM t1 WHERE Id = 1

    --Results

    SELECT * FROM t1

    SELECT * FROM t2

    /*

    Id

    -----------

    2

    Id t1Id

    ----------- -----------

    3 2

    */

    Ryan Randall

    Solutions are easy. Understanding the problem, now, that's the hard part.

  • GilaMonster (4/14/2008)


    The WHERE should be an ON

    CREATE TRIGGER On_Delete

    ON tblActivityArchive

    AFTER DELETE

    AS

    BEGIN

    DELETE dbo.tblJobLinesArchive

    FROM dbo.tblJobLinesArchive a

    INNER JOIN Deleted d ON a.ActivityRef = d.ActivityRef

    END

    Agreed... coffee deprivation... I've fixed my post.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • How do you get around Delete Cascade when there are more that 2 tables involved without a trigger? is there a way in 2008?

    Thanks

  • Lloyd Harrison (9/26/2008)


    How do you get around Delete Cascade when there are more that 2 tables involved without a trigger? is there a way in 2008?

    Thanks

    Like this:

    create table DTest1 (

    ID int identity primary key);

    go

    create table DTest2 (

    ID int identity primary key,

    T1ID int references dbo.dtest1(id) on delete cascade); -- First Table

    go

    create table DTest3 (

    ID int identity primary key,

    T2ID int references dbo.dtest2(id) on delete cascade); -- Chain from 2nd table

    go

    create table DTest4 (

    ID int identity primary key,

    T1ID int references dbo.dtest1(id) on delete cascade); -- First table

    go

    -- Populate for test

    insert into dtest1

    default values

    go 10

    insert into dtest2

    select id

    from dtest1;

    insert into dtest3

    select id

    from dtest2;

    insert into dtest4

    select id

    from dtest1;

    -- Actual test

    select *

    from dtest1

    where id = 1;

    select *

    from dtest2

    where t1id = 1;

    select *

    from dtest3

    where t2id = 1;

    select *

    from dtest4

    where t1id = 1;

    delete from dtest1 -- Chain delete

    where id = 1;

    select *

    from dtest1

    where id = 1;

    select *

    from dtest2

    where t1id = 1;

    select *

    from dtest3

    where t2id = 1;

    select *

    from dtest4

    where t1id = 1;

    Is that what you mean?

    - Gus "GSquared", RSVP, OODA, MAP, NMVP, FAQ, SAT, SQL, DNA, RNA, UOI, IOU, AM, PM, AD, BC, BCE, USA, UN, CF, ROFL, LOL, ETC
    Property of The Thread

    "Nobody knows the age of the human race, but everyone agrees it's old enough to know better." - Anon

  • Yes, and thats really wired because I trued that, well sort of.

    I tried the following:

    alter table my_table2

    add constraint fk_my_table2

    foreign key (fk_id2)

    references my_table1(pk_id)

    on delete cascade

    This worked, then tried this

    alter table my_table3

    add constraint fk_my_table3

    foreign key (fk_id3)

    references my_table1(pk_id)

    on delete cascade

    and got the following error:

    Msg 1785, Level 16, State 0, Line 1

    Introducing FOREIGN KEY constraint 'fk_my_table3'

    on table 'my_table3' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

    Msg 1750, Level 16, State 0, Line 1

    Could not create constraint. See previous errors.

    But when I tried your sample code, it worked...bizarre!!!

  • That's because the chain of deletes points to the same table more than once IE :

    A > B > C > A

    If any letter comes in the delete sequence more than once, the server will throw that error. That is also why you can't use on delete cascade when using a self-reference FK (when the FK points on the same table).

    I have a table like this :

    ClientID

    ClientID_BillTo

    If I need to delete a client, I have to manually delete the ClientID_BillTo in the same delete command... same thing goes for updates.

  • Many-to-many reference tables have a similar problem. Can't chain delete from both parents into those. Have to use triggers for that kind of thing.

    - Gus "GSquared", RSVP, OODA, MAP, NMVP, FAQ, SAT, SQL, DNA, RNA, UOI, IOU, AM, PM, AD, BC, BCE, USA, UN, CF, ROFL, LOL, ETC
    Property of The Thread

    "Nobody knows the age of the human race, but everyone agrees it's old enough to know better." - Anon

  • Wait, I didnt think my chains overlapped.

    Here:

    ZIP>SCHOOL

    ZIP>DISTRICT

    My district tabel and school table have a reference to zip.

    If I delete ZIP I want to delete both the school and district

    So each table, school and district, would have a cascade delete in it that references the zip table.

    No?

  • Lloyd Harrison (9/26/2008)


    Wait, I didnt think my chains overlapped.

    Here:

    ZIP>SCHOOL

    ZIP>DISTRICT

    My district tabel and school table have a reference to zip.

    If I delete ZIP I want to delete both the school and district

    So each table, school and district, would have a cascade delete in it that references the zip table.

    No?

    That should work. Are you sure you don't have any other fk constraints laying around?

    Can we see the full ddl scripts for all the tables in question.

    P.S. 1 way to figure out if any other tables are involved is to create a new database diagram, add those 3 tables with the option to include all related tables to 2-3 levels. You'll see any other possible objects there.

    Another gotcha is that you can have "test" fks that you forgot you have (happens once in a while in dev environements).

  • ZIP is obviously a reference table. Someday, you may want to delete a ZIP with no impact on other tables. Cascading deletes is a pretty bad idea to begin with because deletes, by themselves, can get you into pretty big trouble especially if you have SOX or SEC to contend with. I realize a school program has no such impact, but you can still get into a heap of trouble with deletes... those problems can be magnified quite a bit by the use of cascading deletes. My personal feeling is that one business rule that shouldn't be enforced by FK's is that of cascading deletes.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

Viewing 15 posts - 1 through 15 (of 18 total)

You must be logged in to reply to this topic. Login to reply