傑里米·託德提供good answer(+1給他),我只想做一個額外的點...
是「當前」同樣的事情,「最新」?如果是的話,那麼你可以使用一個標識的關係,得到的複合鍵自然模型:
![enter image description here](https://i.stack.imgur.com/iINYv.png)
所有同一頁的修訂具有相同的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'表中有一個空的當前版本的記錄的時間很短,但如果你使用一個事務,這是一個非問題。 –