Ich habe immer verstanden, dass die CASEAussage 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 ABSauf dem MONTHS_BETWEENin 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 CASEgenommen 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)