Почему пользовательские функции SQL-сервера так ограничены? - PullRequest
2 голосов
/ 25 февраля 2010

Из документов MSDN для create function:

User-defined functions cannot be used to perform actions that modify the database state.

Мой вопрос просто - почему ?

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

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

Причина этого вопроса заключается в следующем: я пишу функцию, возвращающую GUID для определенного уникального целочисленного идентификатора. Если GUID уже выделен для этого идентификатора, я просто возвращаю его; в противном случае я хочу сгенерировать новый GUID, сохранить его в таблице и вернуть вновь сгенерированный GUID. (Да, это звучит многословно и, возможно, безумно, но когда вы отправляете данные в другую компанию разработчиков, которая считает, что их дизайн был передан Богом и не может быть улучшен, проще просто улыбнуться, кивнуть и сделать то, что они просят ).

Я знаю, что могу использовать хранимую процедуру с выходным параметром для достижения того же результата, но затем я должен объявить новую переменную просто для хранения результата sproc. Кроме того, мне нужно преобразовать мой простой select в цикл while, который вставляется во временную таблицу, и вызывать sproc для каждой итерации этого цикла.

Ответы [ 3 ]

2 голосов
/ 25 февраля 2010

Обычно лучше всего думать о доступных инструментах как о спектре, от представлений через UDF до хранимых процедур. С одной стороны (Представления) у вас есть много ограничений, но это означает, что оптимизатор может на самом деле «видеть» код и делать разумные выборы. С другой стороны (хранимые процедуры) у вас есть большая гибкость, но поскольку у вас есть такая свобода, вы теряете некоторые способности (например, потому что вы можете возвращать несколько наборов результатов из хранимого процесса, вы теряете способность «составлять»). это как часть большего запроса).

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

0 голосов
/ 25 февраля 2010

Иногда, если вы не можете реализовать решение, которое вы придумали, это может указывать на то, что ваше решение не является оптимальным.

Используя подобное утверждение

INSERT INTO IntGuids(IntValue, GuidValue)
SELECT MyIntValues.IntValue, NEWID()
FROM MyIntValues
LEFT OUTER JOIN IntGuids ON MyIntValues.IntValue = IntGuids.IntValue
WHERE IntGuids.IntValue IS NULL

создает все GUID, которые вам нужны в 1 выражении. Нет необходимости SELECT + INSERT для каждого отдельного значения.

0 голосов
/ 25 февраля 2010

Я понимаю вашу проблему, я думаю, но, исходя из вашего комментария:

Я хочу сделать что-то вроде select my_udf(my_variable) from my_table, где my_udf либо выбирает, либо создает возвращаемое значение

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

Чтение вашего описания того, что вам действительно нужно сделать:

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

Я знаю, что могу использовать хранимую процедуру с выходным параметром для достижения того же результата, но затемЯ должен объявить новую переменную только для хранения результата sproc.Кроме того, мне нужно преобразовать мой простой выбор в цикл while, который вставляется во временную таблицу, и вызывать sproc для каждой итерации этого цикла.

из этого последнего предложения это звучит каквам нужно обрабатывать много строк одновременно, так как насчет одного INSERT, который вставляет GUID для тех идентификаторов, у которых их еще нет, а затем один SELECT, который возвращает все GUID, которые (сейчас) существуют?

...