Bağlı sunucular neden bir CASE ifadesinde 10 dal sınırlaması var?


19

Bu CASEifade neden :

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

Bu sonuç üretilsin mi?

Hata iletisi: Msg 8180, Seviye 16, Durum 1, Satır 1
İfadeleri hazırlanamadı.
Msg 125, Seviye 15, Durum 4, Satır 1
Vaka ifadeleri yalnızca 10. seviyeye yuvalanabilir.

Açıkçası CASEburada 10'dan fazla "dal" olmasına rağmen iç içe bir ifade yoktur.

Başka bir tuhaflık. Bu satır içi tablo değerli işlev aynı hatayı üretir:

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
)

Ancak benzer bir çoklu ifade TVF iyi çalışıyor:

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

Yanıtlar:


24

Açıkçası burada iç içe bir CASEifade yok.

Sorgu metninde değil, hayır. Ancak ayrıştırıcı her zaman CASEifadeleri iç içe biçime genişletir :

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

Yerel sorgu planı

Bu sorgu yereldir (bağlı sunucu yok) ve Hesaplama Skaleri aşağıdaki ifadeyi tanımlar:

İç içe CASE ifadesi

Bu, yerel olarak yürütüldüğünde sorun yok, çünkü ayrıştırıcıCASE 10 düzey derinlikte iç içe geçmiş bir deyim görmediğinden (yerel sorgu derlemesinin sonraki aşamalarına geçmesine rağmen) sorun yoktur.

Ancak, bağlantılı sunucu ile oluşturulan metin olabilir derleme için uzak sunucuya gönderilecek. Bu durumda, uzak ayrıştırıcıCASE 10 düzeyden daha derin iç içe bir deyim görür ve 8180 hatası alırsınız.

Başka bir tuhaflık. Bu satır içi tablo değerli işlev aynı hatayı üretir

Satır içi işlevi yerinde özgün sorgu metnine genişletilir, bu nedenle bağlantılı sunucuda aynı hata sonuçlarının olması şaşırtıcı değildir.

Ancak benzer bir çok ifadeli TVF iyi çalışıyor

Benzer, ama aynı değil. MsTVF, ifadenin uzak sunucuya gönderilmesini varchar(max)önleyen örtük bir dönüştürme içerir CASE. Çünkü CASElokal olarak değerlendirilir, bir ayrıştırıcı aşırı iç içe görür asla CASEve hiçbir hata yoktur. Tablo tanımını varchar(max)yerine örtük türüne değiştirirsenizCASE sonucunvarchar(2) - ifadesi msTVF ile kalır ve bir hata alırsınız.

Sonuçta, hata, iç içe geçmiş CASEuzak sunucu tarafından değerlendirildiğinde ortaya çıkar . Eğer CASEUzaktan Sorgu yineleyici, hata sonuçlarında değerlendirilmez. Örneğin, aşağıdakiler CONVERTuzak olmayan bir içerir , bu nedenle bağlantılı bir sunucu kullanılsa bile hata oluşmaz:

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 uzak değil


6

Benim önsezi, sorgu biraz farklı bir CASEyapıya sahip olmak için yol boyunca bir yere yeniden yazılıyor olmasıdır , örn.

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

Bu kullandığınız bağlantılı sunucu sağlayıcısında bir hata olduğuna inanıyorum (aslında belki de hepsi - birkaç karşı rapor gördüm). Ayrıca, işlevselliğini veya davranışı açıklayan kafa karıştırıcı hata mesajında ​​nefesinizi düzeltmek için beklememeniz gerektiğine inanıyorum - bu uzun zamandır bildirildi, bağlantılı sunucuları içeriyor (SQL'den beri çok fazla sevgisi yoktu Server 2000) ve bu kafa karıştırıcı hata iletisinden çok daha az kişiyi etkiler aynı ömürden sonra düzeltilmesi gereken .

Paul'un işaret ettiği gibi , SQL Server CASEifadenizi iç içe çeşitliliğe genişletiyor ve bağlı sunucu bundan hoşlanmıyor. Hata iletisi kafa karıştırıcıdır, ancak yalnızca ifadenin temeldeki dönüşümü hemen görünmez (ne de herhangi bir şekilde sezgisel).

Geçici bir çözüm (sorunuza eklediğiniz işlev değişikliği dışında), bağlı sunucuda bir görünüm veya saklı yordam oluşturmak ve tam sorguyu bağlı sunucu sağlayıcısından geçirmek yerine buna başvurmak olacaktır.

Başka (sorgunuzun gerçekten bu basit olduğunu varsayalım ve sadece az harflerin sayısal katsayısını istiyorsanız) sahip olmaktır:

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

Kesinlikle olduğu gibi çalışmanız gerekiyorsa, doğrudan desteğe başvurmanızı ve bir dava açmanızı öneririm, ancak sonuçlara kefil olamıyorum - bu sayfada size zaten erişebildiğiniz geçici çözümleri sağlayabilirler.


5

bunun üstesinden gelebilirsin

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

Bu sorun için başka bir geçici çözüm , kalıcı, geçici veya türetilmiş bir tablo / CTE CASEolabilecek bir referans tablosuna ( refaşağıdaki kodda) ifadeyi sol birleşim (veya dış uygulama) ile değiştirerek küme tabanlı mantık kullanmaktır . Birden fazla sorgu ve yordamda bu gerekiyorsa, bunu kalıcı tablo olarak tercih ederim:

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

Bunu aşmanın bir yolu testi whenmaddeye dahil etmektir;

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

Aslında hayır. Hem SELECT CASE v.V WHEN 'a' THEN 1 WHEN 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);ve SELECT CASE WHEN v.V = 'a' THEN 1 WHEN v.V = 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);çevirmek tamamen aynı VAKA ifadesi olarak yeniden tanımlanır yürütme planı (kendiniz doğrulamak için çekinmeyin) CASE WHEN [Union1002]='a' THEN (1) ELSE CASE WHEN [Union1002]='b' THEN (2) ELSE NULL END END- iç içe olan, gördüğünüz gibi.
Andriy M
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.