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

29

Много написано об опасностях скалярных пользовательских функций в SQL Server. Случайный поиск вернет кучу результатов.

Однако есть несколько мест, где Scalar UDF - единственный вариант.

Как пример: при работе с XML: XQuery не может использоваться как определение вычисляемого столбца. Одна из возможностей, задокументированных Microsoft, заключается в использовании Scalar UDF для инкапсуляции вашего XQuery в Scalar UDF, а затем в его вычисляемом столбце.

Это имеет различные эффекты и некоторые обходные пути.

  • Выполняется строка за строкой при запросе таблицы
  • Принудительно запускает все запросы к таблице.

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

Есть ли известный способ сделать это?

Эрик Дарлинг
источник

Ответы:

31

Да, если вы:

  • работают на SQL Server 2014 или более поздней версии; а также
  • умеют выполнять запрос с активным флагом трассировки 176 ; а также
  • вычисляемый столбец PERSISTED

В частности, требуются как минимум следующие версии :

  • Накопительное обновление 2 для SQL Server 2016 SP1
  • Накопительное обновление 4 для SQL Server 2016 RTM
  • Накопительное обновление 6 для SQL Server 2014 с пакетом обновления 2

НО, чтобы избежать ошибки (ссылка на 2014 , а также на 2016 и 2017 ), внесенной в эти исправления, вместо этого примените:

Флаг трассировки действует как –Tопция запуска , как в глобальной области DBCC TRACEON, так и в области сеанса с использованием , а также для каждого запроса OPTION (QUERYTRACEON)или руководства плана.

Флаг трассировки 176 предотвращает расширение сохраняемого вычисляемого столбца.

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

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

Флаг трассировки 176 помогает в этом, если столбец сохраняется, не загружая определение (поскольку расширение пропускается). Таким образом, скалярная пользовательская функция никогда не присутствует в дереве запросов компиляции, поэтому параллелизм не отключается.

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

Дополнительные сведения см. В моей статье на SQLPerformance.com « Правильно сохраненные вычисляемые столбцы» .

Поскольку в вопросе упоминается XML, в качестве альтернативы продвижению значений с использованием вычисляемого столбца и скалярной функции, вы также можете взглянуть на использование выборочного индекса XML, как вы писали в разделе «Выборочные индексы XML: совсем неплохо» .

Пол Уайт говорит, что GoFundMonica
источник
10

В дополнение к отличному « Да» # 1 от Пола, на самом деле есть « Да № 2», который:

  • работает еще в SQL Server 2005,
  • не требует установки флага трассировки,
  • вовсе не требует, чтобы вычисляемый столбец быть PERSISTED, и
  • (из-за того, что не требуется флаг трассировки 176), не препятствует сопоставлению выражения запроса с сохраняемыми вычисляемыми столбцами

Единственные недостатки (насколько я могу судить):

  • не работает с базой данных SQL Azure (по крайней мере, пока, хотя работает на Amazon RDS SQL Server и SQL Server в Linux), и
  • немного за пределами зоны комфорта многих DBA

И этот вариант: SQLCLR

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

Я использовал начальную настройку из следующего поста в блоге (надеюсь, что OP не считает это ненадежным источником 🙃):

Плохие джинсы идеи: многократные индексы

И выполнили следующие тесты:

  1. Запустил начальный запрос как есть ─⇾ Параллелизм (как и ожидалось)
  2. Добавлен непостоянный вычисляемый столбец, определенный как ([c2] * [c3])─⇾ параллелизм (как и ожидалось)
  3. Удалил этот вычисляемый столбец и добавил непостоянный вычисляемый столбец, который ссылается на скалярную пользовательскую функцию T-SQL (созданную с помощью SCHEMABINDING), определенную как RETURN (@First * @Second);─⇾ НЕТ параллелизма (как и ожидалось)
  4. Удален вычисляемый столбец T-SQL UDF и добавлен непостоянный вычисляемый столбец, который ссылается на скалярный UDF SQLCLR (пробовал с обоими IsDeterministic = trueи = false), определяемый как return SqlInt32.Multiply(First, Second);─⇾ Параллелизм ( ууууу !!)

Таким образом, хотя SQLCLR не будет работать для всех, у него, безусловно, есть свои преимущества для тех людей / ситуаций / сред, которые хорошо подходят. И, поскольку это относится к этому конкретному вопросу - приведенному примеру, касающемуся использования XQuery, - это определенно подойдет для этого (и, в зависимости от того, что конкретно делается, может быть даже немного быстрее 😎).

Соломон Руцкий
источник