Почему производительность функции табличного значения лучше, чем выбор прямого оператора? - PullRequest
0 голосов
/ 08 января 2019

a busy cat

Я использую AdventureWorks2012 и провожу тестирование. и мой вопрос: почему непосредственно оператор SELECT производительность ниже, чем таблица значений. Я только помещаю SELECT statemnt в функцию табличных значений и полностью противоположную производительность.

CREATE FUNCTION [dbo].[atest1]
(
    @iBusinessEntityID  INT
)
RETURNS @t TABLE
(
    [BusinessEntityID]  INT
  , [NationalIDNumber]  NVARCHAR(15)
  , [JobTitle]          NVARCHAR(50)
)
AS
    BEGIN
        INSERT INTO @t
               SELECT 
                   [e].[BusinessEntityID]
                 , [e].[NationalIDNumber]
                 , [e].[JobTitle]
               FROM [HumanResources].[Employee] [e]
               INNER JOIN [Person].[Person] [p]
                    ON [p].[BusinessEntityID] = [e].[BusinessEntityID]
               WHERE [e].[BusinessEntityID] = @iBusinessEntityID;
        RETURN;
    END;

--TEST PERFORMANCE
SELECT 
    *
FROM [dbo].[atest1](5);
GO
SELECT 
    [e].[BusinessEntityID]
  , [e].[NationalIDNumber]
  , [e].[JobTitle]
FROM [HumanResources].[Employee] [e]
INNER JOIN [Person].[Person] [p]
     ON [p].[BusinessEntityID] = [e].[BusinessEntityID]
WHERE [e].[BusinessEntityID] = 5;

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Проблема здесь в том, что расчетный план в SSMS часто показывает неправильный процент , в случае UDF s он почти всегда делает это неправильно.

cost percentage - это расчетная стоимость операции по сравнению с другими операциями, но в случае UDF SSMS не проверяются внутренние компоненты UDF.

Я создал ваш UDF на своем сервере и добавил к нему текст GUID, чтобы я мог легко вернуть план для этого UDF:

CREATE FUNCTION [dbo].[atest1] (@iBusinessEntityID int)
RETURNS @t TABLE(BusinessEntityID int,NationalIDNumber nvarchar(15),JobTitle nvarchar(50)) AS
BEGIN
INSERT INTO @t /*3C6A985B-748B-44D4-9F76-1A0866342728*/ -- HERE IS MY GUID
SELECT e.BusinessEntityID, e.NationalIDNumber, e.JobTitle
FROM HumanResources.Employee e INNER JOIN Person.Person p ON p.BusinessEntityID = e.BusinessEntityID
WHERE e.BusinessEntityID = @iBusinessEntityID
RETURN
END

И теперь я выполняю эту функцию и извлекаю ее план следующим образом:

select p.query_plan
from sys.dm_exec_cached_plans cp
     cross apply sys.dm_exec_sql_text(cp.plan_handle) t
     cross apply sys.dm_exec_query_plan(cp.plan_handle) p
where cp.objtype = 'Proc'
      and t.text like '%3C6A985B-748B-44D4-9F76-1A0866342728%'

Я проверил этот план, и он точно такой же, как план вашего "прямого заявления". То же самое в части SELECT, но в табличной переменной также есть INSERT, а в основном плане - scan. Таким образом, вы можете ясно видеть, что стоимость вашей UDF не может быть ниже, она равна стоимости «прямого заявления» плюс INSERT стоимость плюс табличная переменная scan стоимость.

enter image description here

В этом случае таблицы малы, и существует только один вызов UDF, поэтому вы не можете заметить разницу во времени выполнения, но если вы сделаете цикл, в котором выполняете «прямое утверждение» больше раз и вызываете UDF больше раз » Возможно, вы увидите разницу во времени выполнения, и «прямой оператор» будет быстрее. Но SSMS будет настаивать на более низкой стоимости UDF.

0 голосов
/ 08 января 2019

Обычно функции ведут себя хуже, чем прямые запросы, но возможно, что в этом случае, как и для чего-то, предопределенного как функция, система сохраняет лучший план. В этом случае для функции, которую система выполняет сканирование таблиц, похоже, что иногда для небольших таблиц, как в случае с AdventureWorks DB, это может работать лучше, чем поиск по индексу.

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

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