Açıkladığınız şeye Polimorfik Dernekler denir. Diğer bir deyişle, "yabancı anahtar" sütunu, bir dizi hedef tablodan birinde bulunması gereken bir kimlik değeri içerir. Tipik olarak hedef tablolar, bazı yaygın veri sınıflarının örnekleri gibi bir şekilde ilişkilidir. Ayrıca, yabancı anahtar sütununun yanında başka bir sütuna ihtiyacınız olacak, böylece her satırda hangi hedef tabloya başvuruda bulunulacağını belirtebilirsiniz.
CREATE TABLE popular_places (
user_id INT NOT NULL,
place_id INT NOT NULL,
place_type VARCHAR(10) -- either 'states' or 'countries'
-- foreign key is not possible
);
SQL kısıtlamaları kullanarak Polimorfik İlişkileri modellemenin bir yolu yoktur. Yabancı anahtar kısıtlaması her zaman bir hedef tabloya başvurur.
Çok Biçimli İlişkiler, Rails ve Hibernate gibi çerçevelerle desteklenir. Ancak açıkça bu özelliği kullanmak için SQL kısıtlamalarını devre dışı bırakmanız gerektiğini söylüyorlar. Bunun yerine, başvurunun karşılandığından emin olmak için uygulama veya çerçeve eşdeğer çalışmalar yapmalıdır. Yani, yabancı anahtardaki değer olası hedef tablolarından birinde bulunur.
Çok Biçimli Dernekler veritabanı tutarlılığının güçlendirilmesi bakımından zayıftır. Veri bütünlüğü, aynı başvuru bütünlüğü mantığının uygulandığı veritabanına erişen tüm istemcilere bağlıdır ve ayrıca uygulamanın hatasız olması gerekir.
Veritabanının uyguladığı referans bütünlüğünden yararlanan bazı alternatif çözümler şunlardır:
Hedef başına bir ekstra tablo oluşturun. Örneğinpopular_states
ve popular_countries
aynı zamanda, referans states
ve countries
sırasıyla. Bu "popüler" tabloların her biri de kullanıcının profilini gösterir.
CREATE TABLE popular_states (
state_id INT NOT NULL,
user_id INT NOT NULL,
PRIMARY KEY(state_id, user_id),
FOREIGN KEY (state_id) REFERENCES states(state_id),
FOREIGN KEY (user_id) REFERENCES users(user_id),
);
CREATE TABLE popular_countries (
country_id INT NOT NULL,
user_id INT NOT NULL,
PRIMARY KEY(country_id, user_id),
FOREIGN KEY (country_id) REFERENCES countries(country_id),
FOREIGN KEY (user_id) REFERENCES users(user_id),
);
Bu, bir kullanıcının popüler favori yerlerinin tümünü almak için bu tabloların her ikisini de sorgulamanız gerektiği anlamına gelir. Ancak tutarlılığı uygulamak için veritabanına güvenebileceğiniz anlamına gelir.
places
Süper tablo olarak bir tablo oluşturun . Abie'nin belirttiği gibi, ikinci bir alternatif, popüler yerlerinizin places
hem states
ve hem de ebeveynleri gibi bir tabloya başvurmasıdır countries
. Yani, hem eyaletlerin hem de ülkelerin de yabancı bir anahtarı vardır places
(bu yabancı anahtarı bilestates
ve vecountries
).
CREATE TABLE popular_areas (
user_id INT NOT NULL,
place_id INT NOT NULL,
PRIMARY KEY (user_id, place_id),
FOREIGN KEY (place_id) REFERENCES places(place_id)
);
CREATE TABLE states (
state_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (state_id) REFERENCES places(place_id)
);
CREATE TABLE countries (
country_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (country_id) REFERENCES places(place_id)
);
İki sütun kullanın. İki hedef tablodan birine başvurabilecek bir sütun yerine iki sütun kullanın. Bu iki sütunNULL
; aslında bunlardan sadece biri olmamalıdır NULL
.
CREATE TABLE popular_areas (
place_id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
state_id INT,
country_id INT,
CONSTRAINT UNIQUE (user_id, state_id, country_id), -- UNIQUE permits NULLs
CONSTRAINT CHECK (state_id IS NOT NULL OR country_id IS NOT NULL),
FOREIGN KEY (state_id) REFERENCES places(place_id),
FOREIGN KEY (country_id) REFERENCES places(place_id)
);
İlişkisel teori açısından, Polimorfik Dernekler İlk Normal Formu ihlal eder , çünkü bu popular_place_id
aslında iki anlamı olan bir sütundur: ya bir eyalet ya da bir ülke. Bir kişinin age
ve onlarınkini phone_number
tek bir sütunda depolamazsınız ve aynı nedenle her ikisini de state_id
ve country_id
tek bir sütunda saklamamalısınız . Bu iki özelliğin uyumlu veri türlerine sahip olması rastlantısaldır; hala farklı mantıksal varlıkları ifade ediyorlar.
Polimorfik İlişkiler ayrıca Üçüncü Normal Formu ihlal eder , çünkü sütunun anlamı yabancı anahtarın başvurduğu tabloyu adlandıran ekstra sütuna bağlıdır. Üçüncü Normal Formda, tablodaki bir öznitelik yalnızca o tablonun birincil anahtarına bağlı olmalıdır.
@ SavasVedova'dan yorum:
Tablo tanımlarını veya örnek bir sorguyu görmeden açıklamanızı takip ettiğimden emin değilim, ancak Filters
her biri merkezi bir Products
tabloya başvuran yabancı bir anahtar içeren birden çok tablonuz var gibi görünüyor.
CREATE TABLE Products (
product_id INT PRIMARY KEY
);
CREATE TABLE FiltersType1 (
filter_id INT PRIMARY KEY,
product_id INT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
CREATE TABLE FiltersType2 (
filter_id INT PRIMARY KEY,
product_id INT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
...and other filter tables...
Hangi türe katılmak istediğinizi biliyorsanız, ürünleri belirli bir filtreye eklemek kolaydır:
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
Filtre türünün dinamik olmasını istiyorsanız, SQL sorgusunu oluşturmak için uygulama kodu yazmanız gerekir. SQL, sorguyu yazarken tablonun belirtilmesini ve sabitlenmesini gerektirir. Birleştirilen tablonun tek tek satırlarında bulunan değerlere göre dinamik olarak seçilmesini sağlayamazsınızProducts
.
Diğer tek seçenek dış birleştirmeler kullanarak tüm filtre tablolarına katılmaktır . Eşleşen product_id değeri olmayanlar yalnızca tek bir boş satır olarak döndürülür. Ancak yine de birleştirilmiş tüm tabloları kodlamanız gerekir ve yeni filtre tabloları eklerseniz, kodunuzu güncellemeniz gerekir.
SELECT * FROM Products
LEFT OUTER JOIN FiltersType1 USING (product_id)
LEFT OUTER JOIN FiltersType2 USING (product_id)
LEFT OUTER JOIN FiltersType3 USING (product_id)
...
Tüm filtre tablolarına katılmanın başka bir yolu da bunu seri olarak yapmaktır:
SELECT * FROM Product
INNER JOIN FiltersType1 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType3 USING (product_id)
...
Ancak bu biçim yine de tüm tablolara referanslar yazmanızı gerektirir. Bunun üstesinden gelmek yok.
join
hedef de değişecektir ...... Çok fazla mı karmaşıklaşıyorum ya da ne? Yardım!