Sql-Server

同一條 SQL 語句的執行計劃不同

  • January 30, 2015

作為此問題的背景,可以在此連結中找到詳細資訊。

簡而言之:除了RIGHT表之外,我有 2 個幾乎相同的 SQL 語句。是相同的LEFT JOIN,並且由於表不同,因此返回的列會有所不同。數據庫從 2008 年移至 SQL Server 2012 實例。在 2012 實例上,1 個查詢在 3 秒內執行,另一個則需要將近 2 小時才能執行。執行計劃不同,並發佈在連結上。

但是,我可以在 SQL Server 2008 伺服器上執行這兩個查詢並在 3 秒內完成它們。2008 伺服器上的兩個執行計劃與 2012 伺服器上的“良好”執行計劃相同。

我有一個可以工作的新語句,但它使用的應用程序是使用內聯 SQL 的 C# Windows 應用程序。我不想打破這個 1 聲明的模式。任何人都可以闡明這個問題嗎?

查詢優化器在為此查詢建構執行計劃時有許多選擇。在眾多可用的策略中,它可以在散列連接和嵌套循環連接之間進行選擇。它決定使用哪一個敏感地取決於可用的統計資訊,以及其他因素,例如為 SQL Server 配置的記憶體量。

恰好優化器在一種情況下選擇嵌套循環策略,而在其他情況下選擇雜湊連接。如果您要強制使用散列連接(例如,使用OPTION (HASH JOIN)目前正在使用嵌套循環連接的查詢提示,您會發現嵌套循環計劃的估計成本似乎是優化器更便宜的選擇。

這不是錯誤。這是一個相當正常的計劃選擇範例,它對可用的統計資訊(除其他外)敏感。嵌套循環連接在現實中表現如此糟糕的事實是查詢和數據庫設計對優化器不太友好的結果。鑑於要處理的資訊質量非常低,優化器的計劃選擇幾乎不比猜測好。

無論如何,假設您需要在不更改原始碼的情況下避免性能不佳的計劃形狀(順便說一下,這是一個更喜歡儲存過程而不是內聯 SQL 的原因),您有兩個主要選擇:

第一種是使用計劃指南來強制目標查詢的“好”計劃形狀。如果您以前沒有使用過計劃指南,這是相當高級的工作。如果範例中指定的文字值可能不同,則將涉及額外的步驟。

第二種選擇是為優化器提供一個更有用的索引以供使用。在這種情況下,這涉及添加一個計算列(一個快速的元數據操作),然後為新列建立索引:

-- Metadata-only operation
ALTER TABLE dbo.InHouse_CSV_Backup
ADD MERSNUMBER_CC AS 
   REPLACE(LTRIM(RTRIM([MERSNUMBER])),'-', '');

-- Index on computed column
CREATE NONCLUSTERED INDEX
   IX_dbo_InHouse_CSV_Backup__MERSNUMBER_CC
ON dbo.InHouse_CSV_Backup (MERSNUMBER_CC)
INCLUDE (MERSNUMBER);

查詢很可能使用索引,從而導致更好的計劃穩定性和很可能也有良好的性能。無論如何,它都不是一個完美的解決方案,但考慮到可用的資訊,它是一個相對簡單且不引人注目的解決方案。

未來改進的潛在領域:

  • 考慮儲存過程而不是在應用程序中嵌入 SQL
  • 考慮NOT EXISTS 用空拒絕代替外連接
  • 將字元串與尾隨空格進行比較時避免不必要的 RTRIM
  • 小心隱式類型轉換。例如,MERSMinvarchar(300)但是MERSNUMBER( nvarchar(31)Unicode)。表達式的結果具有相似的派生類型,例如REPLACE被認為產生nvarchar(4000).

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