Sql-Server

我應該使用子查詢來幫助 SQL Server 找到正確的計劃嗎

  • June 18, 2015

我有一個在 SQL Server 2008-R2 中執行的查詢(其組件根據使用者在界面中所做的選擇動態建構),該查詢在大約 100 個數據庫中執行。使用者是公司員工,每個公司都有自己的數據庫,不同公司的表間使用模式和數據比例差異很大。這是一個直接影響使用者對系統速度感知的查詢,而且幾乎所有使用者每次使用系統時都需要執行這個查詢,所以值得我花時間去搞定。

我們很幸運地讓 SQL Server 選擇了計劃(因為最佳計劃可能在不同時間在不同數據庫上有所不同),但是隨著我們擴展到更多使用者並添加更多數據庫(和數據庫伺服器),我們發現它只是決定了錯誤的計劃,並且性能從半秒(此時比使用者界面快)到 10 秒或更差(就使用者而言,這實際上被破壞了) )。我終於能夠隔離性能一直很差的情況,所以我現在可以嘗試解決問題。

這是我們目前擁有的(當然,大大簡化了):

SELECT
   -- about 100 columns
FROM
   base_table
LEFT JOIN
   limiting_table
INNER JOIN
   problem_table
LEFT JOIN
   tables_only_used_in_select
INNER JOIN CONTAINSTABLE(
   base_table, *, 'extended text to search for', LANGUAGE 1033) AS rank1 
ON 
   base_table.row_id = rank1.[KEY]
LEFT OUTER JOIN CONTAINSTABLE(
   base_table, *, 'text to search for', LANGUAGE 1033) AS rank2 
ON 
   base_table.row_id = rank2.[KEY]
WHERE
   various criteria on base table

上述查詢中的“problem_table”似乎是關鍵。大多數情況下,這對查詢返回的行沒有影響。我的第一個修復是將其從 INNER JOIN 更改為 LEFT JOIN。這解決了問題。當然,現在我將不得不確保以不同的方式限制結果(這是可能的,但不是一個簡單的程式碼更改來獲取查詢的資訊),並且所有的連接仍然給數據庫一個有機會找到另一個緩慢的計劃。

但是,我也很幸運地嘗試將“重要”連接隔離到子查詢中,這樣該計劃就不會被其他不那麼重要的表分散注意力。像這樣:

SELECT
   -- about 100 columns
FROM (
   SELECT base_table.*
   FROM 
       base_table
   LEFT JOIN
       limiting_table
   INNER JOIN CONTAINSTABLE(
       base_table, *, 'extended text to search for', LANGUAGE 1033) AS rank1 
   ON 
       base_table.row_id = rank1.[KEY]
   LEFT OUTER JOIN CONTAINSTABLE(
       base_table, *, 'text to search for', LANGUAGE 1033) AS rank2 
   ON 
       base_table.row_id = rank2.[KEY]
   WHERE
       various criteria on base table
   ) base_table
INNER JOIN
   problem_table
LEFT JOIN
   tables_only_used_in_select

我是否正在以這種方式看待問題,而我只是沒有足夠的經驗來更好地了解?還是在這種情況下這是一個合理的實用策略(也許並不理想,但值得嘗試並看看它是否有效)?

更新:

以下是在 1 秒內在一個數據庫上執行的原始查詢的計劃(並返回 46 行):

好計劃

這是在不同數據庫中執行的完全相同的查詢,需要 55 秒並返回 369 行:

慢計劃

將問題表上的 INNER JOIN 更改為 LEFT JOIN 會在一秒鐘內執行,並導致以下計劃:

左連接問題表

子查詢也需要一秒鐘,結果是這個計劃:

在此處輸入圖像描述

注意:我真的以為我看到了一些關於發布計劃的正確方法,但我似乎找不到它。如果有人評論有提示,我會努力做得更好。

對於未來的讀者,我繼續測試了這兩種方法(有和沒有子查詢),它們在所有測試環境中都同樣有效。我不知道是否需要在最初有問題的數據庫上更新數據庫統計資訊,但像許多開發人員一樣,我不負責,也沒有任何能力改變它。

據我所知,在這種情況下使用 LEFT JOIN 的好處是它消除了計劃建構者認為這是一件好事(在這種情況下不是)的吸引力,同時仍然讓計劃建構者選擇從所有其他選擇。子查詢的優點是您仍然可以獲得刪除任何(罕見)無效行的 INNER JOIN 能力,但是您不要讓計劃生成器在關鍵計劃中包含該事實(因為 INNER JOIN 在外部查詢中,而不是子查詢)。本質上,您通過將計劃建構器放置在子查詢中並在主查詢中執行其他連接來限制計劃建構器可以使用的連接。

當然,無論何時您限制計劃生成器,您都有可能將其限制得太遠,並且它會提出一個非最佳計劃。展望未來,在我發現需要之前,我不一定會使用這兩種技術。請注意,我還嘗試使用提示或明確告訴 SQL Server 使用特定類型的連接,但這些都沒有我讓它自己選擇所有內容時那麼快。

最後,事實證明,由於建構此查詢的程式碼的醜陋(甚至在執行中),LEFT JOIN 更容易實現並且有信心我不會破壞任何東西,所以我就這樣去了。但是這個決定是由數據庫外部的因素做出的。

引用自:https://dba.stackexchange.com/questions/103925