Bir yabancı anahtar yalnızca bir üst tabloya başvurmalıdır. Bu, hem SQL sözdizimi hem de ilişkisel teori için temeldir.
Polimorfik İlişkilendirme, belirli bir sütunun iki veya daha fazla ana tablodan birine başvurabileceği zamandır. Bu kısıtlamayı SQL'de bildirmenin bir yolu yok.
Polimorfik İlişkiler tasarımı, ilişkisel veritabanı tasarımının kurallarını çiğniyor. Kullanmanızı tavsiye etmiyorum.
Birkaç alternatif var:
Exclusive Arcs: Her biri bir ebeveyni referans alan birden çok yabancı anahtar sütunu oluşturun. Bu yabancı anahtarlardan tam olarak birinin NULL olmamasını sağlayın.
İlişkiyi Tersine Çevirin: Üç çoktan çoğa tablo kullanın, her biri Açıklamalara ve ilgili bir üst öğeye başvurur.
Somut Üstün Değer: Örtük "yorumlanabilir" üst sınıf yerine, ana tablolarınızın her birinin başvurduğu gerçek bir tablo oluşturun. Ardından Yorumlarınızı bu süper verile bağlayın. Sözde raylar kodu aşağıdakine benzer bir şey olacaktır (Ben bir Rails kullanıcısı değilim, bu yüzden bunu gerçek kod olarak değil, bir kılavuz olarak değerlendirin):
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
Ayrıca SQL'de Pratik Nesne Yönelimli Modeller sunumumda ve kitabımda polimorfik ilişkileri de ele alıyorum sunumumda SQL Antipatterns: Veritabanı Programlamanın Tuzaklarından Kaçınma .
Yorumunuz: Evet, yabancı anahtarın işaret ettiği varsayılan tablonun adını belirten başka bir sütun olduğunu biliyorum. Bu tasarım, SQL'deki yabancı anahtarlar tarafından desteklenmez.
Örneğin, bir Yorum eklerseniz ve bunun için üst tablonun adı olarak "Video" olarak adlandırırsanız ne olur? Comment
? "Video" adlı tablo yok. Ek bir hata ile iptal edilmeli mi? Hangi kısıtlama ihlal ediliyor? RDBMS, bu sütunun var olan bir tabloyu adlandırmasının beklendiğini nereden biliyor? Büyük / küçük harfe duyarlı olmayan tablo adlarını nasıl işler?
Aynı şekilde, Events
tabloyu kaldırırsanız, ancak içinde Comments
Olayları üst öğe olarak belirten satırlarınız varsa , sonuç ne olmalıdır? Düşürme tablosu iptal edilmeli mi? Sıralar Comments
öksüz bırakılmalı mı? Gibi başka bir mevcut tabloya başvurmak için değişmeli Articles
mi? Events
İşaret ederken herhangi bir anlam ifade eden id değerleriArticles
mu?
Bu ikilemlerin tümü, Polimorfik İlişkilendirmelerin meta verilere (bir tablo adı) atıfta bulunmak için verileri (yani bir dize değeri) kullanmaya bağlı olduğu gerçeğinden kaynaklanmaktadır. Bu, SQL tarafından desteklenmez. Veriler ve meta veriler ayrıdır.
Kafamı "Beton Üstün Olabilir" teklifinin etrafına dolamakta zorlanıyorum.
Commentable
Rails model tanımınızda yalnızca bir sıfat olarak değil, gerçek bir SQL tablosu olarak tanımlayın . Başka sütun gerekli değildir.
CREATE TABLE Commentable (
id INT AUTO_INCREMENT PRIMARY KEY
) TYPE=InnoDB;
Tablolar tanımlayın Articles
, Photos
ve Events
"alt sınıflarından" olarak bir Commentable
birincil anahtar aynı zamanda bir yabancı anahtar referans olması yaparak, Commentable
.
CREATE TABLE Articles (
id INT PRIMARY KEY,
FOREIGN KEY (id) REFERENCES Commentable(id)
) TYPE=InnoDB;
Comments
Tabloyu bir yabancı anahtar ile tanımlayın Commentable
.
CREATE TABLE Comments (
id INT PRIMARY KEY AUTO_INCREMENT,
commentable_id INT NOT NULL,
FOREIGN KEY (commentable_id) REFERENCES Commentable(id)
) TYPE=InnoDB;
Bir Article
(örneğin) oluşturmak istediğinizde , içinde Commentable
de yeni bir satır oluşturmalısınız . Yani çok için Photos
ve 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(), ... );
Bir oluşturmak istediğinizde Comment
, içinde bulunan bir değeri kullanın Commentable
.
INSERT INTO Comments (id, commentable_id, ...)
VALUES (DEFAULT, 2, ...);
Bir verilenle ilgili yorumları sorgulamak istediğinizde Photo
, bazı birleştirmeler yapın:
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;
Yalnızca bir yorumun kimliğine sahip olduğunuzda ve yorum yazılabilir kaynağı bulmak istediğinizde. Bunun için, Yorumlanabilir tablonun hangi kaynağı referans verdiğini belirlemesinin yararlı olduğunu görebilirsiniz.
SELECT commentable_id, commentable_type FROM Commentable t
JOIN Comments c ON (t.id = c.commentable_id)
WHERE c.id = 42;
Ardından, commentable_type
hangi tablodan katılacağınızı keşfettikten sonra ilgili kaynak tablosundan (Fotoğraflar, Makaleler, vb.) Veri almak için ikinci bir sorgu çalıştırmanız gerekir . Bunu aynı sorgu içinde yapamazsınız, çünkü SQL tabloların açıkça adlandırılmasını gerektirir; aynı sorgudaki veri sonuçlarına göre belirlenen bir tabloya katılamazsınız.
Kuşkusuz, bu adımlardan bazıları Rails tarafından kullanılan kuralları çiğniyor. Ancak Rails kuralları, uygun ilişkisel veritabanı tasarımı açısından yanlıştır.
foreign_key
aktarılabilecek bir seçenekten bahsetmiyorbelongs_to
. OP, yerel veritabanının "Yabancı Anahtar Kısıtlaması" ndan bahsediyor. Bu bir süre kafamı karıştırdı.