2011/11/15

Trap of SQL "else if" : 關於 SQL 語法 "else if" 的陷阱

今天在工作上看到了一段 SQL 碼,大概是寫著
if @str='a'
    @result=@result+'aaa'
else if @str='b'
    @result=@result+'bbb'
else if @str='c'
    @result=@result+'ccc'
這勾起我的一段回憶,不過有點忘了原因,只記得因為那次的原因,讓我以後在寫 SQL 的 if...else 時,都一定會加上 begin...end

但現在想想,忘了原因卻還繼續堅持加上 if...else 的 begin...end 以後要求別人照做時似乎也站不住腳,因此決定追本朔源並整理出來。

首先,上述那樣的寫法並不會有問題,我當時出現的問題是發生在巢狀 if...else 之中,
declare @var int
set @var=11

if @var <= 10
    if @var <=3
        print '<=3'
    else if @var <=6
        print '<=6'
    else
        print '>6'
else if @var <= 20
    if @var <=13
        print '<=13'
    else if @var <=16
        print '<=16'
    else
        print '>16'
print 'end'

-- 執行結果:
-- <=13
-- end 
如同上述的寫法,此功能完成後,其實也沒什麼事情,該跑到的結果都會跑到。

但如果哪天我不再需要'>6'的分支,是否將它刪掉就可以了呢?
答案是:
declare @var int
set @var=11

if @var <= 10
    if @var <=3
        print '<=3'
    else if @var <=6
        print '<=6'
    --else
    --    print '>6'
else if @var <= 20
    if @var <=13
        print '<=13'
    else if @var <=16
        print '<=16'
    else
        print '>16'
print 'end'

-- 執行結果:
-- end 
「<=13 不見了!」,發生了什麼事?(是阿,那時在一千多行 SQL 碼的 Stored Procedure 裡跑不出某一段的結果,頭皮有麻了一下)

嗯~冷靜一下,再想想 if...else 的原理,其實這邊有個誤會。

"else if" 並不是一個正式的語法,後面的 if 只是 else 的一部分內容,我再把不需要'>6'分支的內容縮排整理一下:
declare @var int
set @var=11

if @var <= 10
    if @var <=3
        print '<=3'
    else 
        if @var <=6
            print '<=6'
        --else
        --    print '>6'
        else 
            if @var <= 20
                if @var <=13
                    print '<=13'
                else 
                    if @var <=16
                        print '<=16'
                    else
                        print '>16'
print 'end'

-- 執行結果:
-- end 
這樣應該就能理解為何 @var=11 跑不出 <=13 的原因了。

所以,再回到原來的問題,不再需要'>6'的分支時,該怎麼寫呢?
請明確的標示出 begin...end
declare @var int
set @var=11

if @var <= 10
begin
    if @var <=3
        print '<=3'
    else if @var <=6
        print '<=6'
    --else
    --    print '>6'
end
else if @var <= 20
begin
    if @var <=13
        print '<=13'
    else if @var <=16
        print '<=16'
    else
        print '>16'
end
print 'end'

-- 執行結果:
-- <=13
-- end 
不過,我自己會全部標上 begin...end (一朝被蛇咬...
declare @var int
set @var=11

if @var <= 10
begin
    if @var <=3
    begin
        print '<=3'
    end
    else if @var <=6
    begin
        print '<=6'
    end
    --else
    --begin
    --    print '>6'
    --end
end
else if @var <= 20
begin
    if @var <=13
    begin
        print '<=13'
    end
    else if @var <=16
    begin
        print '<=16'
    end
    else
    begin
        print '>16'
    end
end
print 'end'

-- 執行結果:
-- <=13
-- end 
以上,就是我那 if 必加 begin...end 的小小原因啦,
感謝捧場。





One More Thing ...
難道,C# 沒這個問題嗎?試試看...
    int var=11;

    if (var <= 10)
        if (var <= 3)
            System.Diagnostics.Debug.Write("<=3");
        else if (var <= 6)
            System.Diagnostics.Debug.Write("<=6");
        else
            System.Diagnostics.Debug.Write(">6");
    else if (var <= 20)
        if (var <= 13)
            System.Diagnostics.Debug.Write("<=13");
        else if (var <= 16)
            System.Diagnostics.Debug.Write("<=16");
        else
            System.Diagnostics.Debug.Write(">16");
直接註解 '>6' 分支,
    if (var <= 10)
        if (var <= 3)
            System.Diagnostics.Debug.Write("<=3");
        else if (var <= 6)
            System.Diagnostics.Debug.Write("<=6");
        //else
        //    System.Diagnostics.Debug.Write(">6");
    else if (var <= 20)
        if (var <= 13)
            System.Diagnostics.Debug.Write("<=13");
        else if (var <= 16)
            System.Diagnostics.Debug.Write("<=16");
        else
            System.Diagnostics.Debug.Write(">16");
等等,Visual Studio 厲害的地方在於它會自動縮排,刪掉最後一個分號再重加分號,讓它排一下,
    int var=11;

    if (var <= 10)
        if (var <= 3)
            System.Diagnostics.Debug.Write("<=3");
        else if (var <= 6)
            System.Diagnostics.Debug.Write("<=6");
        //else
        //    System.Diagnostics.Debug.Write(">6");
        else if (var <= 20)
            if (var <= 13)
                System.Diagnostics.Debug.Write("<=13");
            else if (var <= 16)
                System.Diagnostics.Debug.Write("<=16");
            else
                System.Diagnostics.Debug.Write(">16");
看來,結果和 SQL 的相同,如果以同樣的寫法,哪天還是去只註解掉 '>6' 的那段,又沒有使用自動縮排,我想誤會還是會發生吧。
(想到之前有個同事說,他寫 C# 的 if 判斷式就算是只有單行的內容,還是堅持要加大括號。該不會是相同的原因吧?

沒有留言:

張貼留言