SQLServer: зачем избегать пользовательских функций с табличным значением?

У меня есть довольно большой запрос, который необходим в нескольких хранимых процедурах, и я хотел бы перенести его в UDF, чтобы упростить его обслуживание (представление не будет работать, оно требует кучу параметров), однако все Я когда-либо разговаривал, сказал мне, что UDF невероятно медленны.

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

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


person FlySwat    schedule 03.07.2009    source источник
comment
Можете ли вы опубликовать некоторые или все UDF, о которых вы спрашиваете? Это встроенная таблица UDF или многострочная?   -  person John Saunders    schedule 04.07.2009
comment
многострочный UDF, однако не может опубликовать код.   -  person FlySwat    schedule 04.07.2009
comment
Похоже, что UDF пользуются плохой репутацией. Мы широко используем пользовательские функции с табличным значением для некоторых сложных вычислений с большими наборами данных. Производительность была превосходной. Многие из наших пользовательских функций также являются многострочными, что, по-видимому, не оказывает существенного влияния на производительность в нашем случае.   -  person David Taylor    schedule 22.10.2009


Ответы (4)


Скалярные UDF очень медленные, встроенные UDF на самом деле являются макросами, поэтому они очень быстрые: Несколько статей:

Повторно используйте свой Код с табличными пользовательскими функциями

Много вложенных встроенные UDF очень быстрые

Дополнительные ссылки о медлительности скалярных UDF:

Шаблоны производительности SQL Server для пользовательской функции с параметрами даты и времени

Не все Пользовательские функции плохо влияют на производительность

person A-K    schedule 03.07.2009
comment
Я использую UDF с возвратом нескольких операторов, а не выбор одной таблицы из-за сложности запроса ... Как вы думаете, это все еще быстро? - person FlySwat; 04.07.2009
comment
По моему опыту, вложенные встроенные UDF могут очень хорошо снизить сложность. Многооператорные обычно (не всегда) несколько медленнее. Только бенчмаркинг может показать, насколько медленнее в вашем конкретном случае. Я бы сначала попробовал вложенные встроенные UDF. - person A-K; 04.07.2009
comment
@AlexKuznetsov: не могли бы вы опубликовать ссылку, объясняющую, как и почему скалярная UDF работает медленно? - person John Saunders; 04.07.2009
comment
@ Джон Сондерс: Ссылки добавлены. Первая, третья и четвертая ссылки объясняют это. - person A-K; 04.07.2009
comment
@AlexKuznetsov: все еще не уверен. Не могли бы вы проверить большой комментарий, который я добавил к своему ответу, и сообщить мне, чувствуете ли вы разницу между двумя SELECT? - person John Saunders; 04.07.2009
comment
Вопрос в том, почему следует избегать пользовательских функций с табличным значением?, а не в том, почему следует избегать скалярных пользовательских функций. Может быть, я что-то упускаю, когда читаю все комментарии, но кто-то ответит на вопрос. - person an phu; 19.08.2009

как вы указали, что результаты (таблицы) udf не будут ни к чему присоединены, тогда это не повлияет на производительность.

Чтобы попытаться немного объяснить, почему пользовательские функции могут восприниматься как медленные (на самом деле они просто используются неправильно), рассмотрим следующий пример.

У нас есть таблица A и таблица B. Скажем, у нас было соединение вроде

ВЫБЕРИТЕ A.col1, A.col2, B.ColWhatever FROM A JOIN B ON A.aid = b.fk_aid WHERE B.someCol = @param1 AND A.anotherCol = @param2

В этом случае SQL Server сделает все возможное, чтобы вернуть результаты наиболее эффективным из известных ему способов. Основным фактором в этом является сокращение операций чтения с диска. Итак, он будет использовать условия в предложении JOIN и where для оценки (надеюсь, с индексом), сколько строк нужно вернуть.

Теперь предположим, что мы извлекаем некоторую часть условий, используемых для ограничения количества данных, возвращаемых в UDF. Теперь оптимизатор запросов больше не может вытягивать минимальное количество строк с диска, он может работать только с теми условиями, которые он предоставляет. В двух словах - таблица udf всегда оценивается, и данные возвращаются до того, как они будут возвращены в основную sproc, поэтому, если в исходном соединении были какие-то другие критерии, которые могли вызвать меньшее количество операций чтения с диска - это будет применяться только к данным после втягивания в sproc.

Итак, скажем, мы создаем UDF для выбора строк из таблицы B, которые соответствуют предложению where. Если в таблице B 100 тыс. строк и 50% из них соответствуют критериям предложения where, то все эти строки будут возвращены в sproc для сравнения с таблицей A. Теперь, если только 10% из них имеют совпадения в таблице A сейчас мы говорим только о 5% таблицы B, с которыми мы хотим работать, но мы уже убрали 50%, большинство из которых нам не нужно!

Если это покажется полной тарабарщиной извинений - пожалуйста, дайте мне знать!

person Community    schedule 03.07.2009
comment
Итак, вы говорите, что не используйте скалярные UDF в предложении WHERE JOIN. Любые другие ситуации, когда они замедляют работу? Кроме того, если быть точным, в этом случае вы, кажется, говорите, что дело не в том, что сам UDF медленный, а в том, что в этом случае он мешает оптимизатору оптимизировать, что замедляет работу. - person John Saunders; 04.07.2009

Не могли бы вы опубликовать свой код? Вообще говоря, если вы используете скалярный udf в предложении select запроса, операторы внутри udf будут выполняться один раз для каждой строки, возвращаемой из запроса. Было бы лучше выполнить соединение с табличным значением udf или найти какой-то способ выполнить логику в вашем udf, используя соединение в вашем основном операторе SQL.

person jn29098    schedule 03.07.2009
comment
Я не использую это в соединении. Это UDF, который возвращает табличное значение. - person FlySwat; 04.07.2009

Есть ли какая-то причина, по которой вы не хотите использовать хранимую процедуру вместо UDF?

person CodeMonkey1313    schedule 03.07.2009
comment
В основном потому, что вы не можете сделать это в SQLServer, вам нужно вставить возврат sproc во временную таблицу. - person FlySwat; 04.07.2009
comment
Извините, что нельзя делать в SQL Server? Хранимая процедура может вызывать другую хранимую процедуру. - person John Saunders; 04.07.2009
comment
Невозможно выполнить SELECT * FROM EXEC Sproc, как в FireBird. Вместо этого вам нужно создать временную таблицу и выбрать ее. Я хотел бы избежать этого. - person FlySwat; 04.07.2009
comment
Но вам не нужно делать SELECT * FROM EXEC Sproc. Просто выполните EXEC SPROC. - person John Saunders; 04.07.2009
comment
Нет, этот запрос уточняется в зависимости от использования (выведенные на страницу результаты и т. д.). - person FlySwat; 04.07.2009