我需要一个网络应用程序,该应用程序规定用户应该能够上传说明文档(.pdf、.doc、.txt)或提供说明文本。用户可以上传文档并提供文本,或者他们可以做一个或另一个,但他们必须做一些事情(不可为空)。这将如何在数据库中设计?这会被认为是一个完整的子类型(见下文)吗?
这是更大模式的一小部分,所以我只是发布了我认为对于这个特定问题有必要的内容。
我需要一个网络应用程序,该应用程序规定用户应该能够上传说明文档(.pdf、.doc、.txt)或提供说明文本。用户可以上传文档并提供文本,或者他们可以做一个或另一个,但他们必须做一些事情(不可为空)。这将如何在数据库中设计?这会被认为是一个完整的子类型(见下文)吗?
这是更大模式的一小部分,所以我只是发布了我认为对于这个特定问题有必要的内容。
Ypercube 的回答很好,但事实上,这可以纯粹通过声明完整性来完成,同时保留单独的表格。诀窍是将延迟循环外键与一些创造性的非规范化结合起来:
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).
I think that this cannot be done with Declarative Referential Integrity alone - not if your design has these 3 separate tables.
You'll have to ensure that all Insert/Delete/Update operations are done within transactions (stored procedures) that enforce such a requirement - so no row is ever inserted or left in table Instruction
without a relative row in either one of the 2 other tables.
If you don't mind having nullable fields, you could merge the 3 tables into one and use a CHECK
constraint:
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)
) ;
如果用户提交了文本,您的应用程序可以将其保存为 .txt 文件吗?这样,您只需要担心处理文件。
这里感觉有点不对劲
此架构中没有UserID
,因此应将其添加到
Instruction
表中。
如果用户没有上传任何内容,则(应该)在表中没有该用户的条目Instruction
。
所以问题——如前所述——不在于对这三个表施加约束。
加载此结构时,请使用存储过程和/或事务——以确保至少填充一个子记录。但是,这与用户必须上传内容的业务需求无关。