7

我有三個表:SQL查詢執行快捷方式或邏輯?

SmallTable 
    (id int, flag1 bit, flag2 bit) 
JoinTable 
    (SmallTableID int, BigTableID int) 
BigTable 
    (id int, text1 nvarchar(100), otherstuff...) 

SmallTable有,頂多幾十記錄。 BigTable已經有幾百萬了,實際上是UNION在這個數據庫中的一張表,在同一臺服務器上的另一個數據庫中有一張表。

這裏的加入邏輯:

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=1 OR b.text1 <> 'value1') 

平均加入尺寸爲幾千元的結果。顯示的所有內容都被索引。

對於大多數SmallTable記錄,flag1flag2設置爲1,所以真的沒有必要,甚至訪問BigTable.text1索引,但SQL Server不無論如何,導致代價高昂的索引掃描和嵌套循環。

有沒有更好的方式來暗示到SQL Server,如果flag1flag2都被設置爲1,它應該甚至懶得看text1

其實,如果我可以在這些情況下完全避免與BigTable的連接(JoinTable被管理,所以這不會產生問題),這將使這個關鍵查詢更快。

+0

+1有趣的問題。希望能夠從這裏學到更多! – AdaTheDev 2010-01-25 21:50:11

+0

你提到了一個關於'BigTable'的索引掃描,這是一個視圖。它是索引視圖還是索引掃描在基礎表上執行?你可以在這裏發佈查詢計劃嗎? – Quassnoi 2010-01-27 17:03:41

回答

5

SQL布爾評估確實不是保證運營商短路。請參閱On SQL Server boolean operator short-circuit以獲取一個清晰的示例,說明假設操作員短路可能導致正確性問題和運行時錯誤。

另一方面,我的鏈接中的例子顯示確實爲工作的SQL Server:提供SQL可以使用的訪問路徑。所以,與所有 SQL性能問題和問題一樣,真正的問題不在於表達SQL文本的方式,而在於存儲設計。 IE瀏覽器。查詢優化器可以使用哪些索引來滿足您的查詢?

+0

我同意 - 這本身並不是一個快捷方式問題,但是查詢引擎即使將兩個標誌設置爲1也會將工作檢查爲'text1'的值的問題設置爲1.這是一個索引操作,但是不必要*如果* SmallTable中的所有選定記錄都設置了這些標誌。正如我在其他地方所評論的,我認爲這是一個問題,查詢優化器不夠「足夠聰明」以避免BigTable.text1上的所有記錄不需要文本比較的工作分支。 – richardtallent 2010-01-25 21:34:14

+1

不幸的是,沒有程序/條件查詢樹操作符。換句話說,沒有運營商說'如果條件是真的,沿着這條路走下去,否則這條路'。查詢優化必須創建一個滿足所有可能條件的計劃,即使那些可能性很低的計劃也是如此。查詢計劃可以在許多*確定性*條件下做很多技巧,例如。與可信的外國關係*聯合的兩個表可能*完全消除該計劃中的一個表。這就是所有T-SQL技巧的出現,比如使用UNION而不是OR。 – 2010-01-25 22:24:57

+0

這是一個非常好的評論。我努力說出我的理解,但這個恕我直言是一個很好的解釋。 – AdaTheDev 2010-01-25 22:39:12

0

如果這會更快,而不測試數據不知道......但它聽起來像它可能

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1) AND (s.flag2=1) 
UNION ALL 
SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=0 AND b.text1 <> 'value1') 

請讓我知道發生了什麼

此外,您也許能夠加快這隻需返回此查詢的唯一標識,然後使用該標識的結果獲取所有其他數據。

編輯

這樣的事情?

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1) AND (s.flag2=1) 
UNION ALL 
SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE EXISTS 
    (SELECT 1 from BigTable b 
    WHERE 
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=0 AND b.text1 <> 'value1') 
) 
+0

試過這個,當兩個標誌都設置爲1時,它實際上要慢得多。 – richardtallent 2010-01-25 21:25:51

+0

我想我們可以通過額外的連接和強制順序來做到這一點,但是我的大腦現在正在模糊地想到它。 – Hogan 2010-01-25 21:58:24

0

這不是優雅,但它應該工作...

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1 = 1 and s.flag2 = 1) OR 
    (
     (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%') 
     AND (s.flag2=1 OR b.text1 <> 'value1') 
    ) 
+0

謝謝...試過這個,但執行結果與我所知道的完全相同。 – richardtallent 2010-01-25 21:24:59

1

我不相信SQL Server將像短路條件很遺憾。

所以我建議做2個查詢和聯合他們在一起。首先用s.flag1 = 1和s.flag2 = 1查詢條件,第二個查詢用s.flag1加入到BigTable> 1 a s.flag2 <> 1條件。

This文章對此事是值得一讀,包括底線:

... SQL Server不會做 短路就像是在 其他編程語言完成, 有沒有什麼可以做到的,以強制它 到。

更新:
This文章也是一個有趣的閱讀,幷包含有關這個主題的一些很好的聯繫,包括對SQL Server查詢處理器團隊,簡要地提到,優化器開發經理的TechNet聊天允許短路評估。我從各種文章中得到的總體印象是「是的,優化者可以發現短路的機會,但不應該依靠它,而且你不能強迫它」。因此,我認爲聯盟的做法可能是你最好的選擇。如果它沒有提出一個利用捷徑的機會的計劃,這將歸結爲基於成本的優化器,認爲它是一個合理的計劃,而不是這樣做(這將歸結爲索引,統計數據等) 。

+1

良好的評論,但雖然SQL Server不快捷表達評估,它*做*查詢優化。因此,如果我的查詢僅限於兩個標誌爲「1」的SmallTable結果集,它應該足夠聰明以跳過對「BigTable.text1」的檢查,但這需要根據數據更改執行計劃。我認爲這是核心問題。 – richardtallent 2010-01-25 21:29:03

+0

是的,我看到你在說什麼,但我仍然認爲它是捷徑 - 優化器需要能夠看到短路的機會。我在過去對這類事情做過一些測試,這就是我所看到的(沒有以這種方式進行優化)。 – AdaTheDev 2010-01-25 21:48:11

+0

問題是,對於一個'或'它可以按任何順序進行評估,並且它首先執行右側。 – Hogan 2010-01-25 21:56:00

0

SQL服務器通常抓起子查詢暗示(雖然它是免費丟棄它):

SELECT  * 
FROM  (
      SELECT * FROM SmallTable where flag1 <> 1 or flag2 <> 1 
      ) s 
INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
...