Sql-Server
在這種情況下,表變數如何提高查詢的性能?
對於這種特定情況,我將在下面嘗試解釋,使用表變數比不使用表變數執行得更好。
我想知道為什麼,如果可能的話,擺脫 table 變數。
這是使用表變數的查詢:
USE [BISource_UAT] GO set statistics io on SET STATISTICS TIME ON SET NOCOUNT ON; DECLARE @OrderStartDate DATETIME = '15-feb-2015' DECLARE @OrderEndDate DATETIME = '28-feb-2016' DECLARE @tmp TABLE ( strBxOrderNo VARCHAR(20) ,sintReturnId INT ) INSERT INTO @tmp SELECT strBxOrderNo ,sintReturnId FROM TABLEBACKUPS.dbo.tblBReturnHistory rh WHERE rh.sintReturnStatusId in ( 3 ) AND rh.dtmAdded >= @OrderStartDate AND rh.dtmAdded < @OrderEndDate SELECT op.lngPaymentID ,op.strBxOrderNo ,op.sintPaymentTypeID ,op.strCurrencyCode ,op.strBCCurrencyCode ,op.decPaymentAmount ,op.decBCPaymentAmount ,ap.strAccountCode ,o.sintMarketID ,o.sintOrderChannelID ,o.sintOrderTypeID ,CASE WHEN opgv.lngpaymentID IS NULL THEN NULL -- Not a Voucher = Null WHEN gvp.strIssuedBxOrderNo IS NULL THEN 0 ELSE 1 END AS [IsPromoVoucher] -- Is a Voucher - check type ,o.sdtmOrdCreated FROM @tmp rh INNER JOIN TABLEBACKUPS.dbo.tblBReturn r ON r.sintReturnId = rh.sintReturnId AND r.strBxOrderNo = rh.strBxOrderNo INNER JOIN bocss2.dbo.tblBOrder o ON o.strBxOrderNo = r.strBxOrderNo INNER JOIN Bocss2.dbo.tblBOrderPayment op ON op.strBxOrderNo = o.strBxOrderNo INNER JOIN TABLEBACKUPS.dbo.tblBOrderItemReturn AS oir ON r.sintReturnId = oir.sintReturnID AND r.strBxOrderNo = oir.strBxOrderNo INNER JOIN Bocss2.dbo.tblBOrderItem AS i ON i.strBxOrderNo = oir.strBxOrderNo AND i.sintOrderSeqNo = oir.sintOrderSeqNo INNER JOIN TABLEBACKUPS.dbo.tblBAccountParticipant ap ON o.lngAccountParticipantID = ap.lngParticipantID LEFT OUTER JOIN TABLEBACKUPS.dbo.tblBOrderPaymentGiftVoucher opgv ON op.lngPaymentID = opgv.lngPaymentID LEFT OUTER JOIN TABLEBACKUPS.dbo.tblBGiftVoucher gv ON opgv.strVoucherNumber = gv.strVoucherNumber LEFT OUTER JOIN TABLEBACKUPS.dbo.tblBGiftVoucherPromotion gvp ON gvp.strIssuedBxOrderNo = gv.strIssuedBxOrderNo WHERE oir.decReturnFinalAmount > 0 AND o.sdtmOrdCreated >= @OrderStartDate
這會產生以下統計資訊:
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server parse and compile time: CPU time = 78 ms, elapsed time = 86 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. Table '#BF0B2154'. Scan count 0, logical reads 1957, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBReturnHistory'. Scan count 1, logical reads 13, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 16 ms, elapsed time = 9 ms. Table 'tblBGiftVoucherPromotion'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBGiftVoucher'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderPaymentGiftVoucher'. Scan count 0, logical reads 452, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderItem'. Scan count 0, logical reads 904, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderPayment'. Scan count 186, logical reads 1649, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBAccountParticipant'. Scan count 0, logical reads 7112, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrder'. Scan count 3557, logical reads 14267, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderItemReturn'. Scan count 1951, logical reads 5865, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBReturn'. Scan count 0, logical reads 3902, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table '#BF0B2154'. Scan count 1, logical reads 7, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 125 ms, elapsed time = 138 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms.
使用showplan_text 我想顯示查詢計劃:
現在這是不使用表變數的相同查詢:
USE [BISource_UAT] GO set statistics io on SET STATISTICS TIME ON SET NOCOUNT ON; DECLARE @OrderStartDate DATETIME = '15-feb-2015' DECLARE @OrderEndDate DATETIME = '28-feb-2016' SELECT op.lngPaymentID ,op.strBxOrderNo ,op.sintPaymentTypeID ,op.strCurrencyCode ,op.strBCCurrencyCode ,op.decPaymentAmount ,op.decBCPaymentAmount ,ap.strAccountCode ,o.sintMarketID ,o.sintOrderChannelID ,o.sintOrderTypeID ,CASE WHEN opgv.lngpaymentID IS NULL THEN NULL -- Not a Voucher = Null WHEN gvp.strIssuedBxOrderNo IS NULL THEN 0 ELSE 1 END AS [IsPromoVoucher] -- Is a Voucher - check type ,o.sdtmOrdCreated FROM TABLEBACKUPS.dbo.tblBReturnHistory rh INNER JOIN TABLEBACKUPS.dbo.tblBReturn r ON r.sintReturnId = rh.sintReturnId AND r.strBxOrderNo = rh.strBxOrderNo INNER JOIN bocss2.dbo.tblBOrder o ON o.strBxOrderNo = r.strBxOrderNo AND o.sdtmOrdCreated >= @OrderStartDate INNER JOIN Bocss2.dbo.tblBOrderPayment op ON op.strBxOrderNo = o.strBxOrderNo INNER JOIN TABLEBACKUPS.dbo.tblBOrderItemReturn AS oir ON r.sintReturnId = oir.sintReturnID AND r.strBxOrderNo = oir.strBxOrderNo AND oir.decReturnFinalAmount > 0 INNER JOIN Bocss2.dbo.tblBOrderItem AS i ON i.strBxOrderNo = oir.strBxOrderNo AND i.sintOrderSeqNo = oir.sintOrderSeqNo INNER JOIN TABLEBACKUPS.dbo.tblBAccountParticipant ap ON o.lngAccountParticipantID = ap.lngParticipantID LEFT OUTER JOIN TABLEBACKUPS.dbo.tblBOrderPaymentGiftVoucher opgv ON op.lngPaymentID = opgv.lngPaymentID LEFT OUTER JOIN TABLEBACKUPS.dbo.tblBGiftVoucher gv ON opgv.strVoucherNumber = gv.strVoucherNumber LEFT OUTER JOIN TABLEBACKUPS.dbo.tblBGiftVoucherPromotion gvp ON gvp.strIssuedBxOrderNo = gv.strIssuedBxOrderNo WHERE rh.sintReturnStatusId in ( 3 ) AND rh.dtmAdded >= @OrderStartDate AND rh.dtmAdded < @OrderEndDate
查看統計數據時,我們得到了以下資訊:
SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms. Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBGiftVoucher'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBAccountParticipant'. Scan count 1, logical reads 32, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBReturn'. Scan count 1, logical reads 170, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderItemReturn'. Scan count 0, logical reads 35849, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderPayment'. Scan count 9408, logical reads 87643, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderItem'. Scan count 1950, logical reads 8336, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrder'. Scan count 1951, logical reads 7835, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBReturnHistory'. Scan count 1, logical reads 13, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBOrderPaymentGiftVoucher'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. Table 'tblBGiftVoucherPromotion'. Scan count 1, logical reads 27, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 625 ms, elapsed time = 612 ms. SQL Server parse and compile time: CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times: CPU time = 0 ms, elapsed time = 0 ms.
現在,關於文本格式的執行計劃:
設置參數
但是為什麼使用表變數我得到更少的讀取,更少的 I/O,並且執行(不清除記憶體)總是更快?
我可以提供任何創建表腳本,或任何其他必要的東西來更好地理解這種情況。
在這裡發表任何評論,我會回复。
這是一個類似的問題:
為什麼在這種特定情況下使用表變數的速度是 #temp 表的兩倍多?
在CHECKPOINT之後執行查詢時;DBCC 刪除緩衝區;結果是:
使用表變數查詢
沒有表變數的查詢
這裡的主要因素是:
- 優化器不會試圖找到最佳計劃;它的目標是快速找到一個合理的計劃
- 它假定查詢將使用冷記憶體執行
- 使用的成本模型有利於順序 I/O 而不是隨機 I/O
- 假設重複搜尋索引是隨機分佈的
表變數的基數估計為 1 行(除非發生語句級重新編譯,或者跟踪標誌 2453處於活動狀態)。這種低估計導致成本非常低的計劃,具有基於嵌套循環的導航策略。該計劃對於相對較低的行數可能會繼續有效,尤其是在不需要從持久儲存中讀取所需數據的情況下。
通過更準確的基數估計,優化器傾向於使用散列連接和一些掃描的計劃。考慮到上面列出的假設,這似乎比導航策略便宜;特別是關於冷記憶體,以及與許多查找相比順序掃描的成本相對較低(假設大部分是隨機 I/O 模式)。
如果需要的數據不在記憶體中,則表變數計劃可能比替代計劃慢 -或者它可能不在。成本模型就是這樣——一個模型——使用的確切數字可能不代表您的硬體和配置,並且所做的假設在特定情況下可能無效。
所有這些警告尤其適用於低成本查詢(兩者都是),因為小的成本變化會產生非常不同的計劃形狀。事實上,這兩個計劃都是成功的,因為它們能夠快速有效地產生結果。