2009-02-03 16 views
11

Karmaşık mantık ağaçlarını basitleştirmek için this question, a neat answer about using COALESCE'dan. Kısa devre problemini düşündüm. Örneğin, birçok dilde fonksiyonlarda, argümanlar tamamen değerlendirilir ve daha sonra işleve iletilir. C: görünmüyorCOALESCE - kısa devre garantili mi?

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

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

SQL Server COALESCE "fonksiyonu" bir sınırlama olması:

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 

Payda = 0, ben beklenebilir zaman ikinci davayı değerlendiren olsaydı Şöyle bir hata için:

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

Ben Oracle bazı mentionsrelated bulundu. Ve SQL Server ile bazı testler. Kullanıcı tanımlı işlevleri dahil ettiğinizde kısa devrenin bozulabileceği anlaşılıyor.

Bu davranış, ANSI standardı tarafından garanti edilmek zorunda mı?

+1

[Yüksek İlgili] (http://stackoverflow.com/q/7473045/73226) –

+0

((), 1/0 SEÇ 1) COALESCE SEÇİMİ 'DBA cevap özetlemek için' hatasız çalışır ve kısa devre olduğunu gösterir. Tercüman bunu kısaltılmış bir "CASE" ifadesi olarak görüyor. –

cevap

8

Bağlantılı makaleye bir göz attım ve kısa dolaşımın hem COALESCE hem de ISNULL için başarısız olabileceğini onaylayabilirim.

Herhangi bir alt sorgu içeriyorsa başarısız olur, ancak skaler işlevler ve kodlanmış değerler için iyi çalışır. Örneğin

,

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

BIRLEŞIM ANSI standard uygun olarak gerçekleşir. Sadece bir CASE ifadesi için bir kısa yoldur. ISNULL, ANSI standardının bir parçası değildir. Bölüm 6.9 açıkça kısa devre gerektiriyor gibi görünmüyor, ancak when ifadesindeki ilk gerçek yan tümcenin döndürülmesi gerektiği anlamına geliyor.

İşte
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 

HALİNDE ile altta yatan uygulama alt sorguları çalıştırır bazı kanıtıdır: Burada

sayıl bazlı fonksiyonlar (ı SQL Server 2005 üzerine ran) için çalışmalar bazı kanıtıdır.

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

Evet, COALESCE, CASE'e tamamen eşdeğer gibi görünüyor ve aynı şekilde kısa devre yapıyor, ancak, gösterdiğiniz gibi, CASE davranışı her zaman kısa devre yapmıyor, bu gerçekten çok kötü. –

+0

COALESCE, 11g –

+0

'da doğru kısa devreyi (alt sorgularda bile) yapar. Plan 2 tarama gösteriyor olsa bile 2 tablo taraması yapmaz. Bu, 'İSTATİSTİKLERİ IO AÇIK' ile doğrulamak veya yürütme planı özelliklerinde "yürütme sayısını" görmek için basittir. ** ** [bir sorun] (http://connect.microsoft.com/SQLServer/feedback/details/336002/unnecessarily-bad-performance-for-coalesce-subquery) ile "COALESCE" ile oluşmaz. Yine de ISNULL. –

1

Ayrıca cevabın çalıştığını görmek beni şaşırttı! Bu davranışın garanti edildiğinden emin değilim. (Ama çalışmayan bir örnek bulamadım!)

SQL'un beş yılı ve hala şaşırıyorum.

Ben de devam ettim ve bir daha değişti: bende

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 

sonucuydu:

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 

Şimdi daha karıştı! Num = 0 ve den = 0 olduğunda, testcalc'ı nasıl 0 olarak aldım (özellikle son durumdan sonra 0'ı kaldırdığımdan beri!)?

+0

Bu ilk durumda düşmelidir. On yıldan fazla bir SQL Server ve asla COALESCE'i hiç düşünmedim, çünkü bir işlev çağrısı gibi görünüyor. Açıkçası, CASE yapar ve COUTESCE gibi aynı dikişler CASE ile aynı işlevi görür. –

+0

Benim hatam ... tabii ki ilk davaya düşüyor. Şimdi benim işimin işe yaramadığı bir durum bulmak benim görevim :) – Learning

+0

@Öğrenme, genişletilmiş cevabımın bir baktığından emin olun, bazı şeyleri düzeltir. –

3

MS SQL Server kısa devre garanti altına almak için etkili yolu ÖRNEĞİ kullanmaktır. WHEN deyiminin başarısı için başkaları değerlendirilmez. Bu durumda

COALESCE can have issues

, neden BIRLEŞIM/VAKA yapılar içinde bu kadar çok şubeleri var?

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

Cevabımı gör, ISNULL vb .'ye kadar giden CASE ile ilgili bir sorun var ... –

+0

Evet, CASE alt sorgular yapabilir ancak OP'nin sorusuyla ilgili olandan emin değilim. Kısa devre olarak kullandığını gördüm ancak masadaki taramalar veya IO'daki artış nedeniyle kişisel olarak beğenmedim (göstermiş olduğunuz gibi) – gbn

İlgili konular