问题描述
使用提供的模式,我想以某种方式强制每个显示都有唯一的reserved_seat:seat_id。换句话说,如果那个座位已经在显示中被预留,您将无法预留。
一种选择是,还向showing_seat中添加showing_id(这是多余的),然后对(showing_id,seat_id)进行唯一约束。
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,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)
)
解决方法
我相信这是使用代理键(auto_increment id's)而不是自然键使您误入歧途的罕见情况之一。考虑一下如果使用自然键,表定义的外观:
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)
)
现在,您可以将每个显示约束条件的保留席位添加为Reservation_seat上的备用键:
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,column),CONSTRAINT UC_seat_showing_reserved UNIQUE(showing_name,seat_column)
)
但是,这清楚表明主键是多余的,因为它只是我们添加的约束的较弱版本,因此我们应该用新约束替换它。
CREATE TABLE reservation_seat
(
showing_name VARCHAR(45) NOT NULL,column)
)
我们现在可能会担心,我们的Reservation_seat可能引用的showing_id与Reservation_seat本身不同的预订,但是对于自然键来说这不是问题,因为第一个外键引用可以防止这种情况。
现在我们要做的就是将其转换回代理键:
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,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定义改回唯一约束。与您原始的reservation_seat定义相比,我们最后添加了showing_id,但是通过修改后的更强的第一外键定义,我们现在确保Reservation_seat在显示中是唯一的,并且reservation_seat不能具有与其父预订不同的showing_id。
(注意:您可能必须在上面的SQL代码中引用“行”和“列”列名称)
附加说明: DBMS在此方面有所不同(在这种情况下,我不确定MySql),但是许多人将要求外键关系在目标上具有相应的主键或唯一约束(参考)表。这意味着您将不得不使用以下新约束来更改 reservation 表:
CONSTRAINT UC_showing_reserved UNIQUE(showing_id,id)
以匹配我上面建议的 reservation_seat 上的新FK定义:
FOREIGN KEY (showing_id,
从技术上讲,这将是一个多余的约束,因为它是保留表上主键的较弱版本,但是在这种情况下,SQL可能仍需要它来实现FK。
,指定“座位”是否需要90个字符?我熟悉的座位是“ 103-45”或“ J17”。甚至是“第4行43座位105”。您没有提到它,但是行/列不足以回答“这两个座位相邻吗?”的问题。
我第一个解决此问题的方法是摆脱桌子seat
,而不是能够枚举场地中的所有座位。
然后我要问问表reservation_seat
,它闻起来像是多对多映射(加上一个标志)。很多:很多意味着非唯一性。因此,必须有所作为。
未标准化的原始数据似乎是
showing: showing_id (PK),date,time,location
reservation: showing_id,seat,confirmed
(在reservation
上)可能会回答您的问题:
PRIMARY KEY(showing_id,seat)
它将两个表联系在一起,提供“自然” PK,并且仍然允许使用confirmed
标志。
我不知道您“确认”的逻辑。我认为您无法在等待确认的座位上重新分配座位?
回到我的初始评论。 seat 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
这无法照顾您有时想挡住阳台,从而使某些座位无效的场所。对于大多数信息来说,使用不同的venue
和id
可能是正确的方向。
请确保在适当的地方使用交易(BEGIN..COMMIT
和FOR UPDATE
)。
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
为处理座位邻接,我建议手动为每段相邻座位分配一个“条”号,该数字在过道,柱子等处停止。A对于中间位置为“ 1”的中间部分,这是不够的,偶数是一种方式,奇数是另一种方式。因此,尽管分选相距很远,但K-8和K-9相距很远,但K-8和K-10相近。
对于confirmed
,它“属于” reservation
。但是其他操作将其包含在seat
中可能会更方便。我们可能需要计算出SQL语句才能做出决定。另外,SQL语句对于确定要拥有哪个辅助INDEXes
是必需的。