Почему вставка и присоединение таблиц #temp происходит быстрее? - PullRequest
12 голосов
/ 28 мая 2009

У меня есть запрос, который выглядит как

SELECT
 P.Column1,
 P.Column2,
 P.Column3,
 ...
 (
   SELECT
       A.ColumnX,
       A.ColumnY,
       ...
   FROM
      dbo.TableReturningFunc1(@StaticParam1, @StaticParam2) AS A
   WHERE
      A.Key = P.Key
   FOR XML AUTO, TYPE  
 ),
 (
   SELECT
       B.ColumnX,
       B.ColumnY,
       ...
   FROM
      dbo.TableReturningFunc2(@StaticParam1, @StaticParam2) AS B
   WHERE
      B.Key = P.Key
   FOR XML AUTO, TYPE  
 )
FROM
(
   <joined tables here>
) AS P
FOR XML AUTO,ROOT('ROOT') 

P имеет ~ 5000 строк А и В ~ 4000 строк каждая

Этот запрос имеет производительность во время выполнения ~ 10 + минут.

Однако, изменив это на:

SELECT
 P.Column1,
 P.Column2,
 P.Column3,
 ...
INTO #P

SELECT
 A.ColumnX,
 A.ColumnY,
 ...
INTO #A     
FROM
 dbo.TableReturningFunc1(@StaticParam1, @StaticParam2) AS A

SELECT
 B.ColumnX,
 B.ColumnY,
 ...
INTO #B     
FROM
 dbo.TableReturningFunc2(@StaticParam1, @StaticParam2) AS B


SELECT
 P.Column1,
 P.Column2,
 P.Column3,
 ...
 (
   SELECT
       A.ColumnX,
       A.ColumnY,
       ...
   FROM
      #A AS A
   WHERE
      A.Key = P.Key
   FOR XML AUTO, TYPE  
 ),
 (
   SELECT
       B.ColumnX,
       B.ColumnY,
       ...
   FROM
      #B AS B
   WHERE
      B.Key = P.Key
   FOR XML AUTO, TYPE  
 )
FROM #P AS P
FOR XML AUTO,ROOT('ROOT')      

Производительность ~ 4 секунды.

Это не имеет большого смысла, так как может показаться, что стоимость вставки во временную таблицу и последующего объединения должна быть выше по умолчанию. Я склоняюсь к тому, что SQL выполняет неправильный тип «соединения» с подзапросом, но, возможно, я пропустил его, нет способа указать тип соединения для использования с коррелированными подзапросами.

Есть ли способ достичь этого без использования переменных таблицы #temp / @ table с помощью индексов и / или подсказок?

РЕДАКТИРОВАТЬ: обратите внимание, что dbo.TableReturningFunc1 и dbo.TableReturningFunc2 являются встроенными TVF, а не несколькими операторами, или они являются параметризованными операторами представления.

Ответы [ 8 ]

15 голосов
/ 28 мая 2009

Ваши процедуры пересматриваются для каждой строки в P.

То, что вы делаете с временными таблицами, фактически кэширует набор результатов, сгенерированный хранимыми процедурами, что устраняет необходимость повторной оценки.

Вставка в временную таблицу происходит быстро, потому что она не генерирует redo / rollback.

Соединения также бывают быстрыми, поскольку наличие стабильного набора результатов позволяет создать временный индекс с Eager Spool или Worktable

.

Вы можете повторно использовать процедуры без временных таблиц, используя CTE, но для того, чтобы это было эффективным, SQL Server необходимо материализовать результаты CTE.

Вы можете попробовать , чтобы заставить его сделать это, используя ORDER BY внутри подзапроса:

WITH    f1 AS
        (
        SELECT  TOP 1000000000
                A.ColumnX,
                A.ColumnY
        FROM    dbo.TableReturningFunc1(@StaticParam1, @StaticParam2) AS A
        ORDER BY
                A.key
        ),
        f2 AS
        (
        SELECT  TOP 1000000000
                B.ColumnX,
                B.ColumnY,
        FROM    dbo.TableReturningFunc2(@StaticParam1, @StaticParam2) AS B  
        ORDER BY
                B.Key
        )
SELECT  …

, что может привести к Eager Spool, сгенерированному оптимизатором.

Однако это далеко не гарантировано.

Гарантированный способ - добавить OPTION (USE PLAN) к вашему запросу и включить соответствующий ему CTE в предложение Spool.

Смотрите эту запись в моем блоге о том, как это сделать:

Это трудно поддерживать, поскольку вам придется переписывать свой план каждый раз, когда вы переписываете запрос, но это работает хорошо и довольно эффективно.

Однако использовать временные таблицы будет намного проще.

4 голосов
/ 21 февраля 2011

Этот ответ необходимо прочитать вместе со статьей Кассной
http://explainextended.com/2009/05/28/generating-xml-in-subqueries/

При разумном применении CROSS APPLY вы можете принудительно выполнить кэширование или ускоренную оценку встроенных TVF. Этот запрос возвращается мгновенно.

SELECT  *
FROM    (
        SELECT  (
                SELECT  f.num
                FOR XML PATH('fo'), ELEMENTS ABSENT
                ) AS x
        FROM    [20090528_tvf].t_integer i
        cross apply (
            select num
            from [20090528_tvf].fn_num(9990) f
            where f.num = i.num
            ) f
) q
--WHERE   x IS NOT NULL -- covered by using CROSS apply
FOR XML AUTO

Вы не предоставили реальных структур, поэтому сложно создать что-то осмысленное, но техника также должна применяться.

Если вы измените TVF с несколькими утверждениями в статье Quassnoi на встроенный TVF, план станет еще быстрее (по крайней мере, на один порядок), и план волшебным образом превратится в нечто, чего я не могу понять (это слишком просто!).

CREATE FUNCTION [20090528_tvf].fn_num(@maxval INT)  
RETURNS TABLE
AS RETURN 
        SELECT  num + @maxval   num
        FROM    t_integer  

Статистика

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(10 row(s) affected)
Table 't_integer'. Scan count 2, logical reads 22, 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 = 0 ms,  elapsed time = 2 ms.
2 голосов
/ 28 мая 2009

Это проблема с вашим подзапросом, ссылающимся на внешний запрос, то есть подзапрос должен быть скомпилирован и выполнен для каждой строки во внешнем запросе. Вместо использования явных временных таблиц вы можете использовать производную таблицу. Чтобы упростить ваш пример:

SELECT P.Column1,
       (SELECT [your XML transformation etc] FROM A where A.ID = P.ID) AS A

Если P содержит 10000 записей, тогда SELECT A.ColumnX FROM A, где A.ID = P.ID будет выполняться 10000 раз.
Вместо этого вы можете использовать производную таблицу следующим образом:

SELECT P.Column1, A2.Column FROM  
P LEFT JOIN 
 (SELECT A.ID, [your XML transformation etc] FROM A) AS A2 
 ON P.ID = A2.ID

Ладно, не этот иллюстративный псевдокод, но основная идея такая же, как у временной таблицы, за исключением того, что SQL Server все выполняет в памяти: сначала он выбирает все данные в «A2» и создает временную таблицу. в память, затем включается в нее. Это избавит вас от необходимости выбирать его для ТЕМПЕРАТУРЫ самостоятельно.

Просто чтобы дать вам пример принципа в другом контексте, где он может иметь более непосредственный смысл. Рассмотрите информацию о сотруднике и отсутствии, где вы хотите показать количество дней отсутствия, зарегистрированных для каждого сотрудника.

Плохо: (выполняется столько запросов, сколько сотрудников в БД)

SELECT EmpName, 
 (SELECT SUM(absdays) FROM Absence where Absence.PerID = Employee.PerID) AS Abstotal        
FROM Employee

Хорошо: (выполняется только два запроса)

SELECT EmpName, AbsSummary.Abstotal
FROM Employee LEFT JOIN
      (SELECT PerID, SUM(absdays) As Abstotal 
       FROM Absence GROUP BY PerID) AS AbsSummary
ON AbsSummary.PerID = Employee.PerID
1 голос
/ 29 мая 2009

Существует несколько возможных причин, по которым использование промежуточных временных таблиц может ускорить запрос, но наиболее вероятным в вашем случае является то, что функции, которые вызываются (но не перечислены), вероятно, являются TVF с несколькими утверждениями и не находятся в онлайн ТВФ. TVF с несколькими утверждениями непрозрачны для оптимизации их вызывающих запросов, и, таким образом, оптимизатор не может определить, есть ли какие-либо препятствия для повторного использования данных или других оптимизаций переупорядочения логических / физических операторов. Таким образом, все, что он может сделать, это повторно выполнять TVF каждый раз, когда предполагается, что содержащий запрос генерирует еще одну строку со столбцами XML.

Короче говоря, TVF с несколькими утверждениями расстраивает оптимизатор.

Обычные решения в порядке (типичного) предпочтения:

  1. Переписать TVF с многократными утверждениями, чтобы он был встроенным TVF
  2. Ввести код функции в вызывающий запрос или
  3. Сброс данных нарушающего TVF в временную таблицу. что ты и сделал ...
0 голосов
/ 27 июля 2016

Я согласился, временная таблица - хорошая идея. Когда в таблице увеличивается число строк, например, 40 миллионов строк, и я хочу обновить несколько столбцов в таблице, применяя в этом случае объединения с другой таблицей, я бы всегда предпочел использовать выражение Common table для обновления столбцов в операторе select с помощью оператор case, теперь мой результирующий набор оператора select содержит обновленные строки. Вставка 40 миллионов записей во временную таблицу с оператором select с использованием оператора case заняла у меня 21 минуту, а затем создание индекса заняло 10 минут, поэтому мое вставка и создание индекса заняли 30 минут. , Затем я собираюсь применить обновление, соединив обновленный результирующий набор временной таблицы с основной таблицей. Обновление 10 миллионов записей из 40 миллионов записей заняло 5 минут, поэтому мое общее время обновления 10 миллионов записей заняло почти 35 минут против 5 минут из общего выражения таблицы. В этом случае я выбираю общее табличное выражение.

0 голосов
/ 29 мая 2009

Если временные таблицы оказываются быстрее в вашем конкретном случае, вам следует вместо этого использовать табличную переменную.

Здесь есть хорошая статья о различиях и влиянии на производительность:

http://www.codeproject.com/KB/database/SQP_performance.aspx

0 голосов
/ 29 мая 2009

Это не имеет большого смысла, так как Казалось бы стоимость вставить в временная таблица, а затем сделать соединение должно быть выше по де> Это не имеет большого смысла, так как это Казалось бы стоимость вставить в временная таблица, а затем сделать соединение должно по умолчанию выше.

С временными таблицами вы явно указываете Sql Server, какое промежуточное хранилище использовать. Но если вы спрятали все в большой запрос, Sql Server решит сам. Разница не такая уж большая; в конце дня используется временное хранилище, независимо от того, указываете вы его как временную таблицу или нет.

В вашем случае временные таблицы работают быстрее, так почему бы не придерживаться их?

0 голосов
/ 28 мая 2009

Подумайте об использовании конструкции WITH common_table_expression для того, что у вас теперь есть в качестве вложенных выборок или временных таблиц, см. http://msdn.microsoft.com/en-us/library/ms175972(SQL.90).aspx.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...