J'ai toujours compris que l' CASE
énoncé fonctionnait selon un principe de «court-circuit» en ce sens que l'évaluation des étapes suivantes n'a pas lieu si une étape antérieure est évaluée comme vraie. (Cette réponse L'instruction CASE de SQL Server évalue-t-elle toutes les conditions ou quitte-t-elle la première condition VRAIE? Est liée mais ne semble pas couvrir cette situation et concerne SQL Server).
Dans l'exemple suivant, je souhaite calculer le MAX(amount)
entre une plage de mois qui diffère en fonction du nombre de mois entre le début et les dates payées.
(Ceci est évidemment un exemple construit mais la logique a un raisonnement commercial valide dans le code réel où je vois le problème).
S'il y a moins de 5 mois entre les dates de début et de paiement, l' expression 1 sera utilisée, sinon l' expression 2 sera utilisée.
Il en résulte l'erreur «ORA-01428: l'argument« -1 »est hors limites» car 1 enregistrement a une condition de données non valide qui se traduit par une valeur négative pour le début de la clause BETWEEN de la commande ORDER BY.
Requête 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
Je suis donc allé pour cette deuxième requête pour éliminer d'abord où que cela puisse se produire:
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
Malheureusement, il existe un comportement inattendu qui signifie que les valeurs que l' expression 1 DEVRAIT utiliser seraient validées, même si l'instruction ne sera pas exécutée car la condition négative est désormais piégée par l'extérieur CASE
.
Je peux contourner le problème en utilisant ABS
sur MONTHS_BETWEEN
dans Expression 1 , mais je pense que cela ne devrait pas être nécessaire.
Ce comportement est-il conforme aux attentes? Si oui «pourquoi» car cela me semble illogique et ressemble plus à un bug?
Cela va créer une table et tester les données. La requête consiste simplement à vérifier que le chemin d'accès correct CASE
est pris.
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)