Что я могу сделать, чтобы улучшить производительность моей чистой пользовательской функции в SQL Server? - PullRequest
3 голосов
/ 23 февраля 2012

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

Что я могу сделать, чтобы ускорить использование UDF? Я думаю, что должен быть какой-то способ сказать SQL Server, что моя функция возвращает тот же результат с теми же параметрами итаким образом, следует запомнить.Кажется, нет способа сделать это в UDF, потому что они должны быть чистыми и поэтому не могут записывать во временную таблицу.

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

CREATE function [dbo].[WorkDay] (
    @inputDate datetime, 
    @offset int) 
returns datetime as begin

declare 
     @result datetime 

set @result = @inputDate

while @offset != 0
begin
    set @result = dateadd( day, sign(@offset), @result )

    while ( DATEPART(weekday, @result ) not between 2 and 6 )
      or @result in (select date from myDB.dbo.holidays
      where calendar = 'US' and date = @result)
    begin
        set @result = dateadd( day, sign(@offset), @result )
    end
    set @offset = @offset - sign(@offset)
end
return @result

END

Ответы [ 2 ]

2 голосов
/ 23 февраля 2012

Моя первая мысль здесь - в чем проблема производительности? Конечно, у вас есть цикл (один раз для каждой строки, где он применяется) внутри цикла, в котором он выполняет запрос. Но вы получаете плохие планы выполнения? Ваш набор результатов огромен? Но давайте обратимся к общему. Как однажды решить эту проблему? SQL на самом деле не делает запоминания (как указывает выдающийся @Martin_Smith). Так что же делать мальчику?

Вариант 1 - новый дизайн

Создайте совершенно новый дизайн. В этом конкретном случае @Aaron_Bertrand указывает, что таблица календаря может удовлетворить ваши потребности. Совершенно верно. Это не очень помогает в ситуациях, не связанных с календарем, но, как это часто бывает в SQL, нужно думать немного иначе.

Вариант 2 - Вызов UDF Меньше

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

Вариант 3 - Динамический UDF

Я, вероятно, выскользну из комнаты за это предложение, но здесь идет. Что делает этот UDF медленным, так это оператор выбора внутри цикла. Если ваш праздничный стол действительно редко меняется, вы можете поставить на него триггер. Триггер выписал бы и обновил UDF. Новый UDF мог грубой силой принять все праздничные решения. Будет ли это немного похоже на каннибализм с написанием SQL SQL? Конечно. Но это избавит от подзапроса и ускорит UDF. Пусть начинается хеклинг.

Вариант 4 - Memoize It!

Хотя SQL не может напрямую запоминать, у нас есть SQL CLR. Преобразуйте UDF в SQL CLR udf. В CLR вы можете использовать статические переменные. Вы можете легко взять таблицу Holidays через некоторый регулярный интервал и сохранить их в хеш-таблице. Затем просто перепишите ваш цикл в CLR. Вы могли бы даже пойти дальше и запомнить весь ответ, если это соответствует логике.


Обновление:

Вариант 1 - я действительно пытался сосредоточиться на общем, а не на примере функции, которую вы использовали выше. Тем не менее, текущий дизайн вашей UDF допускает несколько вызовов таблицы Holiday, если вам выпало несколько раз подряд. Использование какой-либо таблицы в стиле календаря, содержащей список «плохих дней» и соответствующий «следующий рабочий день», позволит вам исключить возможность множественных обращений и запросов.

Вариант 3 - Хотя домен заранее неизвестен, вы вполне можете изменить свой праздничный стол. Для данного выходного дня он будет содержать следующий соответствующий рабочий день. Из этих данных вы могли бы выложить UDF с длинным регистром (когда «5/5/2012», затем «5/14/2012» или что-то подобное) внизу. Эта стратегия может работать не для всех типов проблем, но может хорошо работать для некоторых типов проблем.

Вариант 4. У каждой технологии есть последствия. Необходимо развернуть CLR, изменить конфигурацию SQL Server, а SQL CLR ограничен платформой 3.5. Лично я нашел эти корректировки достаточно простыми, но ваша ситуация может быть другой (скажем, непокорный администратор базы данных или ограничения на модификации производственных серверов).

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

Есть некоторые доказательства , что при очень высоких уровнях транзакций CLR не работает так же хорошо, как прямой SQL. Однако в вашем сценарии это наблюдение может быть неприменимо, поскольку нет прямого SQL-корреляции для того, что вы пытаетесь сделать (запомните).

1 голос
/ 23 февраля 2012

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

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

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