Đây là một lỗi trong bình thường hóa dự án , được thể hiện bằng cách sử dụng một truy vấn con bên trong một biểu thức trường hợp với hàm không xác định.
Để giải thích, chúng ta cần lưu ý hai điều trước:
- SQL Server không thể thực thi các truy vấn con trực tiếp, vì vậy chúng luôn không được kiểm soát hoặc chuyển đổi thành một ứng dụng .
- Các ngữ nghĩa của
CASE
sao cho một THEN
biểu thức chỉ nên được đánh giá nếu WHEN
mệnh đề trả về true.
Truy vấn con (tầm thường) được giới thiệu trong trường hợp có vấn đề do đó dẫn đến một toán tử áp dụng (tham gia các vòng lặp lồng nhau). Để đáp ứng yêu cầu thứ hai, SQL Server ban đầu đặt biểu thức dbo.test6(1) + dbo.test6(2)
ở phía bên trong của ứng dụng:
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
... với CASE
ngữ nghĩa được vinh danh bởi một vị từ xuyên qua khi tham gia:
[@i]=(1) OR [@i]=(2) OR IsFalseOrNull [@i]=(3)
Phía bên trong của vòng lặp chỉ được đánh giá nếu điều kiện chuyển qua đánh giá là sai (có nghĩa @i = 3
). Đây là tất cả chính xác cho đến nay. Các Tính vô hướng sau các vòng lồng nhau tham gia cũng vinh danh những CASE
ngữ nghĩa một cách chính xác:
[Expr1001] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
Vấn đề là giai đoạn chuẩn hóa dự án của quá trình biên dịch truy vấn thấy Expr1000
không tương thích và xác định rằng nó sẽ an toàn (trình tường thuật: không ) để di chuyển nó ra ngoài vòng lặp:
[Expr1000] = Scalar Operator([dbo].[test6]((1))+[dbo].[test6]((2)))
Điều này phá vỡ * ngữ nghĩa được thực hiện bởi vị từ đi qua , do đó, hàm được đánh giá khi không nên và kết quả vòng lặp vô hạn.
Bạn nên báo cáo lỗi này. Một cách giải quyết là để ngăn chặn biểu thức được di chuyển ra ngoài ứng dụng bằng cách làm cho nó tương quan (nghĩa là bao gồm @i
trong biểu thức) nhưng đây là một hack tất nhiên. Có một cách để vô hiệu hóa bình thường hóa dự án, nhưng tôi đã được yêu cầu trước đó không chia sẻ công khai, vì vậy tôi sẽ không.
Vấn đề này không phát sinh trong SQL Server 2019 khi hàm vô hướng được nội tuyến , bởi vì logic nội tuyến hoạt động trực tiếp trên cây được phân tích cú pháp (trước khi chuẩn hóa dự án). Logic đơn giản trong câu hỏi có thể được đơn giản hóa bằng logic nội tuyến thành không đệ quy:
[Expr1019] = (Scalar Operator((1)))
[Expr1045] = Scalar Operator(CONVERT_IMPLICIT(int,CONVERT_IMPLICIT(int,[Expr1019],0)+(2),0))
... Trả về 3.
Một cách khác để minh họa vấn đề cốt lõi là:
-- Not schema bound to make it non-det
CREATE OR ALTER FUNCTION dbo.Error()
RETURNS integer
-- WITH INLINE = OFF -- SQL Server 2019 only
AS
BEGIN
RETURN 1/0;
END;
GO
DECLARE @i integer = 1;
SELECT
CASE
WHEN @i = 1 THEN 1
WHEN @i = 2 THEN 2
WHEN @i = 3 THEN (SELECT dbo.Error()) -- 'subquery'
ELSE NULL
END;
Tái sản xuất các bản dựng mới nhất của tất cả các phiên bản từ 2008 R2 đến 2019 CTP 3.0.
Một ví dụ nữa (không có hàm vô hướng) do Martin Smith cung cấp :
SELECT IIF(@@TRANCOUNT >= 0, 1, (SELECT CRYPT_GEN_RANDOM(4)/ 0))
Điều này có tất cả các yếu tố chính cần thiết:
CASE
(thực hiện trong nội bộ như ScaOp_IIF
)
- Hàm không xác định (
CRYPT_GEN_RANDOM
)
- Một truy vấn con trên nhánh không nên được thực thi (
(SELECT ...)
)
* Nghiêm túc, việc chuyển đổi ở trên vẫn có thể đúng nếu đánh giá Expr1000
được hoãn lại một cách chính xác, vì nó chỉ được tham chiếu bởi cấu trúc an toàn:
[Expr1002] = Scalar Operator(CASE WHEN [@i]=(1) THEN (1) ELSE CASE WHEN [@i]=(2) THEN (2) ELSE CASE WHEN [@i]=(3) THEN [Expr1000] ELSE NULL END END END)
... nhưng điều này đòi hỏi một cờ ForceOrder nội bộ (không phải gợi ý truy vấn), cũng không được đặt. Trong mọi trường hợp, việc thực hiện logic được áp dụng bởi chuẩn hóa dự án là không chính xác hoặc không đầy đủ.
Báo cáo lỗi trên trang web Phản hồi Azure cho SQL Server.