2009-02-03 9 views
11

Od this question, a neat answer about using COALESCE w celu uproszczenia złożonych drzew logicznych. Rozważyłem problem zwarcia.WĘGIEL - gwarantowane zwarcie?

Na przykład w funkcjach w większości języków argumenty są w pełni analizowane, a następnie przekazywane do funkcji. C:

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

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

To nie wydaje się być ograniczenie „funkcja” COALESCE w SQL Server:

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 

Gdyby były oceny drugi przypadek, gdy mianownik = 0, spodziewałbym zobaczyć błąd jak:

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

znalazłem niektóre mentionsrelated Oracle. I niektóre testy z SQL Server. Wygląda na to, że zwarcie może się zepsuć, jeśli uwzględnisz funkcje zdefiniowane przez użytkownika.

Czy to zachowanie powinno być zagwarantowane przez normę ANSI?

+1

[spokrewnionych] (http://stackoverflow.com/q/7473045/73226) –

+0

Podsumowując odpowiedź DBA, 'SELECT COALESCE (1, (SELECT 1/0))' działa bez błędu i pokazuje, że to zwarcie. Interpreter traktuje to jako skróconą instrukcję "CASE". –

Odpowiedz

8

Właśnie obejrzałem powiązany artykuł i mogę potwierdzić, że zwarcie może zakończyć się niepowodzeniem zarówno dla COALESCE, jak i ISNULL.

Wydaje się nie działać, jeśli masz jakieś pod-zapytanie, ale działa dobrze dla funkcji skalarnych i wartości zakodowanych.

Przykładowo

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

COALESCE jest realizowany zgodnie z ANSI standard. Jest to po prostu skrót do instrukcji CASE. ISNULL nie jest częścią standardu ANSI. Sekcja 6.9 nie wydaje się wyraźnie wymagać zwarcia, ale oznacza, że ​​powinna zostać zwrócona pierwsza prawdziwa klauzula w oświadczeniu when.

Oto dowód, że to działa dla funkcji skalarnych podstawie (i prowadził ją na SQL Server 2005):

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 

Oto dowód, że podstawowa realizacja z CASE wykona sub kwerend.

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

Tak, wygląda na to, że COALESCE jest całkowicie równoważny CASE, i powoduje zwarcia w ten sam sposób, jednak, jak pokazujesz, zachowanie CASE nie zawsze powoduje zwarcie, co jest naprawdę bardzo nieprzyjemne. –

+0

COALESCE wykonuje poprawnie zwarcie (nawet z podzapytaniami) w 11g –

+0

Wykonuje ** nie ** do 2 skanów stołów, nawet jeśli plan pokazuje 2 skany. Można to łatwo zweryfikować za pomocą opcji "USTAW STATYSTYKI IO ON" lub po prostu spojrzeć na "liczbę wykonań" we właściwościach planu wykonawczego. Tam ** jest ** [problem] (http://connect.microsoft.com/SQLServer/feedback/details/336002/niepotrzebnie-bad-profildo-do-przeprowadzania-próbki-) z 'COALESCE', która nie występuje z 'ISNULL' chociaż. –

1

Byłem również zaskoczony, że ta odpowiedź działa! Nie jestem pewien, czy to zachowanie jest gwarantowane. (Ale nie udało mi się znaleźć przykładu, który nie działa!)

Pięć lat SQL, i nadal jestem zaskoczony.

ja też poszedł do przodu i zrobił jeszcze jedną zmianę:

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 

Wynik Dostałem:

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 

teraz jestem jeszcze bardziej mylić! Dla przypadku gdy liczba = 0 i den = 0, jak uzyskałem testcalc jako 0 (zwłaszcza, że ​​usunąłem 0 po ostatnim przypadku!)?

+0

To powinno należeć do pierwszego przypadku. Przez ponad dekadę SQL Server i nigdy nie uważałem COALESCE za zwodniczy, ponieważ wygląda jak wywołanie funkcji. Oczywiście CASE ma, a jego szwy jak COALESCE są zdefiniowane tak, aby funkcjonowały identycznie jak CASE. –

+0

moje złe ... oczywiście wpada w pierwszym przypadku. Teraz misją mojego życia jest znalezienie przypadku, w którym to nie zadziała :) – Learning

+0

@Learning, upewnij się, że zapoznałeś się z moją rozszerzoną odpowiedzią, poprawia ona pewne rzeczy. –

3

wydajny sposobem na zagwarantowanie zwarcie w MS SQL Server jest użycie sprawy. W przypadku klauzuli WHEN sukcesu, żadne inne nie są oceniane.

COALESCE can have issues

W tym przypadku, dlaczego tak wiele oddziałów w konstruktów COALESCE/CASE?

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

Zobacz moja odpowiedź, istnieje podstawowy problem z CASE, który przepływa do ISNULL etc ... –

+0

Tak, CASE może wykonywać podzapytania, ale I0m nie jestem pewien stosowności do pytania OP. Widziałem to jako zwarcie, ale nie podoba mi się to osobiście z powodu skanowania tabeli lub zwiększenia IO (jak wykazałeś) – gbn

Powiązane problemy