Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Aplicar restrição exclusiva composta que depende do valor da coluna pai


Acredito que este seja um daqueles raros casos em que o uso de chaves substitutas (auto_increment ids) em vez de chaves naturais o levou ao erro. Considere como suas definições de tabela ficariam se você usasse chaves naturais:
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)
)

Agora você pode adicionar seu assento reservado por restrição de exibição como uma chave alternativa em 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)
)

No entanto, isso deixa claro que a chave primária é supérflua porque é apenas uma versão mais fraca da restrição que adicionamos, portanto, devemos substituí-la por nossa nova restrição.
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)
)

Podemos nos preocupar agora que nosso reservation_seat possa estar referenciando uma reserva com um show_id diferente do próprio reservation_seat, mas isso não é um problema para chaves naturais porque a primeira referência de chave estrangeira impede isso.

Agora tudo o que precisamos fazer é traduzir isso de volta em chaves substitutas:
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)
)

Como estamos tornando o reservation_seat(id) a chave primária, precisamos alterar a definição de PK nomeada de volta para uma restrição exclusiva. Em comparação com sua definição de reservation_seat original, acabamos com a adição de show_id, mas com a primeira definição de chave estrangeira mais forte modificada, agora asseguramos que reservation_seat seja único em uma exibição e que reservation_seat não possa ter um show_id diferente de sua reserva pai.

(Nota:você provavelmente terá que citar os nomes das colunas 'linha' e 'coluna' no código SQL acima)

Observação adicional: Os DBMSs variam nisso (e não tenho certeza sobre o MySql neste caso), mas muitos exigirão que uma relação de chave estrangeira tenha uma chave primária ou restrição exclusiva correspondente na tabela de destino (referenciada). Isso significaria que você teria que alterar a reserva table com uma nova restrição como:
CONSTRAINT UC_showing_reserved UNIQUE(showing_id, id)

para corresponder à nova definição FK em reservation_seat que sugeri acima:
FOREIGN KEY (showing_id, reservation_id) REFERENCES reservation(showing_id, id),

Tecnicamente, isso seria uma restrição redundante, pois é uma versão mais fraca da chave primária na tabela de reserva, mas, nesse caso, o SQL provavelmente ainda exigiria a implementação do FK.