Věřím, že toto je jeden z těch vzácných případů, kdy vás použití náhradních klíčů (auto_increment id) místo přirozených klíčů svedlo z omylu. Zvažte, jak by vypadaly definice vaší tabulky, kdybyste místo toho použili přirozené klíče:
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)
)
Nyní můžete přidat své rezervované sedadlo podle zobrazených omezení jako alternativní klíč na 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)
)
To však jasně ukazuje, že primární klíč je nadbytečný, protože je to jen slabší verze omezení, které jsme přidali, takže bychom ho měli nahradit naším novým omezením.
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)
)
Můžeme se nyní obávat, že by naše reservation_seat mohla odkazovat na rezervaci s jiným show_id než samotné reservation_seat, ale to není problém pro přirozené klíče, protože první reference na cizí klíč tomu brání.
Nyní vše, co musíme udělat, je převést to zpět do náhradních klíčů:
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)
)
Protože provádíme rezervaci_sedadlo(id) jako primární klíč, musíme pojmenovanou definici PK změnit zpět na jedinečné omezení. Ve srovnání s vaší původní definicí reservation_seat skončíme s přidaným id show_id, ale s upravenou definicí silnějšího prvního cizího klíče nyní zajišťujeme, že reservation_seat je v rámci představení jedinečná a že reservation_seat nemůže mít show_id odlišné od své rodičovské rezervace.
(Poznámka:Pravděpodobně budete muset uvést názvy sloupců „řádek“ a „sloupec“ ve výše uvedeném kódu SQL)
Další poznámka: DBMS se v tomto liší (a v tomto případě si nejsem jistý MySql), ale mnoho z nich bude vyžadovat, aby vztah cizího klíče měl odpovídající primární klíč nebo jedinečné omezení na cílové (odkazované) tabulce. To by znamenalo, že byste museli změnit rezervaci tabulka s novým omezením jako:
CONSTRAINT UC_showing_reserved UNIQUE(showing_id, id)
aby odpovídaly nové definici FK na rezervaci_sedadla které jsem navrhl výše:
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),
Technicky by to bylo redundantní omezení, protože se jedná o slabší verzi primárního klíče v rezervační tabulce, ale v tomto případě by jej SQL pravděpodobně stále vyžadoval k implementaci FK.