SQL에서는 쉽지 않지만 불가능하지는 않습니다. 이를 DDL만으로 시행하려면 DBMS가 DEFERRABLE
제한 조건 을 구현해야 합니다. 이 작업을 수행 할 수 있습니다 (Postgres에서 작동하는지 확인할 수 있음).
-- lets create first the 2 tables, A and B:
CREATE TABLE a
( aid INT NOT NULL,
bid INT NOT NULL,
CONSTRAINT a_pk PRIMARY KEY (aid)
);
CREATE TABLE b
( bid INT NOT NULL,
aid INT NOT NULL,
CONSTRAINT b_pk PRIMARY KEY (bid)
);
-- then table R:
CREATE TABLE r
( aid INT NOT NULL,
bid INT NOT NULL,
CONSTRAINT r_pk PRIMARY KEY (aid, bid),
CONSTRAINT a_r_fk FOREIGN KEY (aid) REFERENCES a,
CONSTRAINT b_r_fk FOREIGN KEY (bid) REFERENCES b
);
여기까지는 "정상적인"디자인으로 모든 A
것이 0, 1 또는 다수 B
와 B
관련 될 수 있고 모든 것이 0, 하나 또는 다수 와 관련 될 수 있습니다 A
.
"전체 참여"제한에는 역순으로 제한이 필요합니다 ( A
및 B
참조 R
). 데 FOREIGN KEY
(X에서 Y와 X에 Y에서) 원의 (a "닭이 먼저 냐 달걀이 먼저 냐"문제)을 형성하고, 우리는 적어도이 될 그들 중 하나를 필요로하는 이유의 그 반대 방향으로 제약 DEFERRABLE
. 이 경우에는 두 개의 원이 있습니다 (A -> R -> A
그리고 B -> R -> B
우리는 두 가지, 연기 제약 조건이 필요합니다
-- then we add the 2 constraints that enforce the "total participation":
ALTER TABLE a
ADD CONSTRAINT r_a_fk FOREIGN KEY (aid, bid) REFERENCES r
DEFERRABLE INITIALLY DEFERRED ;
ALTER TABLE b
ADD CONSTRAINT r_b_fk FOREIGN KEY (aid, bid) REFERENCES r
DEFERRABLE INITIALLY DEFERRED ;
그런 다음 데이터를 삽입 할 수 있는지 테스트 할 수 있습니다. 가 있습니다 INITIALLY DEFERRED
필요하지 않습니다. 우리는 제약 조건을 정의 할 수 DEFERRABLE INITIALLY IMMEDIATE
있었지만 SET CONSTRAINTS
트랜잭션 중에 명령문을 사용하여 연기해야합니다. 그러나 모든 경우에 단일 트랜잭션으로 테이블에 삽입해야합니다.
-- insert data
BEGIN TRANSACTION ;
INSERT INTO a (aid, bid)
VALUES
(1, 1), (2, 5),
(3, 7), (4, 1) ;
INSERT INTO b (aid, bid)
VALUES
(1, 1), (1, 2),
(2, 3), (2, 4),
(2, 5), (3, 6),
(3, 7) ;
INSERT INTO r (aid, bid)
VALUES
(1, 1), (1, 2),
(2, 3), (2, 4),
(2, 5), (3, 6),
(3, 7), (4, 1),
(4, 2), (4, 7) ;
END ;
SQLfiddle 에서 테스트되었습니다 .
DBMS에 DEFERRABLE
제한 조건 이없는 경우 한 가지 해결 방법은 A (bid)
및 B (aid)
열을로 정의하는 것 NULL
입니다. INSERT
절차 / 문은 다음에 먼저 삽입해야 A
하고 B
(에 널 (null)을 넣어bid
와 aid
다음, 각각)에 삽입 R
한 다음에서 관련 null이 아닌 값으로 위의 널 (null) 값을 업데이트 R
.
이 방법으로, DBMS는 DDL 혼자 그러나으로 요구 사항을 적용마다하지 않습니다 INSERT
(그리고UPDATE
및 DELETE
및 MERGE
절차를 고려하고 그에 따라 조정 및 사용자는 그들과하지가 테이블에 직접 쓰기 액세스를 사용하도록 제한 할 필요가되어야한다).
에 서클을 갖는 FOREIGN KEY
제약 조건 것은 많은 모범 사례에 의해 고려되지 않으며 좋은 이유로 복잡성이 그 중 하나입니다. 예를 들어 널 입력 가능 열을 사용하는 두 번째 방법을 사용하면 DBMS에 따라 행 업데이트 및 삭제를 추가 코드로 수행해야합니다. 예를 들어 SQL Server에서는 ON DELETE CASCADE
FK 서클이있을 때 계단식 업데이트 및 삭제가 허용되지 않기 때문에 넣을 수 없습니다 .
이 관련 질문에 대한 답변도 읽으십시오 :
특권 자녀와 일대 다 관계를 맺는 방법?
또 다른 세 번째 접근법 (위의 질문에서 내 대답 참조)은 원형 FK를 완전히 제거하는 것입니다. 그래서, 코드의 첫 번째 부분을 유지 (테이블 A
, B
, R
단지 R에서 A와 B와 외래 키) (실제로 그것을 단순화) 거의 그대로, 우리는 또 다른 테이블 추가 A
에서 "이 있어야 하나"관련 항목을 저장하기를 B
. 따라서 A (bid)
열 A_one (bid)
은 B에서 A 로의 역 관계에 대해 동일하게 수행됩니다.
CREATE TABLE a
( aid INT NOT NULL,
CONSTRAINT a_pk PRIMARY KEY (aid)
);
CREATE TABLE b
( bid INT NOT NULL,
CONSTRAINT b_pk PRIMARY KEY (bid)
);
-- then table R:
CREATE TABLE r
( aid INT NOT NULL,
bid INT NOT NULL,
CONSTRAINT r_pk PRIMARY KEY (aid, bid),
CONSTRAINT a_r_fk FOREIGN KEY (aid) REFERENCES a,
CONSTRAINT b_r_fk FOREIGN KEY (bid) REFERENCES b
);
CREATE TABLE a_one
( aid INT NOT NULL,
bid INT NOT NULL,
CONSTRAINT a_one_pk PRIMARY KEY (aid),
CONSTRAINT r_a_fk FOREIGN KEY (aid, bid) REFERENCES r
);
CREATE TABLE b_one
( bid INT NOT NULL,
aid INT NOT NULL,
CONSTRAINT b_one_pk PRIMARY KEY (bid),
CONSTRAINT r_b_fk FOREIGN KEY (aid, bid) REFERENCES r
);
첫 번째와 두 번째 방법의 차이점은 순환 FK가 없으므로 계단식 업데이트 및 삭제가 제대로 작동한다는 것입니다. "총 참여"의 시행은 2 차 접근법에서와 같이 DDL 단독이 아니며 적절한 절차에 따라 수행되어야한다 (INSERT/UPDATE/DELETE/MERGE
). 두 번째 접근 방식과의 약간의 차이점은 모든 열을 nullable이 아닌 것으로 정의 할 수 있다는 것입니다.
또 다른 네 번째 접근 방법 ( 위 의 질문 에서 @Aaron Bertrand의 답변 참조 )은 DBMS에서 사용할 수있는 경우 필터링 된 / 부분 고유 인덱스 를 사용 하는 것 R
입니다 (이 경우 테이블에 두 개가 필요합니다 ). 이것은 두 개의 추가 테이블이 필요하지 않다는 점을 제외하고는 세 번째 접근 방식과 매우 유사합니다. "전체 참여"제한 조건은 여전히 코드에 의해 적용되어야합니다.