สิ่งที่คุณกำลังอธิบายเรียกว่าสมาคมโพลีมอร์ฟิค นั่นคือคอลัมน์ "foreign key" มีค่า id ที่ต้องมีอยู่ในหนึ่งในชุดของตารางเป้าหมาย โดยทั่วไปแล้วตารางเป้าหมายจะเกี่ยวข้องกันในบางวิธีเช่นเป็นอินสแตนซ์ของซูเปอร์คลาสทั่วไปของข้อมูล คุณต้องการคอลัมน์อื่นที่อยู่ด้านข้างคอลัมน์คีย์ต่างประเทศเพื่อให้ในแต่ละแถวคุณสามารถกำหนดตารางเป้าหมายที่อ้างอิงได้
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
);
ไม่มีวิธีใดในการสร้างแบบจำลอง Polymorphic Associations โดยใช้ข้อ จำกัด ของ SQL ข้อ จำกัด foreign key อ้างอิงตารางเป้าหมายหนึ่งตารางเสมอ
Polymorphic Association ได้รับการสนับสนุนโดยเฟรมเวิร์กเช่น Rails และ Hibernate แต่พวกเขาบอกอย่างชัดเจนว่าคุณต้องปิดการใช้งานข้อ จำกัด ของ SQL เพื่อใช้คุณสมบัตินี้ แอปพลิเคชันหรือกรอบงานจะต้องทำงานเทียบเท่าเพื่อให้มั่นใจว่าการอ้างอิงนั้นเป็นที่น่าพอใจ นั่นคือค่าใน foreign key มีอยู่ในหนึ่งในตารางเป้าหมายที่เป็นไปได้
Polymorphic Associations นั้นอ่อนแอเมื่อเทียบกับการบังคับใช้ความสอดคล้องของฐานข้อมูล ความถูกต้องของข้อมูลขึ้นอยู่กับไคลเอนต์ทั้งหมดที่เข้าถึงฐานข้อมูลด้วยตรรกะความสมบูรณ์ของ Referential เดียวกันที่บังคับใช้และการบังคับใช้ต้องปราศจากข้อบกพร่อง
ต่อไปนี้เป็นโซลูชันทางเลือกบางตัวที่ใช้ประโยชน์จาก integrity Referential ที่บังคับใช้ฐานข้อมูล:
สร้างตารางพิเศษหนึ่งตารางต่อเป้าหมาย ตัวอย่างเช่นpopular_states
และpopular_countries
ซึ่งการอ้างอิงstates
และcountries
ตามลำดับ แต่ละตาราง "ยอดนิยม" เหล่านี้ยังอ้างอิงโปรไฟล์ของผู้ใช้
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),
);
นี่หมายความว่าการรับสถานที่โปรดยอดนิยมของผู้ใช้ทั้งหมดคุณต้องสอบถามทั้งสองตารางนี้ แต่หมายความว่าคุณสามารถพึ่งพาฐานข้อมูลเพื่อบังคับใช้ความสอดคล้อง
สร้างplaces
ตารางเป็น supertable ในฐานะที่เป็นแอบี้กล่าวเป็นทางเลือกที่สองคือการที่ได้รับความนิยมของสถานที่อ้างอิงตารางเช่นplaces
ซึ่งเป็นผู้ปกครองทั้งสองและstates
countries
นั่นคือทั้งรัฐและประเทศยังมีความสำคัญต่างประเทศไปplaces
(คุณยังสามารถทำให้ต่างประเทศที่สำคัญนี้ยังเป็นคีย์หลักของstates
และcountries
)
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)
);
ใช้สองคอลัมน์ แทนที่จะเป็นหนึ่งคอลัมน์ที่อาจอ้างอิงหนึ่งในสองตารางเป้าหมายให้ใช้สองคอลัมน์ สองคอลัมน์นี้อาจเป็นNULL
; 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)
);
ในแง่ของทฤษฎีเชิงสัมพันธ์สมาคมโพลีมอร์ฟิคละเมิดรูปแบบปกติครั้งแรกเพราะpopular_place_id
คอลัมน์มีผลกับความหมายสองประการนั่นคือเป็นรัฐหรือประเทศ คุณจะไม่เก็บบุคคลage
และบุคคลphone_number
ในคอลัมน์เดียวและด้วยเหตุผลเดียวกับที่คุณไม่ควรจัดเก็บทั้งสองstate_id
และcountry_id
ในคอลัมน์เดียว ความจริงที่ว่าแอตทริบิวต์ทั้งสองนี้มีชนิดข้อมูลที่เข้ากันได้นั้นเป็นเรื่องบังเอิญ พวกเขายังคงมีความหมายที่แตกต่างกันนิติบุคคล
สมาคม Polymorphic ก็ละเมิดเช่นกัน ฟอร์มปกติที่สามด้วยเนื่องจากความหมายของคอลัมน์ขึ้นอยู่กับคอลัมน์พิเศษที่กำหนดชื่อตารางซึ่งคีย์ต่างประเทศอ้างอิง ในฟอร์มปกติที่สามแอตทริบิวต์ในตารางจะต้องขึ้นอยู่กับคีย์หลักของตารางนั้นเท่านั้น
ความคิดเห็นอีกครั้งจาก @SavasVedova:
ฉันไม่แน่ใจว่าฉันทำตามคำอธิบายของคุณโดยไม่เห็นคำจำกัดความของตารางหรือแบบสอบถามตัวอย่าง แต่ดูเหมือนว่าคุณมีหลายFilters
ตารางโดยแต่ละอันมีคีย์ต่างประเทศที่อ้างอิงถึงProducts
ตารางกลาง
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...
การเข้าร่วมผลิตภัณฑ์กับตัวกรองประเภทใดประเภทหนึ่งนั้นเป็นเรื่องง่ายถ้าคุณรู้ว่าคุณต้องการเข้าร่วมประเภทใด:
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
ถ้าคุณต้องการให้ตัวกรองเป็นแบบไดนามิกคุณต้องเขียนรหัสแอปพลิเคชันเพื่อสร้างแบบสอบถาม SQL SQL ต้องการให้ระบุตารางและแก้ไขในเวลาที่คุณเขียนแบบสอบถาม คุณไม่สามารถเลือกตารางที่เข้าร่วมได้แบบไดนามิกโดยยึดตามค่าที่พบในแต่ละแถวของProducts
คุณไม่สามารถทำให้ตารางเข้าร่วมจะได้รับเลือกแบบไดนามิกขึ้นอยู่กับค่าที่พบในแต่ละแถวของ
ตัวเลือกอื่น ๆ เท่านั้นคือการเข้าร่วมกับตารางตัวกรองทั้งหมดโดยใช้การรวมภายนอก ผู้ที่ไม่มี product_id ที่ตรงกันจะถูกส่งคืนเป็น null แถวเดียว แต่คุณยังคงต้องฮาร์ดโค้ดตารางที่เข้าร่วมทั้งหมดและหากคุณเพิ่มตารางตัวกรองใหม่คุณต้องอัปเดตรหัสของคุณ
SELECT * FROM Products
LEFT OUTER JOIN FiltersType1 USING (product_id)
LEFT OUTER JOIN FiltersType2 USING (product_id)
LEFT OUTER JOIN FiltersType3 USING (product_id)
...
อีกวิธีในการเข้าร่วมกับตารางตัวกรองทั้งหมดคือการทำมันตามลำดับ:
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)
...
แต่รูปแบบนี้ยังต้องการให้คุณเขียนการอ้างอิงไปยังตารางทั้งหมด ไม่มีการรับรอบที่
join
เป้าหมายจะเปลี่ยนแปลงเช่นกัน ...... ฉันยุ่งมากเกินไปหรือเปล่า? ช่วยด้วย!