Oracle 实施";两者,或其中一个,但不为空;数据库中的需求
我有一个web应用程序的要求,该应用程序声明用户应该能够上载说明文档(.pdf、.doc、.txt)或提供说明文本。用户可以上传文档并提供文本,也可以执行其中一项操作,但必须执行某些操作(不能为null)。如何在数据库中进行设计?是否将其视为完整的子类型(见下文)Oracle 实施";两者,或其中一个,但不为空;数据库中的需求,oracle,database-design,Oracle,Database Design,我有一个web应用程序的要求,该应用程序声明用户应该能够上载说明文档(.pdf、.doc、.txt)或提供说明文本。用户可以上传文档并提供文本,也可以执行其中一项操作,但必须执行某些操作(不能为null)。如何在数据库中进行设计?是否将其视为完整的子类型(见下文) 这是一个更大的模式的一小部分,所以我只是发布了我认为对于这个特定问题是必要的 如果用户提交了文本,您的应用程序能否将其保存为.txt文件?这样,您只需担心如何处理文件。我认为,单凭声明性引用完整性是无法做到这一点的——如果您的设计有
这是一个更大的模式的一小部分,所以我只是发布了我认为对于这个特定问题是必要的 如果用户提交了文本,您的应用程序能否将其保存为.txt文件?这样,您只需担心如何处理文件。我认为,单凭声明性引用完整性是无法做到这一点的——如果您的设计有这3个单独的表,就无法做到这一点 您必须确保所有的插入/删除/更新操作都是在执行此类要求的事务(存储过程)中完成的,因此,在表
指令中插入或保留的任何行都不会在其他两个表中的任何一个表中没有相对行
如果您不介意使用可为空的字段,可以将3个表合并为一个表,并使用检查约束:
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)
) ;
这里有点不舒服
此架构中没有UserID
,因此应将其添加到
说明
表格
如果用户不上传任何内容,则不会有任何条目
对于说明
表中的该用户
因此,正如前面所述,问题不在于设置约束
在这三张桌子上
加载此结构时,请使用存储过程和/或事务,以确保至少填充一个子记录。不过,这与用户必须上传内容的业务需求无关
这很好,但事实上,这完全可以通过声明完整性来完成,同时保留单独的表。诀窍是将延迟循环外键与一点创造性的非规范化结合起来:
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 (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 (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 (4, 4);
COMMIT; -- Error (FOREIGN KEY violation).
INSERT INTO Document (InstructionId) VALUES (1);
COMMIT; -- Error (FOREIGN KEY violation).
当然,尝试将错误值插入指令失败(这次是在提交之前):
这是一个很大的应用程序。所以我只是发布了这部分内容。有一个用户表和大约50个其他表我必须处理。您是为特定的目标DBMS设计的吗?我认为这将满足要求。队里的一位大四学生也提出了同样的建议。谢谢。+1我并不特别喜欢可延迟的FK,但很高兴知道它可以做到(以及如何做到)。如果您想在2个子表中添加其他字段,那么这当然很有用@布兰科:这在甲骨文和Postgres中也有效吗?@ypercube上面的测试是在甲骨文10.2上进行的。我对PostgreSQL不太确定,但我看不出有什么理由不能将此技术移植到任何支持延迟FKs的DBMS上。