UDF против прямой производительности SQL - PullRequest
5 голосов
/ 02 декабря 2011

Использование MSSQL 2005

Сегодня я играл с Scalar UDF в операторе where, чтобы увидеть некоторые затраты, связанные с выполнением вызовов и разницей в Io и т. Д.

I'mначиная с 2 основных таблиц.Клиент, который имеет 1 миллион строк.и покупки, которые имеют 100 000.Оба имеют столбец автоматической идентификации в качестве первичного ключа.Другие индексы не определены.

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

SET STATISTICS IO ON
    SELECT * FROM Customer C 
    INNER JOIN Purchases P on C.[IDENTITY] = P.CustomerID
    WHERE P.Amount > 1000
SET STATISTICS IO OFF

Возвращает статистику ввода-вывода

Table 'Customer'. Scan count 0, logical reads 3295, physical reads 1, read-ahead reads 32, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Purchases'. Scan count 1, logical reads 373, physical reads 1, read-ahead reads 370, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Так что просто чтобы увидеть влияние скалярного UDF, я просто переместил P.Amount> 1000 вUDF.Функция выглядит следующим образом:

CREATE FUNCTION [dbo].[HighValuePurchase]
(
    @value int
)
RETURNS bit
AS
BEGIN
    DECLARE @highValue bit
    SET @highValue = '0'

    IF @value > 1000
    BEGIN
        SET @highValue = '1'
    END
    RETURN @highValue
END

Итак, я запустил следующий запрос:

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

SET STATISTICS IO ON      
    SELECT * FROM Customer C 
    INNER JOIN Purchases P on C.[IDENTITY] = P.CustomerID
    WHERE dbo.HighValuePurchase(P.Amount) = '1'
SET STATISTICS IO OFF

Я ожидал, что это будет работать хуже.Этот запрос вернул следующую статистику ввода-вывода:

Table 'Purchases'. Scan count 1, logical reads 373, physical reads 1, read-ahead reads 370, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Customer'. Scan count 1, logical reads 35, physical reads 3, read-ahead reads 472, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Это также вернулось быстрее, чем запрос> 1000.В то время как были возвращены те же строки, порядок сортировки вызывающей UDF строки автоматически сортировался по C. [IDENTITY], где другой запрос оказался несортированным.Вероятно, это связано с тем, как комбинаты были выполнены в планах выполнения.Схема планов приведена ниже.

План выполнения для не-UDF показывает сканирование кластерного индекса для покупок и поиск кластерного индекса для клиентов, объединенных во вложенном соединении.

План выполнения для UDFВерсия показывает сканирование кластерного индекса для покупок, затем фильтр, затем сортировка.На клиенте есть кластерное сканирование индекса.Затем результаты объединяются в объединение слиянием.

Я уверен, что это связано с отсутствием индексов и т. Д., Но я не уверен, почему эти результаты такие, какие есть.Я чувствовал, что UDF работает мучительно медленно, и все говорят, что их использование - это, как правило, плохая идея, поэтому я провел этот тест вместе.Я не могу сейчас объяснить, почему версия UDF кажется намного лучше.

1 Ответ

1 голос
/ 02 февраля 2012
  • Если вы хотите присоединиться к Purchases.CustomerID, вы должны поместить в него индекс.
  • Если вы часто запрашиваете диапазоны значений, вам также следует включить индекс для этого.

Поскольку вы запрашиваете у SQL-сервера выбор между двумя плохими планами.

SQL Server может приблизительно угадать, сколько покупок будет покрыто запросом > 1000, и на основе этого выберет план.

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

Вы можете увидеть сгенерированные планы, и он подскажет вам приблизительное количество строк в каждом планеа также фактическое количество.Эти предполагаемые цифры учитывают выбор плана в каждом конкретном случае.

...