Mengapa server yang ditautkan memiliki batasan 10 cabang dalam ekspresi KASUS?

19

Mengapa CASEungkapan ini :

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

Hasilkan hasil ini?

Pesan kesalahan: Msg 8180, Level 16, Status 1, Baris 1
Pernyataan tidak dapat disiapkan.
Msg 125, Level 15, State 4, Line 1
Ekspresi kasus hanya dapat disarangkan ke level 10.

Jelas tidak ada sarang CASE ekspresi sini, meskipun ada lebih dari 10 "cabang."

Keanehan lain. Fungsi bernilai tabel inline ini menghasilkan kesalahan yang sama:

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
)

Tetapi TVF multi-pernyataan yang serupa bekerja dengan baik:

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
Andrey
sumber

Jawaban:

24

Jelas tidak ada CASEekspresi bersarang di sini.

Tidak dalam teks kueri, tidak. Namun pengurai selalu memperluas CASEekspresi ke bentuk bersarang:

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

Paket kueri lokal

Kueri itu adalah lokal (tidak ada server yang ditautkan) dan Compute Scalar mendefinisikan ekspresi berikut:

Ekspresi KASUS bersarang

Ini bagus ketika dijalankan secara lokal, karena parser tidak melihat bersarangCASE pernyataan lebih dari 10 level (meskipun itu meneruskannya ke tahap selanjutnya dari kompilasi permintaan lokal).

Namun, dengan server tertaut, teks yang dihasilkan dapat dikirim ke server jarak jauh untuk kompilasi. Jika itu masalahnya, parser jauh melihat CASEpernyataan bersarang lebih dari 10 level dan Anda mendapatkan kesalahan 8180.

Keanehan lain. Fungsi bernilai tabel inline ini menghasilkan kesalahan yang sama

Fungsi in-line diperluas di tempat ke dalam teks kueri asli, jadi tidak mengherankan hasil kesalahan yang sama dengan server yang ditautkan.

Tapi TVF multi-pernyataan serupa bekerja dengan baik

Serupa, tetapi tidak sama. MSTVF melibatkan konversi implisit ke varchar(max), yang terjadi untuk mencegah CASEekspresi dikirim ke server jarak jauh. Karena CASEdievaluasi secara lokal, parser tidak pernah melihat over-nested CASEdan tidak ada kesalahan. Jika Anda mengubah definisi tabel dari varchar(max)tipe implisit dari CASEhasil - varchar(2)- ekspresi dibuat ulang dengan msTVF dan Anda akan mendapatkan kesalahan.

Pada akhirnya, kesalahan terjadi ketika over-nested CASEdievaluasi oleh server jarak jauh. Jika CASEtidak dievaluasi dalam iterator Remote Query, tidak ada hasil kesalahan. Misalnya, berikut ini termasuk CONVERTyang tidak di-remote, jadi tidak ada kesalahan terjadi meskipun server yang ditautkan digunakan:

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

KASUS tidak dibuat ulang

Paul White mengatakan GoFundMonica
sumber
6

Firasat saya adalah bahwa kueri sedang ditulis ulang di suatu tempat di sepanjang jalan untuk memiliki CASEstruktur yang sedikit berbeda , misalnya

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

Saya percaya ini adalah bug dalam penyedia server terkait apa pun yang Anda gunakan (bahkan mungkin semuanya - saya pernah melihatnya melaporkan beberapa). Saya juga percaya Anda tidak harus menahan napas menunggu perbaikan, baik dalam fungsi atau pesan kesalahan membingungkan menjelaskan perilaku - ini telah dilaporkan sejak lama, melibatkan server yang terhubung (yang tidak memiliki banyak cinta sejak SQL Server 2000), dan dampaknya jauh lebih sedikit orang daripada pesan kesalahan yang membingungkan ini , yang belum diperbaiki setelah umur panjang yang sama.

Seperti yang ditunjukkan oleh Paul , SQL Server memperluas CASEekspresi Anda ke varietas bersarang, dan server tertaut tidak menyukainya. Pesan kesalahan membingungkan, tetapi hanya karena konversi ekspresi yang mendasarinya tidak segera terlihat (atau intuitif dengan cara apa pun).

Satu solusi (selain dari perubahan fungsi yang Anda tambahkan ke pertanyaan Anda) adalah dengan membuat tampilan atau prosedur tersimpan pada server yang ditautkan, dan referensi itu, alih-alih mengirimkan kueri penuh melalui penyedia server yang ditautkan.

Lain (menganggap permintaan Anda benar-benar sederhana, dan Anda hanya ingin koefisien numerik huruf az) adalah:

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

Jika Anda benar-benar membutuhkan ini untuk bekerja sebagaimana mestinya, saya sarankan Anda menghubungi dukungan secara langsung dan membuka kasing, meskipun saya tidak dapat menjamin hasilnya - mereka mungkin hanya memberi Anda solusi yang sudah Anda miliki aksesnya di halaman ini.

Aaron Bertrand
sumber
5

Anda bisa menyiasatinya dengan

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
Nik
sumber
2

Solusi lain untuk masalah ini adalah dengan menggunakan logika berbasis set, mengganti CASEekspresi dengan gabungan kiri (atau terapkan luar) ke tabel referensi ( refdalam kode di bawah), yang dapat berupa tabel permanen, sementara atau turunan / CTE. Jika ini diperlukan dalam beberapa kueri dan prosedur, saya lebih suka menjadikan ini sebagai tabel permanen:

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 ;
ypercubeᵀᴹ
sumber
-4

salah satu cara untuk mengatasi ini adalah dengan memasukkan tes dalam whenklausa yaitu

case
  when SUBSTRING(p.Name, 1, 1) = 'a' THEN '1'
...
pengguna98586
sumber
Sebenarnya tidak. Keduanya SELECT CASE v.V WHEN 'a' THEN 1 WHEN 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);dan SELECT CASE WHEN v.V = 'a' THEN 1 WHEN v.V = 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);terjemahkan dengan rencana eksekusi yang persis sama (jangan ragu untuk memverifikasi itu sendiri), di mana ekspresi KASUS didefinisikan ulang sebagai CASE WHEN [Union1002]='a' THEN (1) ELSE CASE WHEN [Union1002]='b' THEN (2) ELSE NULL END END- dengan bersarang, seperti yang Anda lihat.
Andriy M