Mysql
 sql >> Base de données >  >> RDS >> Mysql

Appliquer une contrainte unique composite qui dépend de la valeur de la colonne parent

Je crois que c'est l'un de ces rares cas où l'utilisation de clés de substitution (identifiants auto_increment) au lieu de clés naturelles vous a induit en erreur. Considérez à quoi ressembleraient vos définitions de table si vous utilisiez plutôt des clés naturelles :

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)
)

Vous pouvez maintenant ajouter votre siège réservé par contrainte d'affichage en tant que clé alternative sur 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)
)

Cependant, cela indique clairement que la clé primaire est superflue car il s'agit simplement d'une version plus faible de la contrainte que nous avons ajoutée, nous devons donc la remplacer par notre nouvelle contrainte.

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)
)

Nous craignons peut-être maintenant que notre reservation_seat fasse référence à une réservation avec un coming_id différent du reservation_seat lui-même, mais ce n'est pas un problème pour les clés naturelles car la première référence de clé étrangère l'empêche.

Il ne nous reste plus qu'à retraduire ceci en clés de substitution :

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)
)

Parce que nous faisons de reservation_seat(id) la clé primaire, nous devons reconvertir la définition PK nommée en une contrainte unique. Par rapport à votre définition d'origine de reservation_seat, nous nous retrouvons avec l'ajout de l'identifiant de l'affichage, mais avec la première définition de clé étrangère plus forte et modifiée, nous assurons désormais à la fois que l'identifiant de la réservation_seat est unique au sein d'une diffusion et que l'identifiant de la réservation ne peut pas être différent de celui de sa réservation parente.

(Remarque :vous devrez probablement citer les noms de colonne 'ligne' et 'colonne' dans le code SQL ci-dessus)

Remarque supplémentaire : Les SGBD varient sur ce point (et je ne suis pas sûr de MySql dans ce cas), mais beaucoup exigeront qu'une relation de clé étrangère ait une clé primaire ou une contrainte unique correspondante sur la table cible (référencée). Cela signifierait que vous devriez modifier la réservation table avec une nouvelle contrainte comme :

CONSTRAINT UC_showing_reserved UNIQUE(showing_id, id)

pour correspondre à la nouvelle définition FK sur reservation_seat que j'ai suggéré ci-dessus :

FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),

Techniquement, ce serait une contrainte redondante puisqu'il s'agit d'une version plus faible de la clé primaire sur la table de réservation, mais dans ce cas, SQL en aurait probablement encore besoin pour implémenter le FK.