복붙노트

[SQL] 외래 키 제약은 사이클 또는 여러 개의 캐스케이드 경로의 원인?

SQL

외래 키 제약은 사이클 또는 여러 개의 캐스케이드 경로의 원인?

내 테이블에 제약 조건을 추가하려고 할 때 문제가 있습니다. 나는 오류가 발생합니다 :

내 제약은 코드 테이블과 직원 테이블 사이에있다. 코드 테이블은 ID, 이름,의 FriendlyName, 유형 및 값이 포함되어 있습니다. 직원은 기준 코드 때문에 코드의 각 유형에 대한 참조가있을 수 있다는 것이 다수의 필드를 갖는다.

참조 코드가 삭제 된 경우 필드가 null로 설정 될 때까지 나는이 필요합니다.

모든 아이디어를 어떻게하면 할 수 있습니까?

해결법

  1. ==============================

    1.SQL 서버가 아니라 어떤주기가 실제로 존재 여부를 해결하려고 노력보다, 캐스케이드 경로의 간단한 계산을 수행하고, 그것은 최악의 가정 및 참조 동작 (CASCADE)을 만들 거부 : 당신은 여전히 ​​참조 동작하지 않고 제약 조건을 작성해야 할 수 있습니다. 당신이 (또는 그렇게하면 물건을 손상 것) 설계를 변경할 수 없습니다 경우에 당신은 최후의 수단으로 트리거를 사용하는 것이 좋습니다.

    SQL 서버가 아니라 어떤주기가 실제로 존재 여부를 해결하려고 노력보다, 캐스케이드 경로의 간단한 계산을 수행하고, 그것은 최악의 가정 및 참조 동작 (CASCADE)을 만들 거부 : 당신은 여전히 ​​참조 동작하지 않고 제약 조건을 작성해야 할 수 있습니다. 당신이 (또는 그렇게하면 물건을 손상 것) 설계를 변경할 수 없습니다 경우에 당신은 최후의 수단으로 트리거를 사용하는 것이 좋습니다.

    캐스케이드 경로를 해결 FWIW은 복잡한 문제이다. 다른 SQL 제품은 단순히 아마 디자이너 (예를 들어, ACE / 제트가이 작업을 수행)의 무지에 문제를 무시하고 당신이 경주 마지막으로 값을 덮어 쓸 것이다 볼 수있을 것입니다 경우에 사이클을 만들 수 있습니다. 좀 SQL 제품은 해결의 간단한 경우에 시도 할 것 이해합니다. 사실 남아, SQL 서버는 시도조차하지 않는 이상 하나 개의 경로를 허용하지 않음으로 그것을 매우 안전을 재생하고 적어도 그렇게 알려줍니다.

    마이크로 소프트 자체는 대신 FK 제약의 트리거의 사용을 권장합니다.

  2. ==============================

    2.여러 cascasing 경로와 일반적인 상황이 될 것입니다 : 이 개 세부 마스터 테이블은,의는 "마스터"와 "Detail1"와 "Detail2"을 가정 해 봅시다. 모두 세부 사항은 계단식 삭제합니다. 지금까지 아무런 문제. 그러나 모두 세부 사항은 다른 테이블과 일대-관계가 있다면 무엇을 ( "SomeOtherTable"라고). SomeOtherTable는 Detail1ID 열 및 Detail2ID 열을 갖는다.

    여러 cascasing 경로와 일반적인 상황이 될 것입니다 : 이 개 세부 마스터 테이블은,의는 "마스터"와 "Detail1"와 "Detail2"을 가정 해 봅시다. 모두 세부 사항은 계단식 삭제합니다. 지금까지 아무런 문제. 그러나 모두 세부 사항은 다른 테이블과 일대-관계가 있다면 무엇을 ( "SomeOtherTable"라고). SomeOtherTable는 Detail1ID 열 및 Detail2ID 열을 갖는다.

    Master { ID, masterfields }
    
    Detail1 { ID, MasterID, detail1fields }
    
    Detail2 { ID, MasterID, detail2fields }
    
    SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields }
    

    즉 : SomeOtherTable의 레코드 중 일부는 Detail1 - 기록과 연결되어 SomeOtherTable의 레코드 중 일부는 Detail2 기록과 연결되어 있습니다. 결코 모두 세부 사항에 속하는 SomeOtherTable-기록, SomeOtherTable에 마스터에서 여러 계단식 경로 (Detail1를 통해 하나 Detail2를 통해 하나)가 있기 때문에 SomeOhterTable의 기록이 모두 자세한 내용은 삭제 계단식으로 만들기 위해 지금 불가능하다고 보장 경우에도 마찬가지입니다. 지금 당신은 이미이를 이해 할 수있다. 여기 가능한 해결책은 :

    Master { ID, masterfields }
    
    DetailMain { ID, MasterID }
    
    Detail1 { DetailMainID, detail1fields }
    
    Detail2 { DetailMainID, detail2fields }
    
    SomeOtherTable {ID, DetailMainID, someothertablefields }
    

    모든 ID 필드는 키 필드와 자동 증가합니다. DetailMainId의 핵심 거짓말은 상세 테이블의 필드. 이 필드는 모두 키와 참조 contraint 있습니다. 오직 마스터 기록을 삭제하여 삭제 모든 계단식 할 수있게되었습니다. 단점은 각 detail1 - 레코드와 각 detail2 레코드에 대해,도 (실제로는 정확하고 고유 ID를 얻기 위해 처음 만들어 질)을 DetailMain-기록이 있어야한다는 것입니다.

  3. ==============================

    3.나는 (기능적) 스키마와 데이터의주기 및 / 또는 여러 경로 사이에 큰 차이가 있음을 지적한다. 사이클과 아마도 데이터의 다중 확실히 복잡 수 처리 및 원인 성능 문제 ( "제대로"처리 비용), 스키마의 이러한 특성의 비용은 제로에 가까워 야하지만.

    나는 (기능적) 스키마와 데이터의주기 및 / 또는 여러 경로 사이에 큰 차이가 있음을 지적한다. 사이클과 아마도 데이터의 다중 확실히 복잡 수 처리 및 원인 성능 문제 ( "제대로"처리 비용), 스키마의 이러한 특성의 비용은 제로에 가까워 야하지만.

    RDB에 가장 명백한 사이클 계층 구조에서 (조직 차트, 부품, 서브 파트 등) 발생 이후로는 SQL Server가 최악의 가정 것은 불행한 일이다; 즉 스키마 사이클 == 데이터 사이클. 당신이 RI 제한 조건을 사용하는 경우 사실, 당신은 실제로 데이터의주기를 빌드 할 수 없습니다!

    나는 다중 경로 문제와 유사하다 생각; 스키마 즉, 여러 경로는 반드시 데이터에 여러 경로를 의미하지는 않습니다,하지만 난 다중 경로 문제를 덜 경험이 있습니다.

    SQL 서버는 아직 (32)의 깊이 대상이 될 것 사이클을 허용 않았다,하지만 아마 대부분의 경우에 적합하다면 물론. (그러나 설정 데이터베이스 아니다 너무의 나쁜!)

    "대신 삭제의"트리거 중 하나가 작동하지 않습니다. 테이블이 방문 두 번째 시간, 트리거는 무시됩니다. 당신이 정말 폭포를 시뮬레이션하려는 경우, 당신은 사이클의 존재에 저장 프로 시저를 사용해야합니다. 대신 -의 - 삭제 - 트리거 그러나 다중의 경우에 작동합니다.

    Celko 사이클을 도입하지 않는 계층을 대표하는 "더 나은"방법을 제시하지만, 장단점이있다.

  4. ==============================

    4.트리거를 사용하여 여러 삭제 경로를 수행하는 방법을 설명하는 글 로모 있습니다. 어쩌면이 복잡한 시나리오에 유용합니다.

    트리거를 사용하여 여러 삭제 경로를 수행하는 방법을 설명하는 글 로모 있습니다. 어쩌면이 복잡한 시나리오에 유용합니다.

    http://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/

  5. ==============================

    5.그것의 소리에 의해 당신은 당신의 코드 테이블을 수정합니다 기존의 외래 키 중 하나에 OnDelete /의 OnUpdate 조치를해야합니다.

    그것의 소리에 의해 당신은 당신의 코드 테이블을 수정합니다 기존의 외래 키 중 하나에 OnDelete /의 OnUpdate 조치를해야합니다.

    이 외부 키를 생성하여, 당신은 순환 문제를 만드는 거라고 그래서,

    예를 들면 종업원 업데이트, 등 ..., 켬 업데이트 작업에 의해 변경에 코드가 발생 종업원이에 업데이트 작업에 의해 변경됩니다 ...

    당신이 두 테이블, 당신의 외래 키 / 제약 조건 정의를 테이블 정의를 게시 할 경우 우리는 문제가 어디 있는지를 알 수있을 것입니다 ...

  6. ==============================

    6.직원은 다른 엔티티 말 자격의 컬렉션을 가질 수와 자격이 다른 수집 대학교가있을 수 있기 때문입니다 예를 들면

    직원은 다른 엔티티 말 자격의 컬렉션을 가질 수와 자격이 다른 수집 대학교가있을 수 있기 때문입니다 예를 들면

    public class Employee{
    public virtual ICollection<Qualification> Qualifications {get;set;}
    

    }

    public class Qualification{
    
    public Employee Employee {get;set;}
    
    public virtual ICollection<University> Universities {get;set;}
    

    }

    public class University{
    
    public Qualification Qualification {get;set;}
    

    }

    의 DataContext에 그것은 아래와 같이 될 수있다

    protected override void OnModelCreating(DbModelBuilder modelBuilder){
    
    modelBuilder.Entity<Qualification>().HasRequired(x=> x.Employee).WithMany(e => e.Qualifications);
    modelBuilder.Entity<University>.HasRequired(x => x.Qualification).WithMany(e => e.Universities);
    

    }

    이 경우에 직원으로부터 자격 및 자격 대학에서 체인이 존재한다. 그래서 나 같은 예외가 발생했습니다.

    나는이 변경 될 때 그것은 나를 위해 일한

        modelBuilder.Entity<Qualification>().**HasRequired**(x=> x.Employee).WithMany(e => e.Qualifications); 
    

        modelBuilder.Entity<Qualification>().**HasOptional**(x=> x.Employee).WithMany(e => e.Qualifications);
    
  7. ==============================

    7.트리거는이 문제에 대한 솔루션입니다 :

    트리거는이 문제에 대한 솔루션입니다 :

    IF OBJECT_ID('dbo.fktest2', 'U') IS NOT NULL
        drop table fktest2
    IF OBJECT_ID('dbo.fktest1', 'U') IS NOT NULL
        drop table fktest1
    IF EXISTS (SELECT name FROM sysobjects WHERE name = 'fkTest1Trigger' AND type = 'TR')
        DROP TRIGGER dbo.fkTest1Trigger
    go
    create table fktest1 (id int primary key, anQId int identity)
    go  
        create table fktest2 (id1 int, id2 int, anQId int identity,
            FOREIGN KEY (id1) REFERENCES fktest1 (id)
                ON DELETE CASCADE
                ON UPDATE CASCADE/*,    
            FOREIGN KEY (id2) REFERENCES fktest1 (id) this causes compile error so we have to use triggers
                ON DELETE CASCADE
                ON UPDATE CASCADE*/ 
                )
    go
    
    CREATE TRIGGER fkTest1Trigger
    ON fkTest1
    AFTER INSERT, UPDATE, DELETE
    AS
        if @@ROWCOUNT = 0
            return
        set nocount on
    
        -- This code is replacement for foreign key cascade (auto update of field in destination table when its referenced primary key in source table changes.
        -- Compiler complains only when you use multiple cascased. It throws this compile error:
        -- Rrigger Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, 
        -- or modify other FOREIGN KEY constraints.
        IF ((UPDATE (id) and exists(select 1 from fktest1 A join deleted B on B.anqid = A.anqid where B.id <> A.id)))
        begin       
            update fktest2 set id2 = i.id
                from deleted d
                join fktest2 on d.id = fktest2.id2
                join inserted i on i.anqid = d.anqid        
        end         
        if exists (select 1 from deleted)       
            DELETE one FROM fktest2 one LEFT JOIN fktest1 two ON two.id = one.id2 where two.id is null -- drop all from dest table which are not in source table
    GO
    
    insert into fktest1 (id) values (1)
    insert into fktest1 (id) values (2)
    insert into fktest1 (id) values (3)
    
    insert into fktest2 (id1, id2) values (1,1)
    insert into fktest2 (id1, id2) values (2,2)
    insert into fktest2 (id1, id2) values (1,3)
    
    select * from fktest1
    select * from fktest2
    
    update fktest1 set id=11 where id=1
    update fktest1 set id=22 where id=2
    update fktest1 set id=33 where id=3
    delete from fktest1 where id > 22
    
    select * from fktest1
    select * from fktest2
    
  8. ==============================

    8.이 유형의 데이터베이스 트리거 정책의 오류입니다. 트리거는 코드와 계단식 삭제 같은 캐스케이드 관계에 약간의 지능이나 조건을 추가 할 수 있습니다. 당신은 CascadeOnDelete 끄기처럼이 주변의 관련 테이블 옵션을 전문으로해야 할 수도 있습니다 :

    이 유형의 데이터베이스 트리거 정책의 오류입니다. 트리거는 코드와 계단식 삭제 같은 캐스케이드 관계에 약간의 지능이나 조건을 추가 할 수 있습니다. 당신은 CascadeOnDelete 끄기처럼이 주변의 관련 테이블 옵션을 전문으로해야 할 수도 있습니다 :

    protected override void OnModelCreating( DbModelBuilder modelBuilder )
    {
        modelBuilder.Entity<TableName>().HasMany(i => i.Member).WithRequired().WillCascadeOnDelete(false);
    }
    

    또는이 기능을 완전히 끄기 :

    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    
  9. ==============================

    9.ASP.NET 코어 2.0 및 EF 코어 2.0을 사용하여 발생하는이 문제에 대한 나의 해결책은 순서대로 다음을 수행 할 수 있었다 :

    ASP.NET 코어 2.0 및 EF 코어 2.0을 사용하여 발생하는이 문제에 대한 나의 해결책은 순서대로 다음을 수행 할 수 있었다 :

    지금, 당신의 마이그레이션에 최신까지해야하며 계단식 삭제는 발생하지 않습니다.

    너무 나쁜 난 엔티티 프레임 워크 코어 2.0에서이 작업을 수행 할 수있는 방법을 찾을 수 없습니다.

    행운을 빕니다!

  10. from https://stackoverflow.com/questions/851625/foreign-key-constraint-may-cause-cycles-or-multiple-cascade-paths by cc-by-sa and MIT license