для этого конкретного случая, который я попытаюсь объяснить ниже, использование табличной переменной работает лучше, чем не использование табличной переменной.
Я хотел бы знать, почему и, если возможно, избавиться от табличной переменной.
это запрос с использованием табличной переменной:
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, я хотел бы показать план запроса:
первая часть запроса - заполнение табличной переменной
вторая часть запроса: используя таблицу переменных и объединяя другие таблицы:
Это план XML запроса с использованием табличной переменной.
теперь это тот же запрос, НЕ использующий переменную таблицы:
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.
Теперь по поводу плана выполнения в текстовом формате:
настройка параметров
Теперь важная часть, запускающая шоу:
Это план XML запроса, НЕ использующий переменную таблицы.
Но почему при использовании табличной переменной у меня было меньше операций чтения, меньше операций ввода-вывода, а выполнение (без очистки кэша) всегда было быстрее?
Я могу предоставить любой сценарий создания таблицы или что-либо еще необходимое для лучшего понимания этой ситуации.
просто оставьте любой комментарий здесь, и я отвечу.
это похожий вопрос:
при выполнении запросов после CHECKPOINT ; DBCC DROPCLEANBUFFERS ; результаты были:
запрос с табличной переменной
запрос без табличной переменной
источник
Ответы:
Основные факторы здесь:
Оценка количества элементов для табличной переменной составляет 1 строку (если только не происходит перекомпиляция уровня оператора или флаг трассировки 2453 активен). Эта низкая оценка приводит к очень дешевому плану с навигационной стратегией, основанной на вложенных циклах. Этот план может продолжать действовать при относительно небольшом количестве строк, особенно если необходимые данные не нужно считывать из постоянного хранилища.
Благодаря более точным оценкам количества элементов, оптимизатор предпочитает план с использованием хеш-соединений и нескольких сканирований. Это кажется дешевле, чем навигационная стратегия, учитывая предположения, перечисленные выше; особенно в отношении холодного кэша и относительно низкой стоимости последовательного сканирования по сравнению со многими поисками (при предположении в основном случайной схемы ввода / вывода).
План табличных переменных может быть медленнее, чем альтернатива, если необходимые данные не находятся в памяти - или нет . Модель стоимости в точности соответствует модели - используемые точные цифры могут не соответствовать вашему оборудованию и конфигурации, а сделанные предположения могут быть недопустимыми в определенных обстоятельствах.
Все эти предостережения особенно применимы к недорогим запросам (которые оба являются), так как небольшие изменения стоимости могут привести к очень разным формам плана. На самом деле оба плана успешны в том, что они дают результаты быстро и достаточно эффективно .
источник