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

Verwenden von IF EXISTS (SELECT ...) in einem BEFORE INSERT-Trigger (Oracle)

Zunächst einmal, wenn Sie SQL*Plus verwenden, wenn Sie ein Objekt erstellen und erfahren, dass es Kompilierungsfehler gibt, der Befehl show errors zeigt Ihnen die Fehler.

Wenn Sie show errors ausgeführt haben , würde Ihnen gesagt werden, dass IF EXISTS ist keine gültige Syntax. Sie könnten so etwas tun wie

SELECT COUNT(*)
  INTO l_cnt
  FROM <<rest of query>>

IF( l_cnt > 0 )
THEN
  RAISE_APPLICATION_ERROR ...
END IF;

Sobald Sie den Kompilierungsfehler behoben haben, werden Sie jedoch Laufzeitfehler erhalten. In einem Trigger auf Zeilenebene auf surveillance , können Sie surveillance generell nicht abfragen (Sie können, wenn Sie nur INSERT VALUES tun die garantiert nur eine einzige Zeile einfügt). Wenn Sie dies tun, erhalten Sie zur Laufzeit einen Mutating-Trigger-Fehler.

Wenn Sie aus Sicht des Datenmodells eine Tabelle entwerfen, in der die gültigen Daten für eine bestimmte Zeile von Daten abhängen, die in anderen Zeilen derselben Tabelle gespeichert sind, haben Sie im Allgemeinen gegen die Normalisierungsprinzipien verstoßen, und Sie sind im Allgemeinen besser bedient, wenn Sie das beheben zugrundeliegendes Datenmodell.

Wenn Sie wirklich entschlossen sind, das Datenmodell beizubehalten, würde ich es vorziehen, eine materialisierte Ansicht zu erstellen, die beim Festschreiben aktualisiert wird und nur Daten für Zeilen enthält, die Ihre Kriterien verletzen. Sie können dieser materialisierten Ansicht dann Einschränkungen auferlegen, die zum Commit-Zeitpunkt Fehler ausgeben, wenn Ihre Kriterien verletzt werden. Dies erfordert materialisierte Ansichtsprotokolle für Ihre Tabelle.

Wenn Sie das Datenmodell wirklich beibehalten und die Logik mit Triggern erzwingen möchten, benötigen Sie die klassische Lösung mit drei Triggern (oder einen zusammengesetzten Trigger mit drei Teilen, wenn Sie 11.2 oder höher verwenden). Sie würden ein Paket mit einer Sammlung von Primärschlüsselwerten erstellen. Ein Before-Statement-Trigger würde die Sammlung initialisieren. Ein Trigger auf Zeilenebene würde die Primärschlüssel der Zeilen einfügen, die in diese Sammlung eingefügt und/oder aktualisiert wurden. Und dann würde ein After-Statement-Trigger über diese Sammlung iterieren und alle gewünschten Prüfungen implementieren. Das sind aber viele bewegende Stücke, weshalb ich generell davon abraten würde.

Und selbst wenn Sie all diese Teile zum Laufen bringen, wird Ihre Logik Sie in einer Umgebung mit mehreren Benutzern nicht schützen. Wenn mehrere Benutzer gleichzeitig auf das System zugreifen, ist es durchaus möglich, dass ein Benutzer eine Zeile einfügt, der zweite Benutzer eine weitere Zeile mit einem überlappenden Bereich einfügt und dann jede Sitzung festgeschrieben wird. In diesem Fall lassen beide Auslösersätze die Änderung zu, aber Sie werden immer noch Daten in der Tabelle haben, die gegen Ihre Anforderungen verstoßen. Die materialisierte Ansicht wird in einer Mehrbenutzerumgebung ordnungsgemäß funktionieren, da sie zum Commit-Zeitpunkt und nicht zum Zeitpunkt des Einfügens erzwungen wird. Wenn Sie möchten, dass die Trigger in einer Mehrbenutzerumgebung funktionieren, müssen Sie sie weiter verkomplizieren, indem Sie zusätzliche Logik hinzufügen, die eine Serialisierung erzwingt, die das insert der zweiten Sitzung blockieren würde von der Ausführung bis zur ersten Sitzung, entweder festgeschrieben oder zurückgesetzt. Das erhöht die Komplexität, verringert die Skalierbarkeit und kann je nach Implementierung zu einem Support-Albtraum werden.