Вярвам, че това е един от онези редки случаи, в които използването на сурогатни ключове (auto_increment id's) вместо естествени ключове ви е подвело. Помислете как биха изглеждали дефинициите на вашите таблици, ако вместо това използвате естествени ключове:
CREATE TABLE showing
(
name VARCHAR(45) NOT NULL, -- globally unique
PRIMARY KEY (name)
)
CREATE TABLE reservation
(
showing_name VARCHAR(45) NOT NULL,
name VARCHAR(45) NOT NULL, -- only unique within showing_name
PRIMARY KEY (name, showing_name),
FOREIGN KEY (showing_name) REFERENCES showing(name)
)
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,
reservation_name VARCHAR(45) NOT NULL,
seat_row VARCHAR(45) NOT NULL,
seat_column VARCHAR(45) NOT NULL,
confirmed TINYINT,
PRIMARY KEY (showing_name, reservation_name, seat_row, seat_column),
FOREIGN KEY (showing_name, reservation_name) REFERENCES reservation(showing_name, name),
FOREIGN KEY (seat_row, seat_column) REFERENCES seat(row, column)
)
Сега можете да добавите запазеното си място според ограничението за показване като алтернативен ключ на reserve_seat:
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,
reservation_name VARCHAR(45) NOT NULL,
seat_row VARCHAR(45) NOT NULL,
seat_column VARCHAR(45) NOT NULL,
confirmed TINYINT,
PRIMARY KEY (showing_name, reservation_name, seat_row, seat_column),
FOREIGN KEY (showing_name, reservation_name) REFERENCES reservation(showing_name, name),
FOREIGN KEY (seat_row, seat_column) REFERENCES seat(row, column),
CONSTRAINT UC_seat_showing_reserved UNIQUE(showing_name, seat_row, seat_column)
)
Това обаче изяснява, че първичният ключ е излишен, защото е просто по-слаба версия на ограничението, което добавихме, така че трябва да го заменим с нашето ново ограничение.
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,
reservation_name VARCHAR(45) NOT NULL,
seat_row VARCHAR(45) NOT NULL,
seat_column VARCHAR(45) NOT NULL,
confirmed TINYINT,
PRIMARY KEY (showing_name, seat_row, seat_column),
FOREIGN KEY (showing_name, reservation_name) REFERENCES reservation(showing_name, name),
FOREIGN KEY (seat_row, seat_column) REFERENCES seat(row, column)
)
Сега може да се тревожим, че нашият reserve_seat може да препраща към резервация с различен showing_id от самия reserve_seat, но това не е проблем за естествените ключове, защото първата препратка към външен ключ предотвратява това.
Сега всичко, което трябва да направим, е да преведем това обратно в сурогатни ключове:
CREATE TABLE reservation_seat
(
id INT NOT NULL AUTO_INCREMENT,
showing_id INT NOT NULL,
reservation_id INT NOT NULL,
seat_id INT NOT NULL,
confirmed TINYINT,
PRIMARY KEY (id),
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),
FOREIGN KEY (seat_id) REFERENCES seat(id),
CONSTRAINT UC_seat_showing_reserved UNIQUE(showing_id, seat_id)
)
Тъй като правим reserve_seat(id) първичен ключ, трябва да променим наименуваната PK дефиниция обратно в уникално ограничение. В сравнение с оригиналната ви дефиниция на reserve_seat, ние завършваме с добавен showing_id, но с модифицираната по-силна дефиниция на първия външен ключ сега гарантираме, че reserve_seat са уникални в рамките на показване и че reserve_seat не може да има showing_id, различен от своята родителска резервация.
(Забележка:вероятно ще трябва да цитирате имената на колоните „ред“ и „колона“ в SQL кода по-горе)
Допълнителна забележка: СУБД се различават по този въпрос (и не съм сигурен за MySql в този случай), но много от тях ще изискват релацията на външен ключ да има съответен първичен ключ или уникално ограничение на целевата (реферирана) таблица. Това би означавало, че ще трябва да промените резервацията таблица с ново ограничение като:
CONSTRAINT UC_showing_reserved UNIQUE(showing_id, id)
за да съответства на новата дефиниция на FK на reservation_seat което предложих по-горе:
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),
Технически, това би било излишно ограничение, тъй като е по-слаба версия на първичния ключ на таблицата за резервации, но в този случай SQL вероятно все пак ще го изисква, за да имплементира FK.