외래 키는 하나의 상위 테이블 만 참조해야합니다. 이것은 SQL 구문과 관계 이론의 기본입니다.
다형성 연관은 주어진 열이 둘 이상의 상위 테이블 중 하나를 참조 할 수있는 경우입니다. SQL에서 제약 조건을 선언 할 수있는 방법은 없습니다.
Polymorphic Associations 디자인은 관계형 데이터베이스 디자인의 규칙을 위반합니다. 나는 그것을 사용하지 않는 것이 좋습니다.
몇 가지 대안이 있습니다.
배타적 호 : 각각 하나의 부모를 참조하는 여러 외래 키 열을 만듭니다. 이러한 외래 키 중 정확히 하나가 NULL이 아닐 수 있도록 강제합니다.
관계 반전 : 3 개의 다 대다 테이블을 사용하고 각 테이블은 주석과 각 상위를 참조합니다.
구체적인 수퍼 테이블 : 암시 적 "주석 가능"수퍼 클래스 대신 각 상위 테이블이 참조하는 실제 테이블을 만듭니다. 그런 다음 주석을 해당 수퍼 테이블에 연결하십시오. Pseudo-rails 코드는 다음과 같습니다 (저는 Rails 사용자가 아니므로 리터럴 코드가 아닌 지침으로 취급하십시오).
class Commentable < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :commentable
end
class Article < ActiveRecord::Base
belongs_to :commentable
end
class Photo < ActiveRecord::Base
belongs_to :commentable
end
class Event < ActiveRecord::Base
belongs_to :commentable
end
또한 프레젠테이션 에서 SQL의 Practical Object-Oriented Models 및 저서 SQL Antipatterns : Preventing the Pitfalls of Database Programming 에서 다형성 연관성에 대해서도 다룹니다 .
Re your comment : 예, 외래 키가 가리키는 테이블의 이름을 기록하는 또 다른 열이 있다는 것을 알고 있습니다. 이 디자인은 SQL의 외래 키에서 지원되지 않습니다.
예를 들어 댓글을 삽입하고 해당 상위 테이블의 이름으로 "Video"라는 이름을 입력하면 Comment
어떻게됩니까? "비디오"라는 테이블이 없습니다. 삽입이 오류와 함께 중단되어야합니까? 어떤 제약이 위반되고 있습니까? RDBMS는이 열이 기존 테이블의 이름을 지정해야한다는 것을 어떻게 알 수 있습니까? 대소 문자를 구분하지 않는 테이블 이름을 어떻게 처리합니까?
마찬가지로 Events
테이블 을 삭제 했지만 Comments
이벤트를 상위로 표시하는 행 이있는 경우 결과는 무엇입니까? 삭제 테이블을 중단해야합니까? 의 행 Comments
이 분리 되어야합니까 ? 같은 다른 기존 테이블을 참조하도록 변경해야합니까 Articles
? Events
가리키는 데 사용되는 ID 값이 의미가 Articles
있습니까?
이러한 딜레마는 모두 다형성 연관이 메타 데이터 (테이블 이름)를 참조하기 위해 데이터 (예 : 문자열 값)를 사용하는 데 의존한다는 사실 때문입니다. 이것은 SQL에서 지원되지 않습니다. 데이터와 메타 데이터는 분리되어 있습니다.
"콘크리트 수퍼 테이블"제안에 대해 머리를 감싸는 데 어려움을 겪고 있습니다.
정의 Commentable
레일스 모델 정의에 실제 SQL 테이블로가 아닌 형용사입니다. 다른 열은 필요하지 않습니다.
CREATE TABLE Commentable (
id INT AUTO_INCREMENT PRIMARY KEY
) TYPE=InnoDB;
테이블 Articles
, Photos
및 Events
의 "하위 클래스"를 정의하여 Commentable
기본 키도를 참조하는 외래 키가되도록합니다 Commentable
.
CREATE TABLE Articles (
id INT PRIMARY KEY,
FOREIGN KEY (id) REFERENCES Commentable(id)
) TYPE=InnoDB;
Comments
에 대한 외래 키를 사용 하여 테이블을 정의합니다 Commentable
.
CREATE TABLE Comments (
id INT PRIMARY KEY AUTO_INCREMENT,
commentable_id INT NOT NULL,
FOREIGN KEY (commentable_id) REFERENCES Commentable(id)
) TYPE=InnoDB;
Article
(예를 들어) 생성하려는 경우 새 행도 생성해야합니다 Commentable
. 그래서 너무에 Photos
와 Events
.
INSERT INTO Commentable (id) VALUES (DEFAULT);
INSERT INTO Articles (id, ...) VALUES ( LAST_INSERT_ID(), ... );
INSERT INTO Commentable (id) VALUES (DEFAULT);
INSERT INTO Photos (id, ...) VALUES ( LAST_INSERT_ID(), ... );
INSERT INTO Commentable (id) VALUES (DEFAULT);
INSERT INTO Events (id, ...) VALUES ( LAST_INSERT_ID(), ... );
을 만들려면에 Comment
있는 값을 사용하십시오 Commentable
.
INSERT INTO Comments (id, commentable_id, ...)
VALUES (DEFAULT, 2, ...);
주어진의 주석을 쿼리하려면 Photo
몇 가지 조인을 수행하십시오.
SELECT * FROM Photos p JOIN Commentable t ON (p.id = t.id)
LEFT OUTER JOIN Comments c ON (t.id = c.commentable_id)
WHERE p.id = 2;
댓글의 ID 만 있고 댓글에 해당하는 댓글 리소스를 찾고 싶을 때. 이를 위해 Commentable 테이블이 참조하는 리소스를 지정하는 것이 도움이 될 수 있습니다.
SELECT commentable_id, commentable_type FROM Commentable t
JOIN Comments c ON (t.id = c.commentable_id)
WHERE c.id = 42;
그런 다음 commentable_type
조인 할 테이블을 찾은 후 각 리소스 테이블 (사진, 기사 등)에서 데이터를 가져 오기 위해 두 번째 쿼리를 실행해야 합니다. SQL에서는 테이블 이름을 명시 적으로 지정해야하므로 동일한 쿼리에서이를 수행 할 수 없습니다. 동일한 쿼리의 데이터 결과로 결정된 테이블에 조인 할 수 없습니다.
물론 이러한 단계 중 일부는 Rails에서 사용하는 규칙을 위반합니다. 그러나 적절한 관계형 데이터베이스 설계와 관련하여 Rails 규칙이 잘못되었습니다.
foreign_key
에 전달 될 수있는 옵션belongs_to
. OP는 네이티브 데이터베이스의 "외래 키 제약"에 대해 이야기하고 있습니다. 그것은 잠시 나를 혼란스럽게했다.