回答

收藏

COALESCE-保证短路?

技术问答 技术问答 254 人阅读 | 0 人回复 | 2023-09-14

从这个问题出发,是关于使用的COALESCE简化复杂逻辑树的简洁答案。我考虑了短路。
/ p6 C+ Q. A  j/ u/ T- t例如,在大多数语言函数中,对参数进行综合评估,然后传递给函数。C中:8 w, e1 [# N$ x( t2 `! Z5 m. [
int f(float x,float y)    return x;}f(a,a / b) ; // This will result in an error if b == 0这似乎不是COALESCESQL Server中功能的限制:2 L5 G  H1 Z3 o  d  Y
AS TestCalcFROM FractionsDROP TABLE Fractions       0    ) AS TestCalcFROM FractionsDROP TABLE Fractions假如它正在评估分母= 0的第二种情况,那么我期待看到以下类似的错误:
: E5 \0 H9 J$ [/ I! R! iMsg 8134,Level 16,State 1,Line 1Divide by zero error encountered.我发现了一些提到
; E9 j( b: T+ i2 q3 k- v相关甲骨文。并使用。SQL! q" A9 T# A' u2 q% Q# t  C0 a
Server进行了一些测试。包含用户定义函数时,短路可能会中断。
! z$ s1 P9 }# B8 ^2 m  n" e那么,这种行为应该由吗?ANSI保证标准?0 X' f+ R  b( E8 v( \& N+ i
                                                                # @6 V% Y0 N. w* Z1 Y. [" Q
    解决方案:                                                               
- L: r1 C# _$ P, r& u                                                                我只是看了链接的文章,可以确认短路COALESCE和ISNULL都可能失败。
# y! @; q6 S/ A' Y0 l如果涉及到任何子查询,它似乎会失败,但是它对于标量函数和硬编码值可以正常工作。0 Q4 W- J: t6 g! R
例如,
) u3 U1 S' l1 k* A8 JDECLARE @test INTSET @test = 1PRINT 'test2'SET @test = COALESCE(@test,(SELECT COUNT(*) FROM sysobjects))SELECT 'test2',@test-- OUCH,a scan through sysobjectsCOALESCE是根据ANSI实施标准CASE句子的简写形式。ISNULL不是ANSI标准的一部分.9节似乎没有明确要求短路,但它确实暗示when应返回句子中的第一个true子句。
1 [4 P% a0 L7 Q9 d: L! X这是一些适合基于标量函数的证明(我在SQL Server: u: O# w" C) L+ ]0 Z8 P4 r& a
它在2005上运行):
/ k/ Y1 n' A- tCREATE FUNCTION dbo.evil()RETURNS intASBEGIN    -- 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 / 0ENDgoselect dbo.evil()-- takes foreverselect ISNULL(1, dbo.evil())-- very fastselect COALESCE(1, dbo.evil())-- very fast这证明CASE执行子查询的基础实现。7 Q: T; O3 |4 K; \. b
DECLARE @test INTSET @test = 1select    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.
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则