sql >> Databáze >  >> RDS >> Mysql

Vynutit složené jedinečné omezení, které závisí na hodnotě nadřazeného sloupce

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.



  1. Najděte poslední změny objektů v databázi SQL Server

  2. Přijdou mi nějaké změny, když nahradím spouštěč Oracle, když je moje aplikace spuštěna?

  3. Import JSON do Mysql

  4. Jak mohu dosáhnout stejného chování řazení Postgres v Linuxu jako v Mac OS?