연결된 서버가 CASE 식에서 10 개의 분기로 제한되는 이유는 무엇입니까?


19

왜 이런 CASE표현 이 됩니까?

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

이 결과를 생성 하시겠습니까?

오류 메시지 : 메시지 8180, 수준 16, 상태 1, 줄 1
문을 준비 할 수 없습니다.
메시지 125, 수준 15, 상태 4, 줄 1
사례 식은 수준 10에만 중첩 될 수 있습니다.

CASE여기 에는 중첩 된 표현이 없지만 "분기"가 10 개 이상 있습니다.

또 다른 이상한. 이 인라인 테이블 반환 함수는 동일한 오류를 생성합니다.

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
)

그러나 유사한 다중 문 TVF는 잘 작동합니다.

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

답변:


24

분명히 CASE여기에 중첩 된 표현 이 없습니다 .

쿼리 텍스트에 없습니다. 그러나 파서는 항상 CASE표현식을 중첩 형식으로 확장 합니다.

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

로컬 쿼리 계획

해당 쿼리는 로컬 (링크 된 서버 없음)이며 Compute Scalar는 다음 표현식을 정의합니다.

중첩 된 CASE 표현식

파서CASE10 레벨 이상의 중첩 명령문을 보지 않기 때문에 로컬에서 실행될 때 좋습니다 (로컬 쿼리 컴파일의 나중 단계로 전달하지만).

그러나 연결된 서버의 경우 생성 된 텍스트 컴파일을 위해 원격 서버로 전송 될 수 있습니다. 이 경우, 원격 구문 분석기CASE10 레벨 이상의 중첩 된 명령문을 보고 오류 8180을 발생시킵니다.

또 다른 이상한. 이 인라인 테이블 반환 함수는 동일한 오류를 생성합니다

인라인 함수는 원래 쿼리 텍스트로 제자리에 확장되므로 연결된 서버에서 동일한 오류 결과가 발생합니다.

그러나 유사한 다중 진술 TVF는 잘 작동합니다.

비슷하지만 동일하지는 않습니다. msTVF는 로의 암시 적 변환을 포함 varchar(max)하며 이로 인해 CASE식이 원격 서버로 전송 되지 않습니다. CASE가 로컬에서 평가 되므로 구문 분석기는 과음 CASE이 발생하지 않으며 오류가 없습니다. 테이블 정의를 결과 varchar(max)의 암시 적 유형으로 변경하면 식이 msTVF와 원격 화되고 오류가 발생합니다.CASEvarchar(2)

궁극적으로 CASE원격 서버 에서 초과 중첩 을 평가할 때 오류가 발생 합니다. 이 경우 CASE원격 쿼리 반복자, 오류 결과에 평가되지 않습니다. 예를 들어, 다음은 CONVERT원격이 아닌 것을 포함 하므로 연결된 서버를 사용하더라도 오류가 발생하지 않습니다.

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가 원격 화되지 않음


6

내 직감은 쿼리가 약간 다른 CASE구조를 가지도록 어딘가에 다시 작성되는 것입니다.

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

나는 이것이 당신이 사용하고있는 연결된 서버 공급자에게 버그라고 생각합니다 (사실 아마도 그들 모두-여러 가지에 대해보고 된 것을 보았습니다). 또한 기능을 설명하거나 동작을 설명하는 혼란스러운 오류 메시지에서 수정을 기다리는 동안 숨을 참아서는 안된다고 생각합니다. Server 2000)과 같은 혼란스러운 오류 메시지 보다 훨씬 적은 수의 사람들에게 영향을 미치며 , 이는 동일한 수명 후에도 아직 수정되지 않았습니다.

마찬가지로 바울이 지적 , SQL Server는 확대되고 CASE중첩 된 다양한 표현을하고, 연결된 서버는 좋아하지 않는다. 오류 메시지는 혼란 스럽지만 식의 기본 변환이 즉시 표시되지 않기 때문에 (어떤 방식으로도 직관적이지 않음)에만 가능합니다.

질문에 추가 한 기능 변경 이외의 한 가지 해결 방법은 연결된 서버에서 뷰 또는 저장 프로 시저를 만들고 연결된 서버 공급자를 통해 전체 쿼리를 전달하는 대신 참조하는 것입니다.

또 다른 질문 (질문이 실제로 단순하다고 가정하고 숫자 문자 az를 원한다면)는 다음과 같습니다.

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

이 기능을 그대로 사용하려면 절대적으로 지원이 필요하면 지원팀에 직접 문의하여 사례를 개설하는 것이 좋습니다. 결과를 보증 할 수는 없습니다.이 페이지에서 이미 액세스 할 수있는 해결 방법 만 제공 할 수도 있습니다.


5

이 문제를 해결할 수 있습니다

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

이 문제에 대한 또 다른 해결 방법은 세트 기반 논리를 사용하여 CASE표현식을 왼쪽 조인 (또는 외부 적용)으로 참조 테이블 ( ref아래 코드에서 )로 대체하는 것입니다 ( 영구적, 임시 또는 파생 테이블 / CTE 일 수 있음). 이것이 여러 쿼리 및 절차에서 필요한 경우 영구 테이블로 사용하는 것이 좋습니다.

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

이 문제를 해결하는 한 가지 방법은 when절에 테스트를 포함시키는 것입니다.

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

사실은 아니야 두 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);에 번역 정확히 같은 CASE 표현식과 같이 재정의 실행 계획 (자신에 대한 확인 주시기) CASE WHEN [Union1002]='a' THEN (1) ELSE CASE WHEN [Union1002]='b' THEN (2) ELSE NULL END END- 중첩와 함께, 당신이 볼 수있다.
Andriy M
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.