2015-11-17 2 views
1

Недавно я рассмотрел функцию SQL-сервера коллеги (в SQL Server 2008), где он поставил запрос T-SQL в скалярную пользовательскую функцию. Затем эта функция использовалась в предложении Select запроса.Выполнение запроса T-Sql внутри скалярной пользовательской функции

Я думал, что наличие запроса внутри функции будет иметь ужасную производительность, потому что я предположил, что каждая возвращаемая строка должна будет запускать запрос, даже если этот запрос оптимизирован для запроса по индексу.

Вот пример того, что я говорю о:

create table [PERSON] (
ID int primary key, 
FIRSTNAME NVARCHAR(100), 
MIDDLENAME NVARCHAR(100) null, 
LASTNAME NVARCHAR(100)) 
GO 

INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME) 
VALUES (1, 'BOB', 'M', 'BLUE') 

INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME) 
VALUES (2, 'VALERIE', 'J', 'GREEN') 

INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME) 
VALUES (3, 'SIMON', 'D', 'RED') 

INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME) 
VALUES (4, 'LIONEL', 'W', 'BROWN') 
GO 

-- Scalar Function with T-SQL 
CREATE FUNCTION dbo.fn_FormatNameFromId(
      @pnPersonId int 
      ) 
Returns nvarchar(300) 
AS 
Begin 
    return (Select FIRSTNAME + ' ' + MIDDLENAME + ' ' + LASTNAME 
     From PERSON 
     Where ID = @pnPersonId) 
End 
go 

-- Scalar Function without T-SQL 
CREATE FUNCTION dbo.fn_FormatNameFromValues(
      @psFirstName nvarchar(100), 
      @psMiddleName nvarchar(100), 
      @psLastName nvarchar(100) 
      ) 
Returns nvarchar(300) 
AS 
Begin 
    return (@psFirstName + ' ' + @psMiddleName + ' ' + @psLastName) 
End 
go 

-- T-SQL within function 
select dbo.fn_FormatNameFromId(ID) 
from PERSON 

-- Pass values directly 
select dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME) 
FROM PERSON 

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

Может кто-нибудь объяснить, как и почему fn_FormatNameFromId не приводит к потере производительности?

+0

Возможно, это из-за кеширования. Вы загружаете все данные из таблицы, а Sql Server загружает все страницы в память. –

+1

«Я думал ... будет иметь ужасное исполнение» - он может иметь худшую производительность, чем эквивалент, но вопрос * first *, о котором вы должны спрашивать, - это текущая эффективность? То есть у вас есть показатели производительности * *, и вы * измерили * текущую производительность? Если это * * работает приемлемо, почему вы тратите время на это? Если это не выполняется приемлемо, у вас уже есть свой ответ. И если вы не ставили цели и не измеряли, как вы знаете, что вы тратите время на то, где должна быть производительность, и ее можно улучшить? –

+0

@Damien_The_Unbeliever, да, спектакль оказался в порядке. Я просто хотел понять, как это работает, чтобы я мог волноваться/не беспокоиться об этом в будущем. Что касается лучшей практики, хотя мы не должны полагаться на оптимизатора. – antman

ответ

1

Вы можете увидеть разницу между Estimated Execution Plan и Actual Execution Plan

Вы лично ожидали select dbo.fn_FormatNameFromId(ID) from PERSON работать, как показано на скриншоте: для каждой строки из запроса 1 (кластерного индекса сканирования) сделать запрос 2 (индекс кластерного Seek - или подзапрос поиск по ID) Estimated Execution Plan

Но sql-server видит общий шаблон этих запросов и оптимизирует фактический план запроса только одним простым Clustered Index Scan, как это делает sql-сервер во втором запросе select dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME) FROM PERSON.

1

Лично я не являюсь Geat поклонником T-SQL функций, насколько ИСПОЛНЕНИЕ беспокойства поэтому я Usally избегать их как можно больше в SQL SERVER,

ДА я знаю, что есть много много преимуществ пользовательских функций в программировании.

отпускает на ваши вопросы непосредственно

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

я обычно не сравнивают план выполнения, чтобы проверить, какой план лучше, а что хуже на основе затрат поддерева/процессора, поскольку эти затраты основаны только на оценочных значениях.

Может кто-нибудь объяснить, как и почему fn_FormatNameFromId не вызывает потеря производительности?

, потому что вы все еще имеет дело с небольшим количеством данных я тестировал ваш обе функции, как показано ниже:

declare @a int=1 
Declare @name varchar(50) 
while (@a<99999) 
Begin 
-- T-SQL within function 
select @name = dbo.fn_FormatNameFromId(ID) 
from PERSON 
SET @a [email protected]+1 
END 

она работала на моей локальной системе в 13 второй

declare @a int=1 
Declare @name varchar(50) 
while (@a<99999) 
Begin 

-- Pass values directly 
select @name =dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME) 
FROM PERSON 
SET @a [email protected]+1 
END 

это бежало по моей локальной системе в 9 сек.

Ааааа я сказал, что я не являюсь большим поклонником функций так же работать без функций

declare @a int=1 
Declare @name varchar(50) 
while (@a<99999) 
Begin 

-- Pass values directly 


Select @name= FIRSTNAME + ' ' + MIDDLENAME + ' ' + LASTNAME 
     From PERSON 


SET @a [email protected]+1 
END 

и побежал через 2 секунды в моей системе

пожалуйста, прочитайте статью ниже

T-SQL User-Defined Functions: the good, the bad, and the ugly

+0

Хотя я в принципе согласен с тем, что функции могут вызывать проблемы, когда они используются при правильных обстоятельствах, они очень полезны. Я не уверен, что ваш пример действительно проверяет производительность T-SQL, а не накладные расходы, необходимые для составления плана выполнения и выполнения запроса. Если вы создали 99,999 строк в таблице PERSON и выполнили запрос, я думаю, вы не найдете разницы в производительности (при использовании функций). Может быть небольшое увеличение производительности в НЕ использовании функции, но в моем сценарии функция лучше, чем повторение кода в сотнях хранимых процедур. – antman

+0

@antman Я связал один атрикл выше, пожалуйста, прочтите это. –

+0

Отчасти это связано с тем, что стоимость скалярных функций (использование ЦП и т. Д.) Не отображается в планах исполнения. Это еще один из их недостатков, это черный ящик. –

Смежные вопросы