Mysql 强制执行依赖于父列值的复合唯一约束

Mysql 强制执行依赖于父列值的复合唯一约束,mysql,sql,constraints,database-normalization,unique-constraint,Mysql,Sql,Constraints,Database Normalization,Unique Constraint,通过提供的模式,我希望以某种方式强制每个放映都有唯一的保留座位:座位id。换句话说,如果某个特定的座位已经在那个节目中预订了,你就不能预订 一个选项是将showing_id添加到reservation_seat(这是多余的),然后对(showing_id,seat_id)进行唯一约束 这可以在sql中完成,还是由应用程序代码完成 DDL: CREATE TABLE showing ( id INT NOT NULL AUTO_INCREMENT,

通过提供的模式,我希望以某种方式强制每个放映都有唯一的保留座位:座位id。换句话说,如果某个特定的座位已经在那个节目中预订了,你就不能预订

一个选项是将showing_id添加到reservation_seat(这是多余的),然后对(showing_id,seat_id)进行唯一约束

这可以在sql中完成,还是由应用程序代码完成

DDL:

CREATE TABLE showing
(
    id              INT  NOT NULL  AUTO_INCREMENT,
    name            VARCHAR(45) NOT NULL,
    PRIMARY KEY (id)
)

CREATE TABLE reservation
(
    id              INT  NOT NULL  AUTO_INCREMENT,
    showing_id      INT  NOT NULL,
    PRIMARY KEY (id),
    FOREIGN KEY (showing_id) REFERENCES showing(id)
)

CREATE TABLE reservation_seat
(
    id              INT  NOT NULL  AUTO_INCREMENT,
    reservation_id  INT  NOT NULL,
    seat_id         INT  NOT NULL,
    confirmed       TINYINT,
    PRIMARY KEY (id),
    FOREIGN KEY (reservation_id) REFERENCES reservation(id),
    FOREIGN KEY (seat_id) REFERENCES seat(id)
)

CREATE TABLE seat
(
    id              INT  NOT NULL  AUTO_INCREMENT,
    row             VARCHAR(45) NOT NULL,
    column          VARCHAR(45) NOT NULL,
    PRIMARY KEY (id)
)

指定“座位”需要90个字符吗?我熟悉的座位是103-45或J17。甚至“第4节第43排第105座”。您没有提到,但行/列不足以回答“这两个座位相邻吗?”

我解决这个问题的第一个方法是去掉表
座位
,而不是能够列举一个场馆中的所有座位

然后我会询问表
reservation\u seat
,它闻起来像是一个多对多映射(加上一个标志)。多:多意味着非唯一性。所以,必须要有所付出

原始的、未规范化的数据似乎是

showing:  showing_id (PK), date, time, location
reservation:  showing_id, seat, confirmed
(关于
预订
)这可能回答了您的问题:

PRIMARY KEY(showing_id, seat)
它将两个表绑定在一起,提供“自然”PK,并且仍然允许使用
确认
标志

我不知道你“确认”的逻辑。我想您不能在等待确认时重新分配座位

回到我的开始评论<代码>座椅VARCHAR(15)可能合适。如果你需要的话,另一张桌子也可以

CREATE TABLE venue (
    venue_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name VARCHAR (144) NOT NULL,
    location ...
    capacity SMALLINT UNSIGNED NOT NULL,
    ...
    PRIMARY KEY(venue_id)
) ENGINE=InnoDB

CREATE TABLE seat (
    venue_id SMALLINT UNSIGNED NOT NULL,
    seat_num VARCHAR(30) NOT NULL,
    is_handicap ...,
    strip_num SMALLINT UNSIGNED NOT NULL,  -- see below
    PRIMARY KEY(venue_id, seat_num)
) ENGINE=InnoDB
这并没有考虑到你有时想要挡住阳台的场地,从而使一些座位无效。拥有不同的
场馆
id
以及大部分信息,这可能是应该采取的方向

确保在适当的情况下使用事务(
BEGIN..COMMIT
进行更新

CREATE TABLE showing (
    showing_id MEDIUM UNSIGNED NOT NULL AUTO_INCREMENT,
    venue_id SMALLINT UNSIGNED NOT NULL,
    date ...
    notes ...
    PRIMARY KEY(showing_id)
) ENGINE=InnoDB

为了处理座位邻接,我建议手动分配相邻座位的每一个“条”号,这些号码在过道、柱子等处停下,唉,这对于中间座位“1”在中间是不够的,偶数是单向的,而另一个是单向的。所以K-8和K-9相距很远,但K-8和K-10是相邻的,尽管它们相距很远


至于
确认
,它“属于”预订。但是,对于其他操作来说,将其放在
位置可能更方便。我们可能需要计算SQL语句来做出决定。此外,SQL语句对于决定要有哪些次要
索引是必需的。

指定“座位”需要90个字符吗?我熟悉的座位是103-45或J17。甚至“第4节第43排第105座”。您没有提到,但行/列不足以回答“这两个座位相邻吗?”

我解决这个问题的第一个方法是去掉表
座位
,而不是能够列举一个场馆中的所有座位

然后我会询问表
reservation\u seat
,它闻起来像是一个多对多映射(加上一个标志)。多:多意味着非唯一性。所以,必须要有所付出

原始的、未规范化的数据似乎是

showing:  showing_id (PK), date, time, location
reservation:  showing_id, seat, confirmed
(关于
预订
)这可能回答了您的问题:

PRIMARY KEY(showing_id, seat)
它将两个表绑定在一起,提供“自然”PK,并且仍然允许使用
确认
标志

我不知道你“确认”的逻辑。我想您不能在等待确认时重新分配座位

回到我的开始评论<代码>座椅VARCHAR(15)
可能合适。如果你需要的话,另一张桌子也可以

CREATE TABLE venue (
    venue_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name VARCHAR (144) NOT NULL,
    location ...
    capacity SMALLINT UNSIGNED NOT NULL,
    ...
    PRIMARY KEY(venue_id)
) ENGINE=InnoDB

CREATE TABLE seat (
    venue_id SMALLINT UNSIGNED NOT NULL,
    seat_num VARCHAR(30) NOT NULL,
    is_handicap ...,
    strip_num SMALLINT UNSIGNED NOT NULL,  -- see below
    PRIMARY KEY(venue_id, seat_num)
) ENGINE=InnoDB
这并没有考虑到你有时想要挡住阳台的场地,从而使一些座位无效。拥有不同的
场馆
id
以及大部分信息,这可能是应该采取的方向

确保在适当的情况下使用事务(
BEGIN..COMMIT
进行更新

CREATE TABLE showing (
    showing_id MEDIUM UNSIGNED NOT NULL AUTO_INCREMENT,
    venue_id SMALLINT UNSIGNED NOT NULL,
    date ...
    notes ...
    PRIMARY KEY(showing_id)
) ENGINE=InnoDB

为了处理座位邻接,我建议手动分配相邻座位的每一个“条”号,这些号码在过道、柱子等处停下,唉,这对于中间座位“1”在中间是不够的,偶数是单向的,而另一个是单向的。所以K-8和K-9相距很远,但K-8和K-10是相邻的,尽管它们相距很远


至于
确认
,它“属于”预订
。但是,对于其他操作来说,将其放在
位置可能更方便。我们可能需要计算SQL语句来做出决定。此外,SQL语句对于决定要有哪些次要
索引是必需的。

我认为这是使用代理键(自动增量id)而不是自然键导致误入歧途的极少数情况之一。如果您使用了自然键,请考虑表定义如何:

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)
)
现在,您可以根据显示约束将您的预订座位添加为预订座位上的备用键:

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)
)
但是,这表明主键是多余的,因为它只是我们添加的约束的一个较弱版本,所以我们应该用新约束替换它

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)
)
现在我们可能会担心,我们的预订座位可能引用了一个与预订座位本身具有不同显示id的预订,但这对于自然键不是问题,因为第一个外键引用阻止了这一点

现在我们需要做的就是将其转换回代理键:

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)
)
因为我们将reservation_seat(id)作为主键,所以必须将命名的PK定义更改回唯一约束。与您最初的预订座位定义相比,我们最终添加了显示id,但修改了更强的第一个外国k