Jeg tror, at dette er et af de sjældne tilfælde, hvor brugen af surrogatnøgler (auto_increment id'er) i stedet for naturlige nøgler har ført dig på afveje. Overvej, hvordan dine tabeldefinitioner ville se ud, hvis du brugte naturlige nøgler i stedet:
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)
)
Nu kan du tilføje dit reserverede sæde pr. visningsbegrænsning som en alternativ nøgle på reservation_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)
)
Dette gør det dog klart, at den primære nøgle er overflødig, fordi den blot er en svagere version af den begrænsning, som vi har tilføjet, så vi bør erstatte den med vores nye begrænsning.
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)
)
Vi kan bekymre os nu, at vores reservation_seat kunne referere til en reservation med et andet showing_id end reservations_seat selv, men det er ikke et problem for naturlige nøgler, fordi den første fremmednøglereference forhindrer det.
Nu skal vi bare oversætte dette tilbage til surrogatnøgler:
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)
)
Fordi vi gør reservation_seat(id) til den primære nøgle, er vi nødt til at ændre den navngivne PK-definition tilbage til en unik begrænsning. Sammenlignet med din oprindelige reservations_seat-definition ender vi med at vise_id tilføjet, men med den modificerede stærkere første udenlandske nøgle-definition sikrer vi nu både, at reservation_seat er unikke inden for en showing, og at reservation_seat ikke kan have et showing_id, der er forskelligt fra dets overordnede reservation.
(Bemærk:du bliver sandsynligvis nødt til at citere kolonnenavnene 'række' og 'kolonne' i SQL-koden ovenfor)
Yderligere bemærkning: DBMS'er varierer på dette (og jeg er ikke sikker på MySql i dette tilfælde), men mange vil kræve, at en Foreign Key-relation har en tilsvarende Primary Key eller Unique Constraint på måltabellen (refereret). Dette ville betyde, at du bliver nødt til at ændre reservationen tabel med en ny begrænsning som:
CONSTRAINT UC_showing_reserved UNIQUE(showing_id, id)
for at matche den nye FK-definition på reservation_seat som jeg foreslog ovenfor:
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),
Teknisk set ville dette være en overflødig begrænsning, da det er en svagere version af den primære nøgle på reservationstabellen, men i dette tilfælde ville SQL sandsynligvis stadig kræve, at den implementerer FK.