How does Sql determine cascade order?

  • I have the following scenario

    • Table A
    • Table B

    • fk Table A - non nullable
  • Table C
    • fk Table A - non nullable
    • fk Table B - nullable

    The following hierarchy of records can be created

    • Table A

    • Table B
    • Table C
    • Table B

    • Table C

    If record in Table C is tied to Table B, the fk's to Table A will be the same.

    So what I had planned on doing was a cascade from Table A to Table B And Table A to Table C.  This will only work however if Table C is the first table to cascade.

    Can anybody 1) enlighten me on how the order is determined, 2) Give suggestions on how to handle this.  (Note: we dont use triggers in our system)

     

     

  • You cannot set on casade delete for all those relations (circular relations) >>

    c to b = fine

    b to a = fine

    c to a >> cascades on b, which cascades on c which cascasdes back to a.

     

    You'll either have to enforce all deletes through stored procs or start using triggers.

    May I ask why your organisation has decided to not use triggers at all?

  • Here's a script to show you the error... and give you something to toy with :

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoB_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)
    
    ALTER TABLE [dbo].[DemoB] DROP CONSTRAINT FK_DemoB_DemoA
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)
    
    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoA
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoB]') AND OBJECTPROPERTY(idN'IsForeignKey'1)
    
    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoB
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoC]') AND OBJECTPROPERTY(idN'IsUserTable'1)
    
    DROP TABLE [dbo].[DemoC]
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoB]') AND OBJECTPROPERTY(idN'IsUserTable'1)
    
    DROP TABLE [dbo].[DemoB]
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoA]') AND OBJECTPROPERTY(idN'IsUserTable'1)
    
    DROP TABLE [dbo].[DemoA]
    
    GO
    
    
    
    CREATE TABLE [dbo].[DemoA] (
    
           [IDa] [char] (10COLLATE French_CI_AS NOT NULL 
    
    ) ON [PRIMARY]
    
    GO
    
    
    
    CREATE TABLE [dbo].[DemoB] (
    
           [IDb] [char] (10COLLATE French_CI_AS NOT NULL ,
    
           [IDa] [char] (10COLLATE French_CI_AS NOT NULL 
    
    ) ON [PRIMARY]
    
    GO
    
    
    
    CREATE TABLE [dbo].[DemoC] (
    
           [IDc] [char] (10COLLATE French_CI_AS NOT NULL ,
    
           [IDa] [char] (10COLLATE French_CI_AS NOT NULL ,
    
           [IDb] [char] (10COLLATE French_CI_AS NULL 
    
    ) ON [PRIMARY]
    
    GO
    
    
    
    ALTER TABLE [dbo].[DemoA] WITH NOCHECK ADD 
    
           CONSTRAINT [PK_DemoA] PRIMARY KEY  CLUSTERED 
    
           (
    
                   [IDa]
    
           )  ON [PRIMARY] 
    
    GO
    
    
    
    ALTER TABLE [dbo].[DemoB] WITH NOCHECK ADD 
    
           CONSTRAINT [PK_DemoB] PRIMARY KEY  CLUSTERED 
    
           (
    
                   [IDb]
    
           )  ON [PRIMARY] 
    
    GO
    
    
    
    ALTER TABLE [dbo].[DemoC] WITH NOCHECK ADD 
    
           CONSTRAINT [PK_DemoC] PRIMARY KEY  CLUSTERED 
    
           (
    
                   [IDc]
    
           )  ON [PRIMARY] 
    
    GO
    
    
    
    ALTER TABLE [dbo].[DemoB] ADD 
    
           CONSTRAINT [FK_DemoB_DemoA] FOREIGN KEY 
    
           (
    
                   [IDa]
    
           REFERENCES [dbo].[DemoA] (
    
                   [IDa]
    
           ON DELETE CASCADE 
    
    GO
    
    
    
    ALTER TABLE [dbo].[DemoC] ADD 
    
           CONSTRAINT [FK_DemoC_DemoA] FOREIGN KEY 
    
           (
    
                   [IDa]
    
           REFERENCES [dbo].[DemoA] (
    
                   [IDa]
    
           ),
    
           CONSTRAINT [FK_DemoC_DemoB] FOREIGN KEY 
    
           (
    
                   [IDb]
    
           REFERENCES [dbo].[DemoB] (
    
                   [IDb]
    
           ON DELETE CASCADE 
    
    GO
    
    --EVERYTHING is fine untill you alter this foreign key to cascade on deletes : 
    
    ALTER TABLE dbo.DemoC
    
           DROP CONSTRAINT FK_DemoC_DemoA
    
    GO
    
    ALTER TABLE dbo.DemoC WITH NOCHECK ADD CONSTRAINT
    
           FK_DemoC_DemoA FOREIGN KEY
    
           (
    
           IDa
    
           REFERENCES dbo.DemoA
    
           (
    
           IDa
    
           ON DELETE CASCADE
    
    
    
    /*
    
    Serveur : Msg 1785, Niveau 16, État 1, Ligne 1
    
    Introducing FOREIGN KEY constraint 'FK_DemoC_DemoA' on table 'DemoC' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
    
    Serveur : Msg 1750, Niveau 16, État 1, Ligne 1
    
    Could not create constraint. See previous errors.
    
    */
    
    --clean up
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoB_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)
    
    ALTER TABLE [dbo].[DemoB] DROP CONSTRAINT FK_DemoB_DemoA
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)
    
    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoA
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoB]') AND OBJECTPROPERTY(idN'IsForeignKey'1)
    
    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoB
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoC]') AND OBJECTPROPERTY(idN'IsUserTable'1)
    
    DROP TABLE [dbo].[DemoC]
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoB]') AND OBJECTPROPERTY(idN'IsUserTable'1)
    
    DROP TABLE [dbo].[DemoB]
    
    GO
    
    
    
    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoA]') AND OBJECTPROPERTY(idN'IsUserTable'1)
    
    DROP TABLE [dbo].[DemoA]
    
    GO
  • I currently have it setup as cascades A -> B and A -> C and restrict B -> C

    So when deleting from A, if i am know that sql will always delete from C first and then B, I wont need to wory about the relationship from B -> C.

    Scenario

    • Table A

    • Record 1
  • Table B
    • Record 1

    • Table A ID = record 1
  • Table C
    • Record 1

    • Table A ID = record 1
  • Record 2
    • Table A ID = record 1
    • Table B ID = record 1

    If I delete Table A - record 1, and it cascades to Table C record 1 and record 2 and then Table B record 1 everything is fine.

    All Table C records pointing to Table B will be gone prior to to Table B deletes.

    However if Table B is the first to cascade, the Restriction on the Table C record will kick in and not allow this.

     

    I am not sure why the decision was made to stay away from triggers.

  • Looks like you'll be ok. The delete doesn't fail (the previous err message was most likely to allow this type of operation not to fail) :

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoB_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)

    ALTER TABLE [dbo].[DemoB] DROP CONSTRAINT FK_DemoB_DemoA

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)

    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoA

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoB]') AND OBJECTPROPERTY(idN'IsForeignKey'1)

    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoB

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoC]') AND OBJECTPROPERTY(idN'IsUserTable'1)

    DROP TABLE [dbo].[DemoC]

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoB]') AND OBJECTPROPERTY(idN'IsUserTable'1)

    DROP TABLE [dbo].[DemoB]

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoA]') AND OBJECTPROPERTY(idN'IsUserTable'1)

    DROP TABLE [dbo].[DemoA]

    GO

    CREATE TABLE [dbo].[DemoA] (

           [IDa] [char] (10COLLATE French_CI_AS NOT NULL 

    ON [PRIMARY]

    GO

    CREATE TABLE [dbo].[DemoB] (

           [IDb] [char] (10COLLATE French_CI_AS NOT NULL ,

           [IDa] [char] (10COLLATE French_CI_AS NOT NULL 

    ON [PRIMARY]

    GO

    CREATE TABLE [dbo].[DemoC] (

           [IDc] [char] (10COLLATE French_CI_AS NOT NULL ,

           [IDa] [char] (10COLLATE French_CI_AS NOT NULL ,

           [IDb] [char] (10COLLATE French_CI_AS NULL 

    ON [PRIMARY]

    GO

    ALTER TABLE [dbo].[DemoA] WITH NOCHECK ADD 

           CONSTRAINT [PK_DemoA] PRIMARY KEY  CLUSTERED 

           (

                   [IDa]

           )  ON [PRIMARY] 

    GO

    ALTER TABLE [dbo].[DemoB] WITH NOCHECK ADD 

           CONSTRAINT [PK_DemoB] PRIMARY KEY  CLUSTERED 

           (

                   [IDb]

           )  ON [PRIMARY] 

    GO

    ALTER TABLE [dbo].[DemoC] WITH NOCHECK ADD 

           CONSTRAINT [PK_DemoC] PRIMARY KEY  CLUSTERED 

           (

                   [IDc]

           )  ON [PRIMARY] 

    GO

    ALTER TABLE [dbo].[DemoB] ADD 

           CONSTRAINT [FK_DemoB_DemoA] FOREIGN KEY 

           (

                   [IDa]

           REFERENCES [dbo].[DemoA] (

                   [IDa]

           ON DELETE CASCADE 

    GO

    ALTER TABLE [dbo].[DemoC] ADD 

           CONSTRAINT [FK_DemoC_DemoA] FOREIGN KEY 

           (

                   [IDa]

           REFERENCES [dbo].[DemoA] (

                   [IDa]

           ON DELETE CASCADE,

           CONSTRAINT [FK_DemoC_DemoB] FOREIGN KEY 

           (

                   [IDb]

           REFERENCES [dbo].[DemoB] (

                   [IDb]

           )  

    GO

    INSERT INTO dbo.DemoA (IDaVALUES ('a')

    INSERT INTO dbo.DemoB (IDaIDbVALUES ('a''b')

    INSERT INTO dbo.DemoC (IDaIDbIDcVALUES ('a''b''c')

    SELECT FROM DemoA

    SELECT FROM DemoB

    SELECT FROM DemoC

    DELETE FROM dbo.DemoA

    SELECT FROM DemoA

    SELECT FROM DemoB

    SELECT FROM DemoC

    --clean up

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoB_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)

    ALTER TABLE [dbo].[DemoB] DROP CONSTRAINT FK_DemoB_DemoA

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoA]') AND OBJECTPROPERTY(idN'IsForeignKey'1)

    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoA

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[FK_DemoC_DemoB]') AND OBJECTPROPERTY(idN'IsForeignKey'1)

    ALTER TABLE [dbo].[DemoC] DROP CONSTRAINT FK_DemoC_DemoB

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoC]') AND OBJECTPROPERTY(idN'IsUserTable'1)

    DROP TABLE [dbo].[DemoC]

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoB]') AND OBJECTPROPERTY(idN'IsUserTable'1)

    DROP TABLE [dbo].[DemoB]

    GO

    IF EXISTS (SELECT FROM dbo.sysobjects WHERE id OBJECT_ID(N'[dbo].[DemoA]') AND OBJECTPROPERTY(idN'IsUserTable'1)

    DROP TABLE [dbo].[DemoA]

    GO

  • Viewing 5 posts - 1 through 5 (of 5 total)

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