문제

아래에 Rails 모델로 표시된 것과 같은 다형성 연관에 외국 키가 없을 수있는 이유는 무엇입니까?

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

class Article < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

class Photo < ActiveRecord::Base
  has_many :comments, :as => :commentable
  #...
end

class Event < ActiveRecord::Base
  has_many :comments, :as => :commentable
end
도움이 되었습니까?

해결책

외국 키는 하나의 상위 테이블 만 참조해야합니다. 이것은 SQL 구문과 관계 이론의 기본입니다.

다형성 연관성은 주어진 열이 둘 이상의 부모 테이블 중 하나를 참조 할 수있는 경우입니다. SQL에서 해당 제약을 선언 할 수있는 방법은 없습니다.

다형성 협회 설계는 관계형 데이터베이스 설계의 규칙을 중단합니다. 나는 그것을 사용하지 않는 것이 좋습니다.

몇 가지 대안이 있습니다.

  • 독점 아크 : 각각 한 부모를 참조하는 여러 개의 외국 키 열을 만듭니다. 이 외국 키 중 하나가 널이 아닌 것과 정확히 일치 할 수 있음을 시행하십시오.

  • 관계를 바꾸십시오 : 3 개의 다중 대기 테이블, 각 참조 주석 및 각 부모를 사용하십시오.

  • 콘크리트 수퍼 테이블 : 암시 적 "주석 가능한"슈퍼 클래스 대신 각 상위 테이블이 참조하는 실제 테이블을 만듭니다. 그런 다음 귀하의 의견을 그 수퍼 테이블에 연결하십시오. 의사 레일 코드는 다음과 같은 것입니다 (나는 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의 실제 객체 지향 모델, 그리고 내 책 SQL antipatterns : 데이터베이스 프로그래밍의 함정을 피합니다.


RE 귀하의 의견 : 그렇습니다. 외국 키가 가리키는 테이블의 이름을 기록하는 또 다른 열이 있다는 것을 알고 있습니다. 이 디자인은 SQL의 외국 키에서 지원되지 않습니다.

예를 들어, 댓글을 삽입하고 "비디오"이름을 삽입하면 부모 테이블의 이름으로 "비디오"를 삽입하면 Comment? "비디오"라는 테이블이 없습니다. 인서트에 오류가 발생해야합니까? 어떤 제약이 위반되고 있습니까? RDBMS는이 열이 기존 테이블의 이름을 지정해야한다는 것을 어떻게 알 수 있습니까? 케이스 감수성 테이블 이름을 어떻게 처리합니까?

마찬가지로, 당신이 떨어 뜨리면 Events 테이블이지만 줄이 있습니다 Comments 그것은 사건을 부모로서 나타냅니다. 결과는 무엇입니까? 드롭 테이블을 중단해야합니까? 줄이 들어와야합니다 Comments 고아? 다음과 같은 다른 기존 테이블을 참조하도록 변경해야합니다. Articles? 가리키는 데 사용 된 ID 값을 수행하십시오 Events 지적 할 때 의미가 있습니다 Articles?

이러한 딜레마는 모두 다형성 연관성이 메타 데이터 (테이블 이름)를 참조하기 위해 데이터를 사용하는 데 의존한다는 사실 때문입니다. 이것은 SQL에 의해 지원되지 않습니다. 데이터와 메타 데이터는 별도입니다.


나는 당신의 "콘크리트 슈퍼 테이블"제안에 머리를 감싸는 데 어려움을 겪고 있습니다.

  • 정의하다 Commentable Rails 모델 정의의 형용사만이 아니라 실제 SQL 테이블로서. 다른 열은 필요하지 않습니다.

    CREATE TABLE Commentable (
      id INT AUTO_INCREMENT PRIMARY KEY
    ) TYPE=InnoDB;
    
  • 테이블을 정의하십시오 Articles, Photos, 그리고 Events "하위 클래스"로 Commentable, 그들의 주요 키를 외국 키 참조로 만들어 Commentable.

    CREATE TABLE Articles (
      id INT PRIMARY KEY, -- not auto-increment
      FOREIGN KEY (id) REFERENCES Commentable(id)
    ) TYPE=InnoDB;
    
    -- similar for Photos and Events.
    
  • 정의하십시오 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); -- generate a new id 1
    INSERT INTO Articles (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 2
    INSERT INTO Photos (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 3
    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 만 가지고 있고 댓글 가능한 리소스를 찾고 싶을 때 의견을 찾으십시오. 이를 위해서는 주석 가능한 테이블이 참조하는 리소스를 지정하는 것이 도움이 될 수 있습니다.

    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 컨벤션은 적절한 관계형 데이터베이스 설계와 관련하여 잘못되었습니다.

다른 팁

Bill Karwin은 SQL이 실제로 기본 개념 다형성 관계를 갖지 않기 때문에 외국 키가 다형성 관계와 함께 사용할 수 없다는 것이 맞습니다. 그러나 외국 키를 갖는 목표가 참조 무결성을 시행하는 것이라면 트리거를 통해 시뮬레이션 할 수 있습니다. 이것은 DB에 특화되어 있지만 아래는 다형성 관계에 대한 외국 키의 계단식 삭제 동작을 시뮬레이션하기 위해 만든 최근 트리거입니다.

CREATE FUNCTION delete_related_brokerage_subscribers() RETURNS trigger AS $$
  BEGIN
    DELETE FROM subscribers
    WHERE referrer_type = 'Brokerage' AND referrer_id = OLD.id;
    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER cascade_brokerage_subscriber_delete
AFTER DELETE ON brokerages
FOR EACH ROW EXECUTE PROCEDURE delete_related_brokerage_subscribers();


CREATE FUNCTION delete_related_agent_subscribers() RETURNS trigger AS $$
  BEGIN
    DELETE FROM subscribers
    WHERE referrer_type = 'Agent' AND referrer_id = OLD.id;
    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER cascade_agent_subscriber_delete
AFTER DELETE ON agents
FOR EACH ROW EXECUTE PROCEDURE delete_related_agent_subscribers();

내 코드에서 brokerages 테이블 또는 레코드 agents 테이블은 기록의 레코드와 관련 될 수 있습니다 subscribers 테이블.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top