Ich habe immer verstanden, dass die CASE
Aussage nach einem Kurzschlussprinzip dahingehend funktioniert, dass die Bewertung nachfolgender Schritte nicht erfolgt, wenn ein vorheriger Schritt als wahr bewertet wird. (Diese Antwort wertet die SQL Server-CASE-Anweisung alle Bedingungen aus oder wird sie bei der ersten TRUE-Bedingung beendet? Ist verwandt, scheint diese Situation jedoch nicht abzudecken und bezieht sich auf SQL Server.)
Im folgenden Beispiel möchte ich die MAX(amount)
Anzahl der Monate berechnen, die sich je nach Anzahl der Monate zwischen dem Start- und dem Zahlungstermin unterscheiden.
(Dies ist offensichtlich ein konstruiertes Beispiel, aber die Logik hat gültige geschäftliche Argumente im tatsächlichen Code, in dem ich das Problem sehe).
Wenn zwischen dem Start- und dem Zahlungstermin <5 Monate liegen, wird Ausdruck 1 verwendet, andernfalls wird Ausdruck 2 verwendet.
Dies führt zu dem Fehler "ORA-01428: Argument '-1' liegt außerhalb des Bereichs", da 1 Datensatz eine ungültige Datenbedingung aufweist, die zu einem negativen Wert für den Start der BETWEEN-Klausel von ORDER BY führt.
Abfrage 1
SELECT ref_no,
CASE WHEN MONTHS_BETWEEN(paid_date, start_date) < 5 THEN
-- Expression 1
MAX(amount)
OVER (PARTITION BY ref_no ORDER BY paid_date ASC
ROWS BETWEEN MONTHS_BETWEEN(paid_date, start_date) PRECEDING
AND CURRENT ROW)
ELSE
-- Expression 2
MAX(amount)
OVER (PARTITION BY ref_no ORDER BY paid_date ASC
ROWS BETWEEN 5 PRECEDING AND CURRENT ROW)
END
END
FROM payment
Also habe ich mich für diese zweite Abfrage entschieden, um zuerst zu beseitigen, wo immer dies auftreten kann:
SELECT ref_no,
CASE WHEN MONTHS_BETWEEN(paid_date, start_date) < 0 THEN 0
ELSE
CASE WHEN MONTHS_BETWEEN(paid_date, start_date) < 5 THEN
MAX(amount)
OVER (PARTITION BY ref_no ORDER BY paid_date ASC
ROWS BETWEEN MONTHS_BETWEEN(paid_date, start_date) PRECEDING
AND CURRENT ROW)
ELSE
MAX(amount)
OVER (PARTITION BY ref_no ORDER BY paid_date ASC
ROWS BETWEEN 5 PRECEDING AND CURRENT ROW)
END
END
FROM payment
Leider gibt es ein unerwartetes Verhalten, das bedeutet, dass die Werte, die Ausdruck 1 verwenden würde, validiert werden, obwohl die Anweisung nicht ausgeführt wird, da die negative Bedingung jetzt vom Äußeren abgefangen wird CASE
.
Ich kann das Problem umgehen , indem Sie ABS
auf dem MONTHS_BETWEEN
in 1 Expression , aber ich fühle mich wie dies nicht notwendig sein sollte.
Ist dieses Verhalten wie erwartet? Wenn ja, warum, wie es mir unlogisch und eher wie ein Käfer erscheint?
Dadurch werden eine Tabelle und Testdaten erstellt. Die Abfrage ist einfach, dass ich überprüfe, ob der richtige Pfad in der CASE
genommen wird.
CREATE TABLE payment
(ref_no NUMBER,
start_date DATE,
paid_date DATE,
amount NUMBER)
INSERT INTO payment
VALUES (1001,TO_DATE('01-11-2015','DD-MM-YYYY'),TO_DATE('01-01-2016','DD-MM-YYYY'),3000)
INSERT INTO payment
VALUES (1001,TO_DATE('01-11-2015','DD-MM-YYYY'),TO_DATE('12-12-2015','DD-MM-YYYY'),5000)
INSERT INTO payment
VALUES (1001,TO_DATE('10-03-2016','DD-MM-YYYY'),TO_DATE('10-02-2016','DD-MM-YYYY'),2000)
INSERT INTO payment
VALUES (1001,TO_DATE('01-11-2015','DD-MM-YYYY'),TO_DATE('03-03-2016','DD-MM-YYYY'),6000)
INSERT INTO payment
VALUES (1001,TO_DATE('01-11-2015','DD-MM-YYYY'),TO_DATE('28-11-2015','DD-MM-YYYY'),10000)
SELECT ref_no,
CASE WHEN MONTHS_BETWEEN(paid_date, start_date) < 0 THEN '<0'
ELSE
CASE WHEN MONTHS_BETWEEN(paid_date, start_date) < 5 THEN
'<5'
-- MAX(amount)
-- OVER (PARTITION BY ref_no ORDER BY paid_date ASC ROWS
-- BETWEEN MONTHS_BETWEEN(paid_date, start_date) PRECEDING
-- AND CURRENT ROW)
ELSE
'>=5'
-- MAX(amount)
-- OVER (PARTITION BY ref_no ORDER BY paid_date ASC ROWS
-- BETWEEN 5 PRECEDING AND CURRENT ROW)
END
END
FROM payment
MAX(amount) OVER (PARTITION BY ref_no ORDER BY paid_date ASC ROWS BETWEEN GREATEST(0, LEAST(5, MONTHS_BETWEEN(paid_date, start_date))) PRECEDING AND CURRENT ROW)
Antworten:
Daher war es für mich schwierig festzustellen, was Ihre eigentliche Frage aus dem Beitrag war, aber ich gehe davon aus, dass dies bei der Ausführung der Fall ist:
Sie erhalten immer noch ORA-01428: Argument '-1' liegt außerhalb des Bereichs ?
Ich denke nicht, dass dies ein Fehler ist. Ich denke, es ist eine Sache der Betriebsordnung. Oracle muss die Analyse für alle von der Ergebnismenge zurückgegebenen Zeilen durchführen. Dann kann es darum gehen, die Ausgabe zu transformieren.
Ein paar zusätzliche Möglichkeiten, dies zu umgehen, wären, die Zeile mit einer where-Klausel auszuschließen:
Oder Sie können einen Fall wie folgt in Ihre Analyse einbetten:
Erläuterung
Ich wünschte, ich könnte eine Dokumentation finden, um die Reihenfolge der Operationen zu sichern, aber ich konnte noch nichts finden.
Die
CASE
Kurzschlussauswertung erfolgt nach Auswertung der Analysefunktion. Die Reihenfolge der Operationen für die betreffende Abfrage wäre:Da
max over()
dies also vor dem Fall geschieht, schlägt die Abfrage fehl.Die Analysefunktionen von Oracle werden als Zeilenquelle betrachtet . Wenn Sie einen Erklärungsplan für Ihre Abfrage ausführen, sollte eine "Fenstersortierung" angezeigt werden, bei der es sich um die analytische, generierende Zeile handelt, die von der vorherigen Zeilenquelle, der Zahlungstabelle, an sie weitergeleitet wird. Eine case-Anweisung ist ein Ausdruck, der für jede Zeile in der Zeilenquelle ausgewertet wird. Es macht also (zumindest für mich) Sinn, dass der Fall nach der Analyse passiert.
quelle
SQL definiert, was zu tun ist, nicht wie es zu tun ist. Während Oracle normalerweise die Fallbewertung kurzschließt, ist dies eine Optimierung und wird daher vermieden, wenn der Optimierer der Ansicht ist, dass ein anderer Ausführungspfad eine überlegene Leistung bietet. Ein solcher Optimierungsunterschied wäre zu erwarten, wenn es um Analysen geht.
Der Optimierungsunterschied ist nicht auf den Fall beschränkt. Ihr Fehler kann mit Koaleszenz reproduziert werden, was normalerweise auch einen Kurzschluss bedeuten würde.
Es scheint keine Dokumentation zu geben, die ausdrücklich besagt, dass die Kurzschlussauswertung vom Optimierer ignoriert werden kann. Das Nächste (wenn auch nicht nah genug), das ich finden kann, ist Folgendes :
Diese Frage zeigt, dass die Kurzschlussbewertung auch ohne Analyse ignoriert wird (obwohl es eine Gruppierung gibt).
Tom Kyte erwähnt, dass Kurzschlüsse in seiner Antwort auf eine Frage zur Bewertung der Reihenfolge der Prädikate ignoriert werden können .
Sie sollten eine SR mit Oracle öffnen. Ich vermute, sie werden es als Dokumentationsfehler akzeptieren und die Dokumentation in der nächsten Version erweitern, um eine Einschränkung bezüglich des Optimierers aufzunehmen.
quelle
Es sieht so aus, als würde Oracle alle Ausdrücke in CASE auswerten. Sehen
Die ersten beiden Abfragen werden in Ordnung ausgeführt.
quelle