Saya selalu mengerti bahwa CASEpernyataan tersebut bekerja pada prinsip 'hubungan pendek' dalam evaluasi bahwa langkah-langkah selanjutnya tidak terjadi jika langkah sebelumnya dievaluasi menjadi benar. (Jawaban ini Apakah pernyataan KASUS SQL Server mengevaluasi semua kondisi atau keluar pada kondisi BENAR pertama? Terkait tetapi tampaknya tidak mencakup situasi ini dan terkait dengan SQL Server).
Dalam contoh berikut, saya ingin menghitung MAX(amount)rentang bulan yang berbeda berdasarkan berapa bulan antara tanggal mulai dan tanggal berbayar.
(Ini jelas merupakan contoh yang dibangun tetapi logika memiliki alasan bisnis yang valid dalam kode aktual di mana saya melihat masalah).
Jika ada <5 bulan antara tanggal mulai dan tanggal pembayaran maka Ekspresi 1 akan digunakan jika tidak, Ekspresi 2 akan digunakan.
Ini menghasilkan kesalahan "ORA-01428: argumen '-1' berada di luar kisaran" karena 1 catatan memiliki kondisi data yang tidak valid yang menghasilkan nilai negatif untuk dimulainya klausa BETWEEN dari ORDER BY.
Pertanyaan 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
Jadi saya pergi untuk kueri kedua ini untuk menghilangkan dulu di mana saja hal ini dapat terjadi:
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
Sayangnya, ada beberapa perilaku tak terduga yang berarti bahwa nilai-nilai Ekspresi 1 AKAN digunakan divalidasi, meskipun pernyataan itu tidak akan dieksekusi karena kondisi negatif sekarang terjebak oleh luar CASE.
Saya bisa mendapatkan sekitar masalah dengan menggunakan ABSpada MONTHS_BETWEENdi Expression 1 , tapi aku merasa seperti ini harus tidak perlu.
Apakah perilaku ini seperti yang diharapkan? Jika demikian 'mengapa' karena tampaknya tidak masuk akal bagi saya dan lebih seperti bug?
Ini akan membuat tabel dan menguji data. Pertanyaannya hanyalah saya memeriksa bahwa jalur yang benar CASEsedang diambil.
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)