Thiết kế hiện tại của bạn được gọi là vòng cung độc quyền nơi sets
bảng có hai khóa ngoại và cần chính xác một trong số chúng không phải là null. Đây là một cách để triển khai các liên kết đa hình, vì một khóa ngoại nhất định chỉ có thể tham chiếu đến một bảng đích.
Một giải pháp khác là tạo một "bảng xếp hạng cao" chung cho cả users
và schools
tham chiếu, và sau đó sử dụng nó làm cha của sets
.
create table set_owner
create table users
PK is also FK --> set_owner
create table schools
PK is also FK --> set_owner
create table sets
FK --> set_owner
Bạn có thể coi điều này tương tự như một giao diện trong mô hình OO:
interface SetOwner { ... }
class User implements SetOwner { ... }
class School implements SetOwner { ... }
class Set {
SetOwner owner;
}
Re nhận xét của bạn:
Để bảng SetOwners tạo các giá trị id. Bạn phải chèn vào SetOwners trước khi có thể chèn vào Người dùng hoặc Trường học. Vì vậy, hãy đặt id trong Người dùng và Trường học không tự động tăng dần; chỉ sử dụng giá trị được tạo bởi SetOwners:
INSERT INTO SetOwners DEFAULT VALUES; -- generates an id
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location');
Bằng cách này, sẽ không có giá trị id đã cho nào được sử dụng cho cả trường học và người dùng.
Bạn chắc chắn có thể làm được điều này. Trên thực tế, có thể có các cột khác chung cho cả Người dùng và Trường học, và bạn có thể đặt các cột này trong SetOwners siêu bảng. Điều này nằm trong Kế thừa bảng lớp của Martin Fowler mẫu.
Bạn cần phải tham gia. Nếu bạn đang truy vấn từ một Tập hợp nhất định và bạn biết nó thuộc về người dùng (không phải trường học), bạn có thể bỏ qua việc tham gia SetOwners và tham gia trực tiếp với Người dùng. Tham gia không nhất thiết phải thực hiện bằng khóa ngoại.
SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ...
Nếu bạn không biết liệu một tập hợp nhất định thuộc về Người dùng hay Trường học, bạn phải thực hiện kết hợp bên ngoài với cả hai:
SELECT COALESCE(u.name, sc.name) AS name
FROM Sets s
LEFT OUTER JOIN Users u ON s.SetOwner_id = u.id
LEFT OUTER JOIN Schools sc ON s.SetOwner_id = sc.id
WHERE ...
Bạn biết rằng SetOwner_id phải khớp với một hoặc bảng khác, Người dùng hoặc Trường học, nhưng không phải cả hai.