Tôi luôn hiểu rằng CASE
tuyên bố hoạt động theo nguyên tắc 'ngắn mạch' trong việc đánh giá các bước tiếp theo sẽ không diễn ra nếu bước trước được đánh giá là đúng. (Câu trả lời này Câu lệnh CASE của SQL Server có đánh giá tất cả các điều kiện hoặc thoát khỏi điều kiện TRUE đầu tiên không? Có liên quan nhưng dường như không bao gồm tình huống này và liên quan đến SQL Server).
Trong ví dụ sau, tôi muốn tính toán MAX(amount)
giữa một loạt các tháng khác nhau dựa trên số tháng bắt đầu và ngày trả tiền.
(Đây rõ ràng là một ví dụ được xây dựng nhưng logic có lý do kinh doanh hợp lệ trong mã thực tế nơi tôi thấy vấn đề).
Nếu có <5 tháng giữa ngày bắt đầu và ngày trả tiền thì Biểu thức 1 sẽ được sử dụng nếu không Biểu thức 2 sẽ được sử dụng.
Điều này dẫn đến lỗi "ORA-01428: đối số '-1' nằm ngoài phạm vi" vì 1 bản ghi có điều kiện dữ liệu không hợp lệ dẫn đến giá trị âm khi bắt đầu mệnh đề BETweEN của ORDER BY.
Truy vấn 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
Vì vậy, tôi đã tìm kiếm truy vấn thứ hai này để loại bỏ bất cứ nơi nào điều này có thể xảy ra:
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
Thật không may, có một số hành vi không mong muốn có nghĩa là các giá trị Biểu thức 1 NÊN sử dụng được xác thực, mặc dù câu lệnh sẽ không được thực thi vì điều kiện tiêu cực hiện bị bẫy bên ngoài CASE
.
Tôi có thể khắc phục vấn đề này bằng cách sử dụng ABS
trên MONTHS_BETWEEN
trong Biểu 1 , nhưng tôi cảm thấy như thế này nên không cần thiết.
Là hành vi như mong đợi? Nếu vậy 'tại sao' vì nó có vẻ phi logic với tôi và giống như một lỗi hơn?
Điều này sẽ tạo ra một bảng và dữ liệu thử nghiệm. Truy vấn chỉ đơn giản là tôi kiểm tra xem đường dẫn chính xác CASE
đang được thực hiện.
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)