Производительность пользовательских функций в SQL Server 2000 и 2005 - PullRequest
2 голосов
/ 19 марта 2012

У нас есть две таблицы, Customer и CustomerEvent, каждая из которых содержит несколько миллионов строк. В SQL Server 2000 мы развернули пользовательскую функцию с именем fn_CustomerEvent, которая возвращает TRUE или FALSE на основе двух параметров CustomerID и EventCode, например.

SELECT dbo.fn_CustomerEvent(1345678, 'Music')

Код UDF:

CREATE FUNCTION [dbo].[fn_CustomerEvent](@CustomerID INT, @EviCode NVARCHAR(10))
RETURNS NVARCHAR(10)
AS
BEGIN
    DECLARE @List NVARCHAR(10)

    SELECT @List = CASE 
                     WHEN COUNT(*) > 0 THEN 'TRUE'
                     ELSE 'FALSE'
                   END 
    FROM CustomerEvent
    WHERE   
         CustomerID = @CustomerID 
         AND EviCode = @EviCode

    RETURN @List
END

Производительность на SQL Server 2000 была отличной. Вернуть ТОП 5000 строк в течение 3 секунд. Например,

SELECT TOP 5000 
     CustomerID, dbo.fn_CustomerEvent(1345678, 'Music')
FROM [Table1] 

Но теперь мы переходим к SQL Server 2005. Тот же код, тот же UDF, но производительность резко падает с 3 секунд до 1 минуты 20 секунд.

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

Ответы [ 3 ]

3 голосов
/ 19 марта 2012

Скалярный UDF оценивается для каждой строки (то есть 5000 раз). Вы можете вызвать его один раз и сохранить результат в переменной

DECLARE @Result nvarchar(10)
SELECT @Result = dbo.fn_CustomerEvent(1345678, 'Music') 

SELECT TOP 5000 
     CustomerID, @Result
FROM [Table1] 

или вы можете использовать встроенный TVF (и я бы также использовал EXISTS вместо COUNT)

CREATE FUNCTION CustomerEvent (@CustomerID INT, 
                               @EviCode    NVARCHAR(10)) 
RETURNS TABLE 
AS 
  RETURN 
    (SELECT CASE 
              WHEN EXISTS(SELECT * 
                          FROM   CustomerEvent 
                          WHERE  CustomerID = @CustomerID 
                                 AND EviCode = @EviCode) THEN 'TRUE' 
              ELSE 'FALSE' 
            END) 

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

1 голос
/ 19 марта 2012

Есть одна большая проблема с UDF: они не работают с индексами. Если вы хотите повторно использовать код и поддерживать производительность, я обычно создаю вычисляемый столбец (который можно проиндексировать) или представление.

0 голосов
/ 19 марта 2012
CREATE FUNCTION CustomerEvent (@CustomerID INT, 
                               @EviCode    NVARCHAR(10)) 
RETURNS TABLE 
AS 
  RETURN 
    (SELECT COALESCE((SELECT 'TRUE' FROM CustomerEvent
                    WHERE   
                         CustomerID = @CustomerID 
                         AND EviCode = @EviCode)
                , 'FALSE'))

Проверьте индексы, перестройте их и обновите статистику.

...