기본 테이블뿐만 아니라 뷰를 참조하는 외래 키를 허용하는 DBMS가 있습니까?


22

Django 모델링 질문에서 영감을 얻었습니다. Django의 여러 다 대다 관계를 가진 데이터베이스 모델링 . db-design은 다음과 같습니다.

CREATE TABLE Book
( BookID INT NOT NULL
, BookTitle VARCHAR(200) NOT NULL
, PRIMARY KEY (BookID)
) ;

CREATE TABLE Tag
( TagID INT NOT NULL
, TagName VARCHAR(50) NOT NULL
, PRIMARY KEY (TagID)
) ;

CREATE TABLE BookTag
( BookID INT NOT NULL
, TagID INT NOT NULL
, PRIMARY KEY (BookID, TagID)
, FOREIGN KEY (BookID)  REFERENCES Book (BookID)
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
) ;

CREATE TABLE Aspect
( AspectID INT NOT NULL
, AspectName VARCHAR(50) NOT NULL
, PRIMARY KEY (AspectID)
) ;

CREATE TABLE TagAspect
( TagID INT NOT NULL
, AspectID INT NOT NULL
, PRIMARY KEY (TagID, AspectID) 
, FOREIGN KEY (TagID)   REFERENCES Tag (TagID)
, FOREIGN KEY (AspectID)  REFERENCES Aspect (AspectID)
) ;

DB 다이어그램

문제는 BookAspectRating테이블 을 정의하고 참조 무결성을 강화 (Book, Aspect)하는 방법이므로 유효하지 않은 조합에 등급을 추가 할 수 없습니다 .

서브 쿼리와이를 해결할 수있는 둘 이상의 테이블을 포함하는 복잡한 CHECK제한 조건 (또는 ASSERTIONS) 인 AFAIK 는 DBMS에서 사용할 수 없습니다.

또 다른 아이디어는 뷰를 사용 (의사 코드)하는 것입니다.

CREATE VIEW BookAspect_view
  AS
SELECT DISTINCT
    bt.BookId
  , ta.AspectId
FROM 
    BookTag AS bt
  JOIN 
    Tag AS t  ON t.TagID = bt.TagID
  JOIN 
    TagAspect AS ta  ON ta.TagID = bt.TagID 
WITH PRIMARY KEY (BookId, AspectId) ;

위의보기에 대한 외래 키가있는 테이블 :

CREATE TABLE BookAspectRating
( BookID INT NOT NULL
, AspectID INT NOT NULL
, PersonID INT NOT NULL
, Rating INT NOT NULL
, PRIMARY KEY (BookID, AspectID, PersonID)
, FOREIGN KEY (PersonID)   REFERENCES Person (PersonID)
, FOREIGN KEY (BookID, AspectID) 
    REFERENCES BookAspect_view (BookID, AspectID)
) ;

세 가지 질문 :

  • A는 (아마도 구체화) 할 수있다 DBMS 있습니까 VIEWA의는 PRIMARY KEY?

  • 을 할 수있다 DBMS 있습니까 FOREIGN KEY그 (및뿐만 아니라 기본 )은?REFERENCESVIEWTABLE

  • 사용 가능한 DBMS 기능으로이 무결성 문제를 다른 방식으로 해결할 수 있습니까?


설명:

아마도 100 % 만족스러운 해결책이 없기 때문에 장고 질문은 내 것이 아닙니다! -자세한 해결책이 아니라 문제에 대한 일반적인 공격 전략에 더 관심이 있습니다. 따라서 "DBMS-X에서이 작업은 테이블 A의 트리거로 수행 할 수 있습니다" 와 같은 대답 은 완벽하게 허용됩니다.


처음 두 질문에 대한 의견으로 게시-이미 알고있는 것처럼 반드시 그런 것은 아니지만 SQL Server는 뷰에 기본 또는 외래 키를 지원하지 않습니다.
Aaron Bertrand

@Aaron : 예, 감사합니다. Oracle이 PK 비용을 지원한다는 견해를 읽었습니다. 그러나이 상황에서 작동하는지 확실하지 않습니다. 그리고 두 번째 질문에 대한 대답 (FK에 대한 견해)은 Oracle에서 부정적 일 것입니다.
ypercubeᵀᴹ

그러나 다른 솔루션 (트리거, Check costraints 또는 기타 콤보)이
있는지 알고 싶습니다.

답변:


9

이 비즈니스 규칙은 구속 조건 만 사용하여 모델에서 시행 할 수 있습니다. 다음 표는 문제를 해결해야합니다. 보기 대신 사용하십시오.

    CREATE TABLE BookAspectCommonTagLink
    (  BookID INT NOT NULL
    , AspectID INT NOT NULL
    , TagID INT NOT NULL
--TagID is deliberately left out of PK
    , PRIMARY KEY (BookID, AspectID)
    , FOREIGN KEY (BookID, TagID) 
        REFERENCES BookTag (BookID, TagID)
    , FOREIGN KEY (AspectID, TagID) 
        REFERENCES AspectTag (AspectID, TagID)
    ) ;

오 좋아요 내가 생각할 수있는 유일한 문제는 BookTags 및 TagAspects 삽입 / 삭제에 도입 된 복잡성입니다. 예를 들어, 새로운 BookTag (또는 TagAspect)가 제거 될 때마다이 테이블에서 해당 행을 제거하거나 TagID동일한 BookAspect 조합과 관련된 다른 태그로 변경하기 위해 검색을 수행해야합니다 .
ypercubeᵀᴹ

그리고이 두 테이블에 삽입하려면 비슷한 검색을 수행해야합니다. 그러나 복잡한 규칙에는 복잡한 절차가 필요하므로 정말 좋습니다.
ypercubeᵀᴹ

@ypercube 태그를 삭제할 때 동일한 책과 측면을 연결하는 다른 태그를 확인하고 다른 태그로 전환해야합니다. 그러나 새 태그를 삽입 할 때 등급을 삽입해야 할 때까지 점검 할 필요가 없습니다.
AK

1
문제 해결사와 데이터 입력 담당자가 같은 사람이거나 최종 사용자에게 오류 메시지가 표시되면 반드시 확인하십시오. 모든 일을하고있는 1 인 상점에 대해 너무 많이 생각하고 있습니다.
Aaron Bertrand

4
@AaronBertrand 방금 큰 호의를 베 풀었습니다. "낮은 유지 관리 데이터베이스 개발"이라는 기사를 마치고 있는데 응용 프로그램 서버가 데이터베이스에서 들어오는 원래 오류 메시지를 기록해야한다는 것을 잊었습니다. 방금 추가했습니다. ;)
AK

8

많은 경우에 모델만으로는 복잡한 비즈니스 규칙을 시행 할 수 없다는 것을 알게 될 것입니다. 이것은 적어도 SQL Server에서 트리거 (바람직하게는 트리거 대신)가 귀하의 목적에 더 잘 부합한다고 생각하는 경우 중 하나입니다.


아론,이 경우 왜 트리거가 엔티티와 몇 가지 제약보다 더 나은 선택인지 설명해 주시겠습니까?
AK

2
@AlexKuznetsov 물론 여러 다중 열 외래 키로 구현하는 방법과 유효성 검사 및 오류 처리를 처리하는 데 필요한 모든 추가 논리에 대해 17 시간을 소비하지 않았기 때문에 17 시간을 소비하지 않았습니까?
Aaron Bertrand

2
순진한 트리거 구현으로 인해 발생할 수있는 경쟁 조건에주의하십시오. 예를 들어, 한 트랜잭션이 책을 태그에서 연결 해제하고 다른 트랜잭션은 첫 번째 트랜잭션이 아직 커밋되지 않았기 때문에 해당 측면에 연결해도 괜찮다고 생각합니다. @AlexKuznetsov 답변에 의해 도입 된 복잡성은 아마도 IMHO 트리거의 경쟁 조건을 방지하는 데 필요한 잠금 "프로토콜"의 복잡성과 취약성보다 적을 것입니다.
Branko Dimitrijevic

8

Oracle에서 이러한 종류의 제약 조건을 선언적인 방식으로 적용하는 한 가지 방법은 쿼리에서 모든 유효하지 않은 행 (예 :에 BookAspectRating일치하지 않는 행)을 식별하는 커밋시 빠르게 새로 고쳐 지도록 구체화 된 뷰를 만드는 것 BookAspect_view입니다. 그런 다음 구체화 된 뷰에 행이있는 경우 위반 될 구체화 된 뷰에 대한 간단한 구속 조건을 작성할 수 있습니다. 이는 구체화 된보기에서 복제해야하는 데이터의 양을 최소화하는 이점이 있습니다. 그러나 트랜잭션을 커밋하는 시점에만 제약 조건이 적용되므로 커밋 작업이 실패 할 것으로 예상되는 많은 응용 프로그램이 작성되지 않고 제약 조건 위반이 다소 어려울 수 있기 때문에 문제가 발생할 수 있습니다. 특정 행 또는 특정 테이블과 연결합니다.


4

SIRA_PRISE가 허용합니다.

FK를 더 이상 "FK"라고 부르지 않고 "데이터베이스 제약 조건"만하고 "뷰"를 실제로 뷰로 정의 할 필요는 없지만 식을 정의하는 뷰를 데이터베이스 제약.

당신의 제약은 다음과 같습니다

SEMIMINUS(BOOKASPECT , JOIN(BOOKTAG , TAGASPECT))

그리고 당신은 끝났습니다.

그러나 대부분의 SQL DBMS에서는 제약 조건에 대한 분석 작업을 수행하고 위반 방법을 결정하고 필요한 모든 트리거를 구현해야합니다.


알아. 글을 쓸 당시 내가 중요하다고 생각한 것을 반영합니다.
Erwin Smout

3

PostgreSQL에서 트리거를 사용하지 않고 솔루션을 상상할 수는 없지만 확실하게 해결 할 수 있습니다 (어떤 종류의 구체화 된 뷰를 유지하거나 이전 트리거를 켜기 BookAspectRating). ERROR: referenced relation "v_munkalap" is not a table기본 키는 물론 보기 ( )를 참조하는 외래 키가 없습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.