2012-05-22 77 views
9

道歉,如果這不適合,但真的這是'爲什麼',而不是'如何'。不知道這是否合適,但不知道一個更好的地方要問,我想不出如何說出谷歌來獲得我正在尋找的東西。IF條款的SQL評估

IF 'hell' = 'freezing over' 
    BEGIN 
    SELECT log(0) 
    END 

看看那句話。沒有哪個世界中的IF條款是真實的。如果我試圖運行它,我希望SQL跳過IF子句並移動到最後。相反,我得到:

An invalid floating point operation occurred. 

這是奇怪的。所以我想這就是SQL這樣做的方式。除...

IF 'hell' = 'freezing over' 
    BEGIN 
    SELECT 1/0 
    END 

這裏沒有錯誤。 IF子句中的語句仍然會產生錯誤。任何人都可以解釋爲什麼這不會發生?

這是在調試大量SQL計算時使用EXP(SUM(LOG()))在if子句中累積數據的過程。我可以改變代碼以阻止這種情況再次發生,但爲什麼它在未滿足的IF子句中評估某些內容。

乾杯。

編輯:額外的娛樂。試着抓? Pffft

IF 1=2 
    BEGIN 
     BEGIN TRY 
      SELECT SQRT(-1) 
     END TRY 
     BEGIN CATCH 
     END CATCH 
    END 

非數學:

IF 1=2 
    BEGIN 
    SELECT SUBSTRING('hello',-1,-1) 
    END 

回答

7

我的猜測是,log(0)有效過早評估由於constant-folding1/0不是,要麼是由於其基數估計或更可能的事實ANSI_WARNINGS設置將被零(溢出VS影響鴻溝的理想結果空值)。

+0

ANSI警告沒有區別,但這是我認爲我需要的鏈接,所以非常感謝:) –

+0

歡迎您。通過ANSI_WARNINGS我的意思是,如果在任何時候發佈「set ansi_warnings off」,那麼您將看不到除零錯誤。由於編譯器無法預先知道ANSI_WARNINGS標誌在編譯查詢運行時的狀態,因此它必須忽略在編譯時引發零除異常的任何摺疊表達式,因爲它無法預測所需的行爲。 (如果ansi_warnings保證始終關閉1/0將摺疊爲NULL) –

2

解析器根本沒有的智慧追隨你的IF邏輯。請看下面的例子:

IF (1 = 0) 
BEGIN 
    CREATE TABLE #t(x INT): 
END 
ELSE 
BEGIN 
    CREATE TABLE #t(x INT): 
END 

無論你執行它甚至只是解析它,解析器着眼於所有批次的CREATE TABLE語句,並確定您試圖創建表兩次(第一個副本顯然沒有按這不會發生)。結果:

消息2714,級別16,狀態1,行7
已經有一個數據庫中的命名對象 '#T'。

我真的不知道我是否有更好的答案,而不是解析器不如你那麼聰明。

您可以通過使用動態SQL將推遲的問題推遲到運行時來擊敗解析器,例如,

IF 'hell' = 'freezing over' 
BEGIN 
    EXEC sp_executesql N'SELECT log(0);'; 
END 

但後來我不得不懷疑,究竟是建立腳手架,這將永遠是真實的情況,併發出你知道是怎麼回事錯誤語句的意義呢?

+0

大約有5 IF條件。其中一項計算是在其他條件下會導致錯誤的數據上完成的。 這些例子很容易顯示行爲。它更像是 IF [數據不會導致錯誤] 「Do calc」 END –

+0

另外,有趣的是它解析得很好,但不能成功執行 –

+0

是的解析器並不總是提前捕獲所有類型的錯誤,並提前捕獲一些在運行時永遠不會出錯的東西。正如我所說,這不是最聰明的。但它確實有很多複雜的規則和算法來確定它將允許解析的成功與否。這些規則沒有記錄,所以@GordonLinoff解釋說你只需要知道什麼樣的事情會使解析器發生變化,以及它會讓什麼樣的事情滑動​​。 –

0

顯然,SQL編譯器試圖在編譯時與運行時評估一些表達式。我的猜測是,一個部門被認爲不太昂貴,所以它推遲到運行時間。另一方面,像log()這樣非常複雜的東西價格昂貴,他們希望在編譯時做到這一點。

這是一個猜測。這也意味着這種差異是非確定性的。你必須找出哪一個工作或不工作在一個特定的腳本 - 並且行爲可能會改變數據庫的版本之間。

2

如果我試圖運行它我期待SQL跳過去IF子句和移動到結束。

當您運行批處理三件事情發生

  1. 你的SQL解析

  2. 你的SQL編譯

  3. 你的SQL執行

什麼是不明rtunate是SQL Server中批處理中的編譯和執行錯誤導致相同的「Query Completed with errors」消息。所以讓我們用一個程序,其中它更容易看到其中的差別

考慮以下

Create proc compiletime 
as 
SELECT log(0) 
SELECT SQRT(-1) 
SELECT SUBSTRING('hello',-1,-1) 
SELECT 1/0 

該程序解析罰款。然而它不能被編譯,除非我們刪除第一個三選擇,因爲我們有一些常量作爲參數是無效的。如果SELECT 1/0也會導致編譯時錯誤而不是運行時錯誤,但是由於@Alex K指出該行爲基於ANSI_WARNINGS,所以它不是編譯時錯誤。

所以這就是爲什麼我們看到當第2之間的差異。它也解釋了爲什麼TRY CATCH從編譯時錯誤起不起作用。

現在爲什麼SQL服務器編譯可達代碼。因爲一般爲了知道它無法訪問需要解決停止問題。你可以解決它的一些案件,但隨後這個...


DECLARE @x as integer 
SET @x = SomeFunction() 
If (1 = @x) 
    SomeCompiletime error 

會有不同的行爲,這是更加混亂。

if (1=0) 
    SomeCompiletime error