杰里米·托德提供good answer(+1给他),我只想做一个额外的点...
是“当前”同样的事情,“最新”?如果是的话,那么你可以使用一个标识的关系,得到的复合键自然模型:
所有同一页的修订具有相同的PageRevision.PageId
,并在页面内的历史顺序确定由整数RevNo
。最新版本是其各自页面中最高版本RevNo
。
由于InnoDB tables are clustered,此结构将同一页面的修订版本组合在一起。检索页面的所有修订版本可能会比原始结构更快,并且只检索最新修订版本的速度会更快。
数据修改也会更快,因为我们有一个较少的索引。
我可能会下降Page.CurrentRevisions并添加PageRevisions。isCurrent,但我不喜欢这种设计将允许一个页面的多个版本被标记为当前
不是我会推荐这种方法,但“当前”标志的唯一性可以以声明方式执行,只需使用NULL,而不是假的:
CREATE TABLE PageRevision (
RevId INT PRIMARY KEY,
PageId INT NOT NULL,
IsCurrent BIT CHECK (IsCurrent IS NULL OR (IsCurrent IS NOT NULL AND IsCurrent = 1)),
UNIQUE (PageId, IsCurrent)
);
-- You can insert several "non current" revisions for the same page.
INSERT INTO PageRevision VALUES (1, 1, NULL);
INSERT INTO PageRevision VALUES (2, 1, NULL);
INSERT INTO PageRevision VALUES (3, 1, NULL);
-- You can insert one "current" revision in one page.
INSERT INTO PageRevision VALUES (4, 1, 1);
-- Or another "current" revision in a different page.
INSERT INTO PageRevision VALUES (5, 2, 1);
-- But not the second "current" revision in the same page.
-- The following violates the UNIQUE constraint:
INSERT INTO PageRevision VALUES (6, 1, 1);
注:MySQL的解析,但不会强制执行上面的CHECK约束。因此,除了每个页面有一个(有用的)标志之外,每页还可能有一个(不需要的)错误标志。
注2:由于peculiar nature of NULL,上面的CHECK可以简写为:CHECK (IsCurrent = 1)
。当该标志为0时,该表达式为false,并且CHECK按预期失败。如果该标志为1,则该表达式为真并且CHECK通过。如果标志为NULL,则表达式为NULL,并且检查通过(与将NULL视为false的WHERE不同)。但是我更喜欢在处理NULL时比这更明确。
非常感谢。你认为这个问题和MySQL缺乏对DEFERRED CONSTRAINT的支持是否表明我选择了糟糕的设计?我想知道是否应该创建第三张表来定义Page和PageRevision之间的关系。 – user2045006
不,我认为在您的CurrentRevisions列中允许空值并依赖业务逻辑来保持事物一致性在这种情况下是完全没问题的。中小型应用程序最近的趋势,特别是像ORM框架这样的事情,无论如何都要依赖业务规则来强制执行数据完整性。如果可能的话,确保将所有内容都包含在交易中。 –
我还应该补充说,即使允许使用空值,仍然可以使用外键约束,因此验证和级联删除等操作仍然可行。唯一的危险是你在'Page'表中有一个空的当前版本的记录的时间很短,但如果你使用一个事务,这是一个非问题。 –