Sql-Server

實際行和估計行差異很大

  • December 22, 2021

完整的實際計劃在這裡。

在執行計劃之前(因為我正在調試一個執行不佳的計劃)我有這個變數分配塊:

DECLARE @Days INT = 180
DECLARE @DateRangeFrom DateTime = DATEADD(d, -@Days, getDate())
DECLARE @DateRangeTo DateTime = getDate()
DECLARE @FacilityID INT = 1010
DECLARE @Answer0 INT = 1879
DECLARE @Answer1 INT = 1949
DECLARE @Answer1SetID INT = 1607
DECLARE @Answer2 INT = 1907
DECLARE @Answer2SetID INT = 1593

我的第一個問題是我在 IRItemAnswer_Info 表(節點 ID 19)上執行的查找。它溢出到 Tempdb,它已經開始錯誤地開始查詢。它引用了IRItemAnswerInfo_DGItemID_AnswerSourceID索引,這是正確的索引,因為我在DGItemIDand上匹配AnswerSourceID,然後返回IncidentID。索引創建為

CREATE NONCLUSTERED INDEX IRItemAnswerInfo_DGItemID_AnswerSourceID
ON dbo.IRItemAnswer_Info (DGItemID, AnswerSourceID) 
INCLUDE([IncidentID], [AnswerBoolean])

但是,查詢的估計行數為 53,459,實際行數為 969,812。

我剛剛完成了強制新的統計數據UPDATE STATISTICS IRItemAnswer_Info IRItemAnswerInfo_DGItemID_AnswerSourceID WITH FULLSCAN,它沒有任何區別。

DBCC SHOW_STATISTICS ('IRItemAnswer_Info', 'DGItemID')因為DGItemID=1949EQ_ROWSas1,063,536

DBCC SHOW_STATISTICS ('IRItemAnswer_Info', 'AnswerSourceID')因為AnswerSourceID=1607EQ_ROWS_970,079

數據庫正在執行兼容級別 140 (SQL Server 2017)。我們將執行 2019 年,但在執行此操作之前,我們需要在儲存過程中糾正一些問題。

我接下來要看什麼?


我選擇了性能最差的輸出,這是最常見的值。 IRItemAnswer_Info是一個包含使用者定義的與事件相關聯的答案的表格,其中DGItemID=1949是最常見的問題之一(幾乎每個事件都有一個),而哪裡AnswerSourceID=1607是最常見的答案。鑑於它們之間存在很強的相關性,我應該如何重新排序查詢?

由於有點混亂,INNER JOIN同一張表有兩個 s,IRItemAnswer_Info。一個是我正在尋找的答案(由問題iria.DGItemID=1879及其輸出iria.AnswerSourceID連結確定irai.AltLabel),第二個是一個限制因素。我只想要問題iiai1.DGItemID=1949作為答案的記錄iiai1.AnswerSourceID=1607

我已經明確地從記憶體中刪除了計劃(使用DBCC FREEPROCCACHE)並重新執行它,結果沒有變化 - 雜湊匹配仍在溢出。

正如相關問答中所討論的,SQL Server 如何知道謂詞是相關的?SQL Server 預設假定謂詞是完全獨立的。

它僅在單個前導列上具有詳細的統計資訊(直方圖),即使在使用多列索引或統計資訊的情況下也是如此。那麼問題是如何組合來自兩個獨立謂詞的兩個統計直方圖。

例如,假設您有一個帶有 的查詢WHERE c1 = x AND c2 = y。根據直方圖資訊計算出的選擇性為c1 = x0.2。c2 = y從單獨的直方圖中計算出的選擇性為0.1。

兩個謂詞在一起的選擇性是什麼?0.2? 0.1? 0.2 x 0.1?中間某個地方?

如果沒有特定的附加資訊,SQL Server 必須做出有根據的猜測。最初的預設設置是假設完全獨立。較新的基數估計框架使用指數退避(“介於兩者之間”選項)。

您的情況略有不同,因為您對多列索引中的列進行了兩次相等測試,該索引帶有多列統計資訊。這些並不像聽起來那麼宏偉。我們仍然只得到前列的直方圖,但統計對象確實包含多列的平均密度資訊。

例如,(a,b,c) 上的索引將提供 (a)、(a,b) 和 (a,b,c) 的密度資訊。這個頻率資訊確實捕捉到了一些關於相關性的資訊,但它在每個級別都是一個數字。這意味著在給定相同數量的列的情況下,基於頻率的估計將始終產生相同的估計。

SQL Server 確實從多列頻率資訊中生成選擇性估計,但它也從各個列直方圖(如果可用)計算選擇性。直方圖估計假設獨立,並且不使用指數退避。

如果它比基於頻率的估計具有更高的選擇性,則伺服器選擇基於直方圖的估計。在您的範例中似乎就是這種情況。

Plan for computation:

 CSelCalcPointPredsFreqBased
     Distinct value calculation:
         CDVCPlanLeaf
             1 Multi-Column Stats, 0 Single-Column Stats, 0 Guesses

     Individual selectivity calculations:
         CSelCalcColumnInInterval
             Column: QCOL: [IIAI].DGItemID

         CSelCalcColumnInInterval
             Column: QCOL: [IIAI].AnswerSourceID

Loaded histogram for column QCOL: [IIAI].DGItemID from stats with id 2
Loaded histogram for column QCOL: [IIAI].AnswerSourceID from stats with id 3

Cardinality using multi-column statistics 5.45574e-07 and 
with independence assumption 0.00231336. 
Picking cardinality 0.00231336

Selectivity: 0.00231336

根據問題中的資訊,個人選擇性是:

  • DGItemID = 19299400 中的 1063536
  • AnswerSourceID = 19299400 中的 970079

假設獨立,因為AND我們將這些選擇性相乘,然後乘以全表基數以產生行估計:

19299400 * (1063536/19299400 * 970079/19299400) = 53458.3427124.

有許多內部模型變體以不同的方式處理任務。只有少數被公開記錄並通過提示或跟踪標誌公開。

通常,以下提示似乎會有所幫助:

SELECT
   COUNT_BIG(*)
FROM [VaxxTracker].[dbo].[IRItemAnswer_Info] AS iiai1
WHERE
   iiai1.DGItemID = 1949 
   AND iiai1.AnswerSourceID = 1607
OPTION (USE HINT('ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES'));

文件

  • ‘ASSUME_MIN_SELECTIVITY_FOR_FILTER_ESTIMATES’

使 SQL Server 在估計過濾器的 AND 謂詞以考慮完全相關時使用最小選擇性生成計劃。當與 SQL Server 2012 (11.x) 及更早版本的基數估計模型一起使用時,此提示名稱等效於跟踪標誌 4137,並且當跟踪標誌 9471 與 SQL Server 2014 (12.x) 的基數估計模型一起使用時,具有類似的效果) 或更高。

不幸的是,當基數估計從基於頻率的計算開始時,該提示不適用,使用多列統計。

使用原始 CE 模型,您可能會獲得更好的結果:

USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION')

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