在数据库中实现“Both,Either,or Null”要求

时间:2012-05-04 21:43:54

标签: oracle database-design

我要求网络应用程序声明用户应该能够上传指令文档(.pdf,.doc,.txt)或提供说明文本。用户可以上传文档并提供文本,或者他们可以做一个或另一个,但他们必须做某事(不可为空)。如何在数据库中设计?这会被视为完整的子类型(见下文)吗?

enter image description here 这是较大模式的一小部分,所以我刚刚发布了我认为对于这个特定问题所必需的内容。

4 个答案:

答案 0 :(得分:4)

Ypercube's answer很好,但实际上,这可以完全通过声明完整性来完成,同时保留单独的表。诀窍是将延迟的圆形FOREIGN KEY与一些创造性的非规范化结合起来:

enter image description here

CREATE TABLE Instruction (
    InstructionId INT PRIMARY KEY,
    TextId INT UNIQUE,
    DocumentId INT UNIQUE,
    CHECK (
        (TextId IS NOT NULL AND InstructionId = TextId)
        OR (DocumentId IS NOT NULL AND InstructionId = DocumentId)
    )
);

CREATE TABLE Text (
    InstructionId INT PRIMARY KEY,
    FOREIGN KEY (InstructionId) REFERENCES Instruction (TextId) ON DELETE CASCADE
);

CREATE TABLE Document (
    InstructionId INT PRIMARY KEY,
    FOREIGN KEY (InstructionId) REFERENCES Instruction (DocumentId) ON DELETE CASCADE
);

ALTER TABLE Instruction ADD FOREIGN KEY (TextId) REFERENCES Text DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE Instruction ADD FOREIGN KEY (DocumentId) REFERENCES Document DEFERRABLE INITIALLY DEFERRED;

插入文字的方式如下:

INSERT INTO Instruction (InstructionId, TextId) VALUES (1, 1);
INSERT INTO Text (InstructionId) VALUES (1);
COMMIT;

像这样插入文档:

INSERT INTO Instruction (InstructionId, DocumentId) VALUES (2, 2);
INSERT INTO Document (InstructionId) VALUES (2);
COMMIT;

并插入文字和文档,如下所示:

INSERT INTO Instruction (InstructionId, TextId, DocumentId) VALUES (3, 3, 3);
INSERT INTO Text (InstructionId) VALUES (3);
INSERT INTO Document (InstructionId) VALUES (3);
COMMIT;

但是,在提交时尝试单独插入失败

INSERT INTO Instruction (InstructionId, TextId) VALUES (4, 4);
COMMIT; -- Error (FOREIGN KEY violation).

尝试在提交时插入“不匹配类型”失败

INSERT INTO Document (InstructionId) VALUES (1);
COMMIT; -- Error (FOREIGN KEY violation).

当然,尝试将错误值插入指令失败(这次是在提交之前):

INSERT INTO Instruction (InstructionId, TextId) VALUES (5, 6); -- Error (CHECK violation).
INSERT INTO Instruction (InstructionId) VALUES (7); -- Error (CHECK violation).

答案 1 :(得分:2)

我认为仅凭陈述参照完整性无法做到这一点 - 如果您的设计有这3个单独的表,则不能这样做。

您必须确保所有插入/删除/更新操作都在执行此类要求的事务(存储过程)中完成 - 因此在没有相对行的情况下,不会在表Instruction中插入或保留任何行在其他两个表中的任何一个表中。


如果您不介意拥有可空字段,可以将3个表合并为一个并使用CHECK约束:

CREATE TABLE Instruction
( InstructionID INT          NOT NULL
, Text          VARCHAR(255) NULL
, Filepath      VARCHAR(255) NULL
, PRIMARY KEY (InstructionID)
, CONSTRAINT Instruction_has_either_text_or_document 
    CHECK (Text IS NOT NULL OR FilePath IS NOT NULL)
) ;

答案 2 :(得分:1)

如果用户提交了文本,您的应用程序是否可以将其另存为.txt文件?这样你只需要担心处理文件。

答案 3 :(得分:0)

这里有点感觉

  1. 此架构中没有UserID,因此应将其添加到 Instruction表。

  2. 如果用户没有上传任何内容,则(应该)没有条目 对于Instruction表中的该用户。

  3. 所以问题 - 如上所述 - 不是关于放置约束 在这三张桌子上。

  4. 加载此结构时,请使用存储过程和/或事务 - 以确保至少填充一个子记录。但是,这与用户必须上传内容的业务要求无关。