Ich glaube, dass dies einer der seltenen Fälle ist, in denen die Verwendung von Ersatzschlüsseln (auto_increment-IDs) anstelle von natürlichen Schlüsseln Sie in die Irre geführt hat. Überlegen Sie, wie Ihre Tabellendefinitionen aussehen würden, wenn Sie stattdessen natürliche Schlüssel verwenden würden:
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)
)
Jetzt können Sie Ihren reservierten Sitzplatz per Anzeigebeschränkung als alternativen Schlüssel auf reservation_seat hinzufügen:
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)
)
Dies macht jedoch deutlich, dass der Primärschlüssel überflüssig ist, da er nur eine schwächere Version der von uns hinzugefügten Einschränkung ist, also sollten wir ihn durch unsere neue Einschränkung ersetzen.
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)
)
Wir könnten uns jetzt Sorgen machen, dass unser reservation_seat auf eine Reservierung mit einer anderen show_id als der reservation_seat selbst verweisen könnte, aber das ist kein Problem für natürliche Schlüssel, da die erste Fremdschlüsselreferenz dies verhindert.
Jetzt müssen wir dies nur noch in Ersatzschlüssel zurückübersetzen:
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)
)
Da wir die reservation_seat(id) zum Primärschlüssel machen, müssen wir die benannte PK-Definition wieder in eine eindeutige Einschränkung ändern. Im Vergleich zu Ihrer ursprünglichen Reservierungsplatz-Definition haben wir am Ende die Anzeige_ID hinzugefügt, aber mit der modifizierten, stärkeren ersten Fremdschlüsseldefinition stellen wir jetzt sicher, dass der Reservierungsplatz innerhalb einer Vorführung eindeutig ist und dass der Reservierungsplatz keine andere Vorführungs-ID als die übergeordnete Reservierung haben kann.
(Hinweis:Sie müssen wahrscheinlich die Spaltennamen „Zeile“ und „Spalte“ im obigen SQL-Code in Anführungszeichen setzen)
Zusätzlicher Hinweis: DBMSs unterscheiden sich diesbezüglich (und ich bin mir in diesem Fall nicht sicher, was MySql angeht), aber viele erfordern, dass eine Fremdschlüsselbeziehung einen entsprechenden Primärschlüssel oder eine eindeutige Einschränkung für die Zieltabelle (referenziert) hat. Dies würde bedeuten, dass Sie die Reservierung ändern müssten Tabelle mit einer neuen Einschränkung wie:
CONSTRAINT UC_showing_reserved UNIQUE(showing_id, id)
um der neuen FK-Definition auf reservation_seat zu entsprechen die ich oben vorgeschlagen habe:
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),
Technisch gesehen wäre dies eine redundante Einschränkung, da es sich um eine schwächere Version des Primärschlüssels in der Reservierungstabelle handelt, aber in diesem Fall würde SQL ihn wahrscheinlich immer noch benötigen, um den FK zu implementieren.