Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Implementieren einer „Beide, Entweder-Oder, aber nicht Null“-Anforderung in einer Datenbank

Ypercubes Antwort ist in Ordnung, außer dass dies tatsächlich nur durch deklarative Integrität erfolgen kann, während separate Tabellen beibehalten werden. Der Trick besteht darin, verzögerte zirkuläre FOREIGN KEYS mit ein wenig kreativer Denormalisierung zu kombinieren:

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;

Das Einfügen von Text erfolgt wie folgt:

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

Dokument wie folgt einfügen:

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

Und Einfügen von Text und Dokument wie folgt:

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

Der Versuch, Anweisungen allein einzufügen, schlägt jedoch fehl beim Festschreiben:

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

Auch der Versuch, den "nicht übereinstimmenden Typ" einzufügen, schlägt fehl beim Festschreiben:

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

Und natürlich schlägt der Versuch fehl, schlechte Werte in den Befehl einzufügen (diesmal vor dem Commit):

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