2009-02-03 2 views
11

this question, a neat answer about using COALESCE에서 복잡한 논리 트리를 단순화합니다. 나는 단락의 문제를 고려했다.COALESCE - 단락이 보장 되었습니까?

예를 들어, 대부분의 언어의 함수에서 인수는 완전히 평가 된 다음 함수로 전달됩니다. C에서 : 표시되지 않습니다

int f(float x, float y) { 
    return x; 
} 

f(a, a/b) ; // This will result in an error if b == 0 

는 SQL Server의 COALESCE "기능"의 제한이 될 수 있습니다 :

CREATE TABLE Fractions (
    Numerator float 
    ,Denominator float 
) 

INSERT INTO Fractions VALUES (1, 1) 
INSERT INTO Fractions VALUES (1, 2) 
INSERT INTO Fractions VALUES (1, 3) 
INSERT INTO Fractions VALUES (1, 0) 
INSERT INTO Fractions VALUES (2, 0) 
INSERT INTO Fractions VALUES (3, 0) 

SELECT Numerator 
    ,Denominator 
    ,COALESCE(
     CASE WHEN Denominator = 0 THEN 0 ELSE NULL END, 
     CASE WHEN Numerator <> 0 THEN Numerator/Denominator ELSE NULL END, 
     0 
    ) AS TestCalc 
FROM Fractions 

DROP TABLE Fractions 

분모 = 0, 내가 기대하는 때 두 번째 경우를 평가한다면 과 같은 오류보고 :

Msg 8134, Level 16, State 1, Line 1 
Divide by zero error encountered. 

을 나는 오라클 일부 mentionsrelated를 발견했다. SQL Server으로 일부 테스트. 사용자 정의 함수를 포함하면 단락이 발생할 수 있습니다.

그래서이 동작은 ANSI 표준에 의해 보장되어야합니까?

+1

[고 관련] (http://stackoverflow.com/q/7473045/73226) –

+0

를 (,() 1/0를 선택) COALESCE을 선택'의 DBA의 대답을 요약하면 '오류없이 실행되고 그것이 단락되었음을 보여줍니다. 통역사는 이것을 'CASE'라고 짧게 봅니다. –

답변

8

나는 링크 된 기사를 보았으며 단락 회로가 COALESCE와 ISNULL 모두에서 실패 할 수 있음을 확인할 수 있습니다.

하위 쿼리가 포함되어 있으면 실패하지만 스칼라 함수와 하드 코딩 된 값에 대해서는 문제가 없습니다. 예를 들어

,

DECLARE @test INT 
SET @test = 1 
PRINT 'test2' 
SET @test = COALESCE(@test, (SELECT COUNT(*) FROM sysobjects)) 
SELECT 'test2', @test 
-- OUCH, a scan through sysobjects 

유착은 ANSI standard에 따라 구현된다. CASE 문에 대한 축약어입니다. ISNULL은 ANSI 표준의 일부가 아닙니다. 섹션 6.9에서는 명시 적으로 단락을 요구하지 않지만, when 문에서 첫 번째 true 절이 반환되어야 함을 의미합니다.

다음
CREATE FUNCTION dbo.evil 
(
) 
RETURNS int 
AS 
BEGIN 
    -- Create an huge delay 
    declare @c int 
    select @c = count(*) from sysobjects a 
    join sysobjects b on 1=1 
    join sysobjects c on 1=1 
    join sysobjects d on 1=1 
    join sysobjects e on 1=1 
    join sysobjects f on 1=1 
    return @c/0 
END 
go 

select dbo.evil() 
-- takes forever 

select ISNULL(1, dbo.evil()) 
-- very fast 

select COALESCE(1, dbo.evil()) 
-- very fast 

이 경우에 기본이되는 구현에 하위 쿼리를 실행합니다 몇 가지 증거 : 여기

스칼라 기반 기능 (내가 SQL Server 2005에 그것을 실행)에 대한 작품 일부 증거입니다.

DECLARE @test INT 
SET @test = 1 
select 
    case 
     when @test is not null then @test 
     when @test = 2 then (SELECT COUNT(*) FROM sysobjects) 
     when 1=0 then (SELECT COUNT(*) FROM sysobjects) 
     else (SELECT COUNT(*) FROM sysobjects) 
    end 
-- OUCH, two table scans. If 1=0, it does not result in a table scan. 
+0

네, 마치 COALESCE가 CASE와 완전히 같아 보이고 같은 방법으로 쇼트되지만, CASE의 동작이 항상 쇼트 회로가되는 것은 아니기 때문에 실제로는 상당히 심합니다. –

+0

COALESCE는 11g –

+0

에서 단락 회로를 올바르게 (단락조차도) 단락시킵니다. 계획에 2 회의 스캔이 표시 되더라도 ** 테이블 스캔을 두 번하지 않습니다 **. 이것은 SET STATISTICS IO ON으로 확인하거나 실행 계획 속성에서 "실행 횟수"를보기 만하면됩니다. ** ** [문제] (http://connect.microsoft.com/SQLServer/feedback/details/336002/unnecessarily-bad-performance-for-coalesce-subquery)와 함께 발생하지 않는 'COALESCE'가 있습니다. 'ISNULL '. –

1

나는 또한 대답이 작동하는 것을보고 놀랐습니다! 이 동작이 보장되는지 모르겠습니다. (그러나 나는 작동하지 않는 예제를 찾을 수 없었습니다!)

5 년 동안 SQL, 나는 여전히 놀랐습니다.

는 또한 나서서 한 번 더 변화했다 : 내가 가진

INSERT INTO #Fractions VALUES (0, 0) 

SELECT Numerator 
    ,Denominator 
    ,coalesce (
     CASE WHEN Denominator = 0 THEN 0 ELSE NULL END, 
     CASE WHEN Numerator <> 0 THEN Numerator/Denominator ELSE NULL END) 
    AS TestCalc 
FROM #Fractions 

결과가 있었다 :

Numerator Denominator TestCalc 
1    1   1 
1    2   0.5 
1    3   0.3333333333333335 
1    0   0 
2    0   0 
3    0   0 
0    0   0 

가 지금은 더 혼란 스러워요을! num = 0과 den = 0 인 경우, testcalc를 어떻게 0으로 만들었습니까 (특히 지난 사건 이후 0을 제거한 이후!)?

+0

첫 번째 경우에 해당됩니다. SQL Server의 10 년에 걸쳐서, 나는 COALESCE가 함수 호출과 같이 보이기 때문에 결코 짧게 처리하지 않을 것이라고 생각하지 않았습니다. 분명히 CASE는 CALESCE가 CASE와 동일하게 기능하도록 정의 된 솔기가 있습니다. –

+0

내 잘못 ... 물론 그것은 첫 번째 경우에 빠지다. 이것이 작동하지 않는 경우를 찾는 것이 내 인생의 목표입니다. – Learning

+0

@Learning, 확장 된 답변을 보시고, 몇 가지 사항을 수정하십시오. –

3

MS SQL 서버에서 단락을 보장하는 효율적인 방법은 CASE를 사용하는 것입니다. 성공 WHEN 절의 경우, 다른 WHO 절이 평가되지 않습니다. 이 경우

COALESCE can have issues

, 왜 COALESCE/CASE 구조에 많은 지점을 가지고?

SELECT Numerator 
    ,Denominator 
    ,CASE 
     WHEN Denominator = 0 THEN 0 END, 
     ELSE Numerator/Denominator 
    END AS TestCalc 
FROM Fractions 
+0

내 대답을 참조하십시오. ISNULL 등으로 흘러가는 CASE의 근본적인 문제가 있습니다. –

+0

예, CASE는 하위 쿼리를 수행 할 수 있지만 OP의 질문과의 관련성은 확실하지 않습니다. 나는 그것이 단락 회로로 사용되는 것을 보았습니다.하지만 개인적으로 테이블 스캔이나 IO 증가 때문에 그것을 좋아하지 않습니다. – gbn