순환 외래 키 참조를 갖는 것이 허용됩니까?


29

외래 키 필드의 두 테이블간에 순환 참조가 허용됩니까?

그렇지 않다면 어떻게 이러한 상황을 피할 수 있습니까?

그렇다면 어떻게 데이터를 삽입 할 수 있습니까?

아래는 순환 참조가 허용되는 위치에 대한 예입니다.

CREATE TABLE Account
(
    ID INT PRIMARY KEY IDENTITY,
    Name VARCHAR(50)
)

CREATE TABLE Contact
(
    ID INT PRIMARY KEY IDENTITY,
    Name VARCHAR(50),
    AccountID INT FOREIGN KEY REFERENCES Account(ID)
)

ALTER TABLE Account ADD PrimaryContactID INT FOREIGN KEY REFERENCES Contact(ID)

2
" 그렇다면 데이터 삽입 방법 "-사용중인 DBMS에 따라 다릅니다. 예를 들어 Postgres, Oracle, SQLite 및 Apache Derby는이를 가능하게하는 지연 가능한 제한 조건을 허용합니다. 다른 DBMS를 사용하면 운이
나쁘다

답변:


12

외래 키에 널 입력 가능 필드를 사용하고 있기 때문에 실제로 예상 한대로 올바르게 작동하는 시스템을 구성 할 수 있습니다. Accounts 테이블에 행을 삽입하려면 null PrimaryContactID를 사용하여 Accounts에 삽입을 허용하지 않는 한 Contacts 테이블에 행이 있어야합니다. 계정 행이없는 상태에서 연락처 행을 만들려면 연락처 테이블의 AccountID 열이 Null을 허용해야합니다. 이를 통해 계정에 연락처가없고 연락처에 계정이 없어도됩니다. 아마도 이것이 바람직하지 않을 수도 있습니다.

내가 개인적으로 선호하는 것은 다음과 같이 설정하는 것입니다.

CREATE TABLE dbo.Accounts
(
    AccountID INT NOT NULL
        CONSTRAINT PK_Accounts
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , AccountName VARCHAR(255)
);

CREATE TABLE dbo.Contacts
(
    ContactID INT NOT NULL
        CONSTRAINT PK_Contacts
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , ContactName VARCHAR(255)
);

CREATE TABLE dbo.AccountsContactsXRef
(
    AccountsContactsXRefID INT NOT NULL
        CONSTRAINT PK_AccountsContactsXRef
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , AccountID INT NOT NULL
        CONSTRAINT FK_AccountsContactsXRef_AccountID
        FOREIGN KEY REFERENCES dbo.Accounts(AccountID)
    , ContactID INT NOT NULL
        CONSTRAINT FK_AccountsContactsXRef_ContactID
        FOREIGN KEY REFERENCES dbo.Contacts(ContactID)
    , IsPrimary BIT NOT NULL 
        CONSTRAINT DF_AccountsContactsXRef
        DEFAULT ((0))
    , CONSTRAINT UQ_AccountsContactsXRef_AccountIDContactID
        UNIQUE (AccountID, ContactID)
);

CREATE UNIQUE INDEX IX_AccountsContactsXRef_Primary
ON dbo.AccountsContactsXRef(AccountID, IsPrimary)
WHERE IsPrimary = 1;

이것은 다음과 같은 기능을 제공합니다.

  1. Pieter가 답변에서 권장하는 방식을 상호 참조 표를 통해 연락처와 계정 간의 관계를 명확하게 설명
  2. 비순환 적으로 건전한 참조 무결성을 유지하십시오.
  3. 색인을 통해 유지 관리가 용이 ​​한 기본 연락처 목록을 제공하십시오 IX_AccountsContactsXRef_Primary. 이 인덱스에는 필터가 포함되어 있으므로이를 지원하는 플랫폼에서만 작동합니다. 이 색인은 UNIQUE옵션 으로 지정되므로 각 계정에 대해 하나의 기본 담당자 만있을 수 있습니다.

예를 들어, "기본"상태를 나타내는 열과 함께 모든 계정 목록을 표시하려면 각 계정의 목록 맨 위에 기본 연락처를 표시하십시오.

SELECT A.AccountName
    , C.ContactName
    , XR.IsPrimary
FROM dbo.Accounts A
    INNER JOIN dbo.AccountsContactsXRef XR ON A.AccountID = XR.AccountID
    INNER JOIN dbo.Contacts C ON XR.ContactID = C.ContactID
ORDER BY A.AccountName
    , XR.IsPrimary DESC
    , C.ContactName;

필터링 된 인덱스는 계정 당 하나 이상의 기본 연락처를 삽입하는 것을 방지하는 동시에 기본 연락처 목록을 신속하게 반환하는 방법을 제공합니다. IsActive연락처가 더 이상 계정과 연결되지 않은 후에도 계정별로 연락처 기록을 유지하기 위해 고유하지 않은 필터링 된 인덱스를 사용하여 다른 열을 쉽게 상상할 수 있습니다 .

ALTER TABLE dbo.AccountsContactsXRef
ADD IsActive BIT NOT NULL
CONSTRAINT DF_AccountsContactsXRef_IsActive
DEFAULT ((1));

CREATE INDEX IX_AccountsContactsXRef_IsActive
ON dbo.AccountsContactsXRef(IsActive)
WHERE IsActive = 1;

1
일반적으로 순환 참조는 피해야한다고 말할 것입니까? 나는 그들이 나쁘지 않고 효과적인 디자인을 달성하는 데 사용했다고 생각합니다. 그들은 부모가 아닌 다른 엔티티에서 NULL을 요구하고 NULL로 업데이트한다는 점에서 삭제를 약간 더 복잡하게 만들지 만 편의를 위해 지불하는 저렴한 가격이라는 것을 알았습니다. 나는이 NULL있는 행을 작성하고 영업 이익에 설명 된대로 거의 동일한 기능을 수행하는 다음 자식 테이블에서 PK로 FK 필드를 업데이트하도록 FK 필드가 널 (NULL) 포스트 그레스에 사용할
amphibient

순환 참조는 디자인을 불필요하게 복잡하게 만드는 경향이 있기 때문에 단순히 참조를 좋아하지 않으며, 대부분의 경우 트레이드 오프 가치가 큰 성능 이점을 제공하지 않습니다. 나는 Occam 's Razor의 팬이며 결과적으로 주어진 문제에 대한 가장 간단한 해결책을 찾는 경향이 있습니다.
Max Vernon

1
나는 Occam의 면도칼을 모두 좋아하지만 위에서 설명한 디자인을 사용하면 세 번째 정규 형식을 위반하지 않고도 두 번째 쿼리 또는 조인을 피할 수 있습니다. 귀하의 의견에 감사드립니다
수륙 양용

6

아닙니다. 순환 외래 키 참조는 허용되지 않습니다. 제약 조건을 지속적으로 삭제하고 다시 만들지 않고 데이터를 삽입 할 수 없기 때문에뿐만 아니라. 하지만 제가 생각할 수있는 모든 영역의 근본적인 결함 모델이기 때문입니다. 귀하의 예에서 계정과 연락처 간의 관계가 NN이 아닌 도메인을 생각할 수 없으므로 FK 참조가있는 연결 테이블이 계정과 연락처 모두에 다시 필요합니다.

CREATE TABLE Account
(
    ID INT PRIMARY KEY IDENTITY,
    Name VARCHAR(50)
)

CREATE TABLE Contact
(
    ID INT PRIMARY KEY IDENTITY,
    Name VARCHAR(50),
)

CREATE TABLE AccountContact
(
    AccountID INT FOREIGN KEY REFERENCES Account(ID),
    ContactID INT FOREIGN KEY REFERENCES Contact(ID),

    primary key(AccountID,ContactID)
)

5
" 데이터를 삽입하는 것은 불가능합니다 "-아니오, 불가능하지는 않습니다. 제약 조건을 연기 가능한 것으로 선언하십시오. 그러나 나는 동의합니다 : 거의 모든 경우에 순환 참조는 나쁜 디자인입니다.
a_horse_with_no_name

3
@a_horse-SQL Server에서 연기 가능한 참조를 정의 할 수 없습니다 ... Oracle에서 할 수 있다는 것을 알고 있습니다. 불일치를 지적하고 싶었습니다.
Max Vernon

2
@MaxVernon는 : 질문은뿐만 아니라에 대한 SQL Server 및 연기 제약을 지원 단지 오라클보다 더 DBMS있다 -하지만 말했듯이 : 내가 디자인 자체가 잘못이다 (그의 해결책은 훨씬 더 의미가 있습니다) 것을 피터 동의가
a_horse_with_no_name

4
임의의 하나의 예의 특징을 제외하고, 일반적으로 상호 (즉, "원형") 참조 무결성 제약을 갖는 것에 대해 반드시 틀리거나 "흠이없는"것은 없다. 이것은 사실상 Join Dependency의 예일뿐입니다. DBMS를 통해 구현할 수 있다면 조인 종속성은 원칙적으로 좋은 것입니다. SQL DBMS에서는 테이블 간의 복잡한 종속성을 구현하기가 쉽지 않습니다.
nvogel 2016 년

6
@Pieter, 1-1은 조인 종속성의 유일한 예일뿐만 아니라 특별한 경우도 아닙니다. 조인 종속성 제약 조건이 완벽하게 이해되는 경우가 있습니다.
nvogel 2016 년

1

외부 개체가 계정이 아닌 기본 연락처를 가리 키도록 할 수 있습니다. 데이터는 다음과 같습니다.

CREATE TABLE Account
(
    ID INT PRIMARY KEY IDENTITY,
    Name VARCHAR(50)
)

CREATE TABLE Contact
(
    ID INT PRIMARY KEY IDENTITY,
    Name VARCHAR(50),
    AccountID INT FOREIGN KEY REFERENCES Account(ID)
)

CREATE TABLE AccountOwner (
    Other Stuff Here . . .
    PrimaryContactID INT FOREIGN KEY REFERENCES Contact(ID)
)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.