У меня есть таблица с несколькими десятками строк. Упрощенная настройка следующая
CREATE TABLE #data ([Id] int, [Status] int);
INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);
И у меня есть запрос, который соединяет эту таблицу с набором строк, построенных из значений таблицы (из переменных и констант), как
DECLARE @id1 int = 101, @id2 int = 105;
SELECT
COALESCE(p.[Code], 'X') AS [Code],
COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
(@id1, 'A'),
(@id2, 'B')
) p([Id], [Code])
FULL JOIN #data d ON d.[Id] = p.[Id];
План выполнения запроса показывает, что решение оптимизатора заключается в использовании FULL LOOP JOIN
стратегии, которая представляется целесообразной, поскольку оба входа имеют очень мало строк. Одна вещь, которую я заметил (и не могу согласиться), тем не менее, состоит в том, что строки TVC находятся в очереди (см. Область плана выполнения в красном поле).
Почему оптимизатор вводит здесь спул, для чего это нужно? Нет ничего сложного за катушкой. Похоже, это не обязательно. Как избавиться от этого в этом случае, каковы возможные пути?
Вышеуказанный план был получен на
Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)
источник
Ответы:
За спулом не стоит ссылаться на простую таблицу, которая может быть просто дублирована, когда используется альтернатива левого соединения / антиполусоединения. генерируется полусоединение.
Может выглядеть немного как таблица (постоянное сканирование), но для оптимизатора * это одна
UNION ALL
из отдельных строк вVALUES
предложении.Дополнительной сложности достаточно, чтобы оптимизатор выбрал буферизацию и воспроизведение исходных строк, а не заменял буферизацию простым «получением таблицы» в дальнейшем. Например, начальное преобразование из полного объединения выглядит так:
Обратите внимание на дополнительные катушки, введенные общим преобразованием. Катушки над простой таблицей get вычищаются позже по правилу
SpoolGetToGet
.Если бы у оптимизатора было соответствующее
SpoolConstGetToConstGet
правило, оно могло бы работать в принципе так, как вы хотите.Используйте реальную таблицу (временную или переменную) или запишите преобразование из полного объединения вручную, например:
План ручного переписывания:
Это оценивается в 0,0067201 единиц по сравнению с 0,0203412 единиц для оригинала.
* Это можно наблюдать как
LogOp_UnionAll
в Преобразованном Дереве (TF 8605). В дереве ввода (TF 8606) этоLogOp_ConstTableGet
. В преобразованном Дереве показывает дерево экспрессии Оптимизатора элементов после синтаксического анализа, нормализации, algebrization, связывания и некоторых других подготовительных работ. Input Дерево показывает элементы после преобразования к отрицанию нормальной форме (NNF новообращенного), постоянная времени выполнения разрушающейся, и несколько других битов и качается. NNF-конвертирование включает в себя логику для свертывания логических объединений и общих таблиц, среди прочего.источник
Табличная таблица - это просто создание таблицы из двух наборов кортежей, представленных в
VALUES
предложении.Вы можете удалить катушку, сначала вставив эти значения во временную таблицу, например так:
Рассматривая план выполнения вашего запроса, мы видим, что список вывода содержит два столбца, которые используют
Union
префикс; это подсказка, что спул создает таблицу из источника union'd:FULL OUTER JOIN
Требует SQL Server для доступа к значениям вp
два раза, один раз для каждой «стороны» соединения. Создание буфера позволяет объединяться полученным внутренним циклам для доступа к буферным данным.Интересно, что если вы заменяете на
FULL OUTER JOIN
aLEFT JOIN
и aRIGHT JOIN
иUNION
результаты вместе, SQL Server не использует спул.Обратите внимание, я не предлагаю использовать
UNION
запрос выше; для больших наборов входных данных он может быть не более эффективным, чем тот, который уFULL OUTER JOIN
вас уже есть.источник