Tại sao các máy chủ được liên kết có giới hạn 10 nhánh trong biểu thức CASE?


19

Tại sao CASEbiểu hiện này :

SELECT CASE column 
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        ... c -> i
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END [col] 
FROM LinkedServer.database.dbo.table

Sản xuất kết quả này?

Thông báo lỗi:
Không thể chuẩn bị Msg 8180, Cấp 16, Trạng thái 1, Dòng 1 .
Msg 125, Cấp 15, Trạng thái 4, Dòng 1
Biểu thức trường hợp chỉ có thể được lồng vào cấp 10.

Rõ ràng không có CASEbiểu thức lồng nhau ở đây, mặc dù có hơn 10 "nhánh".

Một điều kỳ lạ khác. Hàm có giá trị bảng nội tuyến này tạo ra cùng một lỗi:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
     @var varchar(20)
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table
)

Nhưng một TVF đa tuyên bố tương tự hoạt động tốt:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
    @var varchar(20)
)
RETURNS @result TABLE 
(
    value varchar(max)
)
AS
BEGIN
    INSERT INTO @result
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table

RETURN;
END

Câu trả lời:


24

Rõ ràng không có một CASEbiểu thức lồng nhau ở đây.

Không có trong văn bản truy vấn, không. Nhưng trình phân tích cú pháp luôn mở rộng các CASEbiểu thức thành dạng lồng nhau:

SELECT CASE SUBSTRING(p.Name, 1, 1)
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM AdventureWorks2012.Production.Product AS p

Kế hoạch truy vấn cục bộ

Truy vấn đó là cục bộ (không có máy chủ được liên kết) và Tính toán vô hướng xác định biểu thức sau:

Biểu thức CASE lồng nhau

Điều này tốt khi được thực thi cục bộ, bởi vì trình phân tích cú pháp không thấy CASEcâu lệnh lồng nhau sâu hơn 10 cấp độ (mặc dù nó chuyển một giai đoạn sang giai đoạn sau của quá trình biên dịch truy vấn cục bộ).

Tuy nhiên, với một máy chủ được liên kết, văn bản được tạo có thể được gửi đến máy chủ từ xa để biên dịch. Nếu đó là trường hợp, trình phân tích cú pháp từ xa nhìn thấy một CASEcâu lệnh lồng nhau sâu hơn 10 cấp độ và bạn gặp lỗi 8180.

Một điều kỳ lạ khác. Hàm giá trị bảng nội tuyến này tạo ra cùng một lỗi

Hàm nội tuyến được mở rộng tại chỗ thành văn bản truy vấn ban đầu, do đó, không có gì ngạc nhiên khi kết quả lỗi tương tự với máy chủ được liên kết.

Nhưng một TVF đa tuyên bố tương tự hoạt động tốt

Tương tự, nhưng không giống nhau. MsTVF liên quan đến một chuyển đổi ngầm định varchar(max), điều này xảy ra để ngăn chặn CASEbiểu thức được gửi đến máy chủ từ xa. Bởi vì CASEđược đánh giá cục bộ, một trình phân tích cú pháp không bao giờ nhìn thấy một lồng quá mức CASEvà không có lỗi. Nếu bạn thay đổi định nghĩa bảng từ varchar(max)thành loại ẩn của CASEkết quả - varchar(2)- biểu thức được từ xa với msTVF và bạn sẽ gặp lỗi.

Cuối cùng, lỗi xảy ra khi một CASEmáy chủ từ xa được đánh giá bởi máy chủ từ xa. Nếu CASEkhông được đánh giá trong Trình lặp truy vấn từ xa, không có kết quả lỗi. Ví dụ: phần sau bao gồm phần CONVERTkhông được từ xa, do đó không xảy ra lỗi ngay cả khi máy chủ được liên kết được sử dụng:

SELECT CASE CONVERT(varchar(max), SUBSTRING(p.Name, 1, 1))
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM SQL2K8R2.AdventureWorks.Production.Product AS p

CASE không được từ xa


6

Linh cảm của tôi là truy vấn sẽ được viết lại ở đâu đó trên đường đi để có CASEcấu trúc hơi khác , ví dụ

CASE WHEN column = 'a' THEN '1' ELSE CASE WHEN column = 'b' THEN '2' ELSE ...

Tôi tin rằng đây là một lỗi trong bất kỳ nhà cung cấp máy chủ được liên kết nào mà bạn đang sử dụng (thực tế có lẽ tất cả trong số họ - tôi đã thấy nó được báo cáo chống lại một số). Tôi cũng tin rằng bạn không nên nín thở chờ sửa chữa, trong chức năng hoặc thông báo lỗi khó hiểu giải thích hành vi - điều này đã được báo cáo trong một thời gian dài, liên quan đến các máy chủ được liên kết (vốn không được yêu thích nhiều từ SQL Máy chủ 2000) và tác động đến số người ít hơn nhiều so với thông báo lỗi khó hiểu này vẫn chưa được khắc phục sau cùng tuổi thọ.

Như Paul chỉ ra , SQL Server đang mở rộng CASEbiểu thức của bạn sang nhiều loại lồng nhau và máy chủ được liên kết không thích nó. Thông báo lỗi gây nhầm lẫn, nhưng chỉ vì chuyển đổi cơ bản của biểu thức không hiển thị ngay lập tức (cũng không trực quan theo bất kỳ cách nào).

Một cách giải quyết khác (ngoài thay đổi chức năng bạn đã thêm vào câu hỏi của mình) sẽ là tạo chế độ xem hoặc quy trình được lưu trữ trên máy chủ được liên kết và tham chiếu rằng, thay vì chuyển toàn bộ truy vấn thông qua nhà cung cấp máy chủ được liên kết.

Một cách khác (giả sử truy vấn của bạn thực sự đơn giản và bạn chỉ muốn hệ số số của các chữ cái az) là có:

SELECT [col] = RTRIM(ASCII([column])-96)
FROM LinkedServer.database.dbo.table;

Nếu bạn thực sự cần điều này để hoạt động, tôi khuyên bạn nên liên hệ trực tiếp với bộ phận hỗ trợ và mở trường hợp, mặc dù tôi không thể đảm bảo kết quả - họ có thể chỉ cung cấp cho bạn cách giải quyết mà bạn đã truy cập trên trang này.


5

bạn có thể vượt qua điều này bằng cách

SELECT COALESCE(
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'a' THEN '1' 
    WHEN 'b' THEN '2' 
    WHEN 'c' THEN '3' 
    WHEN 'd' THEN '4' 
    WHEN 'e' THEN '5' 
    WHEN 'f' THEN '6' 
    WHEN 'g' THEN '7' 
    WHEN 'h' THEN '8' 
    WHEN 'i' THEN '9' 
    ELSE NULL
END,
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'j' THEN '10' 
    WHEN 'k' THEN '11'  
END)
FROM SQL2K8R2.AdventureWorks.Production.Product AS p

2

Một cách giải quyết khác cho vấn đề này là sử dụng logic dựa trên tập hợp, thay thế CASEbiểu thức bằng phép nối trái (hoặc áp dụng bên ngoài) thành bảng tham chiếu ( reftheo mã bên dưới), có thể là bảng vĩnh viễn, tạm thời hoặc bảng dẫn xuất / CTE. Nếu điều này là cần thiết trong nhiều truy vấn và thủ tục, tôi muốn có bảng này là bảng cố định:

SELECT ref.result_column AS [col] 
FROM LinkedServer.database.dbo.table AS t
  LEFT JOIN
    ( VALUES ('a',  '1'),
             ('b',  '2'), 
             ('c',  '3'),
             ---
             ('j', '10'),
             ('k', '11')
    ) AS ref (check_col, result_column) 
    ON ref.check_col = t.column ;

-4

một cách để giải quyết vấn đề này là đưa bài kiểm tra vào whenmệnh đề tức là

case
  when SUBSTRING(p.Name, 1, 1) = 'a' THEN '1'
...

Thật ra là không. Cả hai SELECT CASE v.V WHEN 'a' THEN 1 WHEN 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);SELECT CASE WHEN v.V = 'a' THEN 1 WHEN v.V = 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);dịch sang chính xác cùng một kế hoạch thực hiện (hãy tự mình xác minh điều đó), trong đó biểu thức CASE được xác định lại là CASE WHEN [Union1002]='a' THEN (1) ELSE CASE WHEN [Union1002]='b' THEN (2) ELSE NULL END END- với lồng nhau, như bạn có thể thấy.
Andriy M
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.