I have requirement for a web app that states that a user should be able to either upload an instruction document(s) (.pdf, .doc, .txt) or provide text for the instructions. The
Ypercube's answer is fine, except this can, in fact, be done purely through declarative integrity while keeping separate tables. The trick is to combine deferred circular FOREIGN KEYs with a little bit of creative denormalization:
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;
Inserting Text is done like this:
INSERT INTO Instruction (InstructionId, TextId) VALUES (1, 1);
INSERT INTO Text (InstructionId) VALUES (1);
COMMIT;
Inserting Document like this:
INSERT INTO Instruction (InstructionId, DocumentId) VALUES (2, 2);
INSERT INTO Document (InstructionId) VALUES (2);
COMMIT;
And inserting both Text and Document like this:
INSERT INTO Instruction (InstructionId, TextId, DocumentId) VALUES (3, 3, 3);
INSERT INTO Text (InstructionId) VALUES (3);
INSERT INTO Document (InstructionId) VALUES (3);
COMMIT;
However, trying to insert Instruction alone fails on commit:
INSERT INTO Instruction (InstructionId, TextId) VALUES (4, 4);
COMMIT; -- Error (FOREIGN KEY violation).
Attempting to insert the "mismatched type" also fails on commit:
INSERT INTO Document (InstructionId) VALUES (1);
COMMIT; -- Error (FOREIGN KEY violation).
And of course, trying to insert bad values into Instruction fails (this time before commit):
INSERT INTO Instruction (InstructionId, TextId) VALUES (5, 6); -- Error (CHECK violation).
INSERT INTO Instruction (InstructionId) VALUES (7); -- Error (CHECK violation).