實際行和估計行差異很大
在執行計劃之前(因為我正在調試一個執行不佳的計劃)我有這個變數分配塊:
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
索引,這是正確的索引,因為我在DGItemID
and上匹配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=1949
有EQ_ROWS
as1,063,536
和
DBCC SHOW_STATISTICS ('IRItemAnswer_Info', 'AnswerSourceID')
因為AnswerSourceID=1607
有EQ_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 = x
0.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')