2011-03-09 30 views
5

我想我總是天真地認爲SQL查詢的選擇部分中的標量函數將只適用於滿足where子句的所有條件的行。執行SQL語句時可以在過濾之前應用標量函數嗎?

今天我正在調試供應商的一些代碼,並有這個假設的挑戰。我能想到的代碼失敗的唯一原因是Substring()函數正在調用應該被WHERE子句過濾掉的數據。但是,似乎在過濾發生之前正在應用子串調用,查詢失敗。 這裏是我的意思的例子。假設我們有兩個表格,每個表格有2列,分別有2行和1行。每個中的第一列只是一個id。 NAME只是一個字符串,NAME_LENGTH告訴我們名稱中有多少個字符具有相同的ID。請注意,只有具有多個字符的名稱在LONG_NAMES表中具有對應的行。

NAMES: ID, NAME 
    1, "Peter" 
    2, "X" 
LONG_NAMES: ID, NAME_LENGTH 
    1, 5 

如果我想查詢打印每名以切斷最後3個字母,我可能會先嚐試這樣的事情(假設SQL Server語法現在):

SELECT substring(NAME,1,len(NAME)-3) 
    FROM NAMES; 

我很快會發現這會給我一個錯誤,因爲當它到達「X」時,它會嘗試在子串調用中使用負數,並且它會失敗。 我的供應商決定解決這個問題的方式是過濾掉那些字符串太短而無法使len - 3查詢正常工作的行。他通過加入另一個表來完成它:

SELECT substring(NAMES.NAME,1,len(NAMES.NAME)-3) 
    FROM NAMES 
     INNER JOIN LONG_NAMES 
      ON NAMES.ID = LONG_NAMES.ID; 

乍一看,這個查詢看起來像可能工作。連接條件將消除任何具有足夠短的NAME字段的行,以使子字符串調用失敗。

但是,從我可以觀察到的情況來看,SQL Server有時會嘗試計算表中所有內容的子字符串表達式,然後應用聯接來過濾掉行。這是否應該以這種方式發生?是否有一個記錄的操作順序,我可以找出什麼時候會發生某些事情?它是特定於某個特定的數據庫引擎還是SQL標準的一部分?如果我決定在我的NAMES表中包含一些謂詞來過濾短名稱(如len(NAME)> 3),SQL Server是否也可以在嘗試應用子字符串後選擇應用該謂詞?如果是這樣,那麼似乎做一個子字符串的唯一安全方法是將其包裝在select中的「case when」構造中?

+4

是的。 'CASE'是這樣做的唯一安全方式。請參閱http://stackoverflow.com/questions/5191701/tsql-divide-by-zero-encountered-despite-no-columns-containing-0/5203211#5203211以獲得有關此主題的良好答案。 – 2011-03-09 17:40:34

+0

@Martin感謝您的鏈接。我無法弄清楚如何搜索類似的問題,因爲它是抽象的。 – 2011-03-09 17:46:34

回答

0

您正在考慮稱爲查詢執行計劃的內容。它基於查詢優化規則,索引,節奏緩衝區和執行時間統計信息。如果您使用的是SQL Managment Studio,那麼您的查詢編輯器上會有一個工具箱,您可以查看預計的執行計劃,它會顯示您的查詢將如何改變以獲得某種速度。因此,如果只是使用了您的名稱表並且它在緩衝區中,引擎可能會首先嚐試子查詢您的數據,然後將其與其他表加入。

2

Martin給出了這個鏈接,可以解釋發生了什麼 - 查詢優化器可以自由地重新排序,但它喜歡的東西。我將此列爲答案,以便我可以接受某些事情。馬丁,如果你用你的鏈接創建一個答案,我會很樂意接受,而不是這個。

我確實想在這裏留下我的問題,因爲我認爲這是一個棘手的問題,而且我的問題的特定措辭可能更容易讓其他人在未來找到答案。

TSQL divide by zero encountered despite no columns containing 0

編輯:隨着越來越多的反應都來了,我又糊塗了。目前還不清楚何時允許優化器在select子句中評估事物。我想我必須自己去查找SQL標準,看看我能否理解它。

+0

符合SQL標準的dbms有一些*自由重新排序的東西,但它不能自由地重新排序,只是它喜歡的東西。仍然需要產生相同的結果,作爲對查詢進行非優化,強力,循序漸進的鉛筆和紙張評估。在這種情況下,我非常確定這意味着它必須像評估WHERE子句一樣來評估SELECT子句。 (請參閱我的答案,並在附近的某處參考。) – 2011-03-09 17:59:21

+1

@Catcall - 此前已在Microsoft Connect站點中提出過。請參閱https://connect.microsoft.com/SQLServer/feedback/details/537419/sql-server-should-not-raise-illogical-errors – 2011-03-09 18:03:22

+0

我想我寧願說「它可以自由地重新排序,但是隻要它仍然產生與未經優化的,強力的,逐步的,相同查詢的鉛筆和紙張評估相同的結果「。 – 2011-03-09 18:30:42

1

曾幫助編寫早期SQL標準的Joe Celko幾次在各種USENET新聞組中發佈了類似的內容。 (我跳過了那些不適用於你的SELECT語句的子句)。他通常會說「這就是語句應該如何工作」。換句話說,SQL實現應該像執行這些步驟一樣執行,而實際上並不需要執行這些步驟中的每一個。

  1. 構建所有的 表構造一個工作表在FROM子句 。
  2. 從工作表中刪除那些不符合WHERE 子句的行。
  3. 針對工作表構建 SELECT子句中的表達式。

因此,在此之後,沒有SQL dbms應該像在SELECT子句中評估函數一樣,然後它會像應用WHERE子句一樣操作。

在最近的發帖中,Joe expands the steps to include CTEs

CJ Date和Hugh Darwen在他們的書SQL指南的第11章(「表格表達式」)中說基本相同的東西。他們還指出,本章對應於SQL標準中的「查詢規範」部分(章節?)。

+0

連接是否被認爲是where子句的一部分還是單獨的階段? – 2011-03-09 19:12:21

+0

在我讀的東西中,JOIN子句被認爲是FROM子句中的許多「表構造函數」之一。 – 2011-03-09 19:39:23

相關問題