Несколько примеров, чтобы показать, только в случае:
Встроенная таблица оценена
CREATE FUNCTION MyNS.GetUnshippedOrders()
RETURNS TABLE
AS
RETURN SELECT a.SaleId, a.CustomerID, b.Qty
FROM Sales.Sales a INNER JOIN Sales.SaleDetail b
ON a.SaleId = b.SaleId
INNER JOIN Production.Product c ON b.ProductID = c.ProductID
WHERE a.ShipDate IS NULL
GO
Многозначная таблица значений
CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT)
RETURNS @CustomerOrder TABLE
(SaleOrderID INT NOT NULL,
CustomerID INT NOT NULL,
OrderDate DATETIME NOT NULL,
OrderQty INT NOT NULL)
AS
BEGIN
DECLARE @MaxDate DATETIME
SELECT @MaxDate = MAX(OrderDate)
FROM Sales.SalesOrderHeader
WHERE CustomerID = @CustomerID
INSERT @CustomerOrder
SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b
ON a.SalesOrderID = b.SalesOrderID
INNER JOIN Production.Product c ON b.ProductID = c.ProductID
WHERE a.OrderDate = @MaxDate
AND a.CustomerID = @CustomerID
RETURN
END
GO
Есть ли преимущество в использовании одного типа (в строке или нескольких операторов) перед другим? Существуют ли определенные сценарии, когда один лучше другого или различия чисто синтаксические? Я понимаю, что два примера запросов делают разные вещи, но есть ли причина, по которой я бы написал их таким образом?
Чтение о них и преимуществах / различиях действительно не было объяснено.
Ответы:
Исследуя комментарий Мэтта, я пересмотрел свое первоначальное утверждение. Он прав, будет разница в производительности между встроенной табличной функцией (ITVF) и многозначной табличной функцией (MSTVF), даже если они оба просто выполняют оператор SELECT. SQL Server будет рассматривать ITVF как
VIEW
в том смысле, что он рассчитает план выполнения, используя последние статистические данные по рассматриваемым таблицам. MSTVF эквивалентно вставке всего содержимого вашего оператора SELECT в табличную переменную и затем присоединению к ней. Таким образом, компилятор не может использовать какую-либо статистику таблиц для таблиц в MSTVF. Таким образом, при прочих равных условиях (что они редко бывают), ITVF будет работать лучше, чем MSTVF. В моих тестах разница в производительности во время завершения была незначительной, однако с точки зрения статистики это было заметно.В вашем случае две функции не являются функционально эквивалентными. Функция MSTV выполняет дополнительный запрос при каждом вызове и, что наиболее важно, фильтрует идентификатор клиента. В большом запросе оптимизатор не сможет воспользоваться преимуществами других типов объединений, так как он должен будет вызывать функцию для каждого переданного customerId. Однако, если вы переписали свою функцию MSTV следующим образом:
В запросе оптимизатор сможет вызвать эту функцию один раз и построить лучший план выполнения, но он все равно не будет лучше, чем эквивалентный, непараметрический ITVS или a
VIEW
.ITVF должны быть предпочтительнее, чем MSTVF, когда это возможно, потому что типы данных, обнуляемость и сопоставление из столбцов в таблице, в то время как вы объявляете эти свойства в табличной функции с несколькими утверждениями и, что важно, вы получите лучшие планы выполнения от ITVF. По моему опыту, я не нашел много обстоятельств, когда ITVF был лучшим вариантом, чем VIEW, но пробег может отличаться.
Спасибо Мэтту.
прибавление
Так как я видел, что это появилось недавно, Уэйн Шеффилд сделал отличный анализ, сравнивающий разницу в производительности между встроенными табличными функциями и функциями с несколькими утверждениями.
Его оригинальный пост в блоге.
Копировать на SQL Server Central
источник
Внутренне SQL Server обрабатывает встроенную табличную функцию со значениями так же, как это делает представление, и обрабатывает многозначную табличную функцию, аналогично хранимой процедуре.
Когда встроенная табличная функция используется как часть внешнего запроса, обработчик запросов расширяет определение UDF и генерирует план выполнения, который обращается к базовым объектам, используя индексы этих объектов.
Для табличной функции с множеством операторов план выполнения создается для самой функции и сохраняется в кэше плана выполнения (после того, как функция была выполнена в первый раз). Если табличные функции с несколькими выражениями используются как часть больших запросов, оптимизатор не знает, что возвращает функция, и поэтому делает некоторые стандартные предположения - фактически он предполагает, что функция возвратит одну строку и что функция будет доступна с помощью сканирования таблицы по таблице с одной строкой.
Если многозначные табличные функции могут работать плохо, они возвращают большое количество строк и объединяются во внешних запросах. Проблемы с производительностью связаны, прежде всего, с тем фактом, что оптимизатор создаст план, предполагая, что возвращается одна строка, что необязательно будет наиболее подходящим планом.
Как общее практическое правило, мы обнаружили, что, где это возможно, встроенные табличные функции следует использовать в предпочтении перед несколькими операторами (когда UDF будет использоваться как часть внешнего запроса) из-за этих потенциальных проблем с производительностью.
источник
Есть еще одно отличие. Встроенную табличную функцию можно вставлять, обновлять и удалять из нее - как в представлении. Применяются аналогичные ограничения - нельзя обновлять функции с использованием агрегатов, нельзя обновлять вычисляемые столбцы и т. Д.
источник
Ваши примеры, я думаю, очень хорошо отвечают на вопрос. Первая функция может быть выполнена как один выбор, и это хорошая причина для использования встроенного стиля. Второе, вероятно, может быть выполнено в виде одного оператора (используя подзапрос, чтобы получить максимальную дату), но некоторым кодировщикам может быть легче читать или более естественно делать это в нескольких утверждениях, как вы это сделали. Некоторые функции просто не могут быть выполнены одним оператором, и поэтому требуют версии с несколькими операторами.
Я предлагаю использовать самое простое (встроенное) всякий раз, когда это возможно, и использовать несколько утверждений, когда это необходимо (очевидно) или когда личные предпочтения / удобочитаемость делают это дополнительным набором текста.
источник
Посмотрите Сравнение встроенных и табличных функций с несколькими утверждениями, вы можете найти хорошие описания и показатели производительности.
источник
Я не проверял это, но функция нескольких операторов кэширует набор результатов. Могут быть случаи, когда оптимизатор слишком много встроит функцию. Например, предположим, что у вас есть функция, которая возвращает результат из разных баз данных, в зависимости от того, что вы передаете как «Номер компании». Как правило, вы можете создать представление с объединением all, а затем фильтровать по номеру компании, но я обнаружил, что иногда sql-сервер откатывает весь объединение и не достаточно умен, чтобы вызвать выбранное. Табличная функция может иметь логику для выбора источника.
источник
Другим случаем использования многострочной функции было бы обойти сервер sql от нажатия предложения where.
Например, у меня есть таблица с именами таблиц, и некоторые имена таблиц отформатированы как C05_2019 и C12_2018, и все таблицы, отформатированные таким образом, имеют одинаковую схему. Я хотел объединить все эти данные в одну таблицу и разобрать 05 и 12 в столбец CompNo и 2018,2019 в столбец год. Тем не менее, есть другие таблицы, такие как ACA_StupidTable, которые я не могу извлечь CompNo и CompYr и получить ошибку преобразования, если я попытаюсь. Итак, мой запрос состоял из двух частей: внутреннего запроса, который возвращал только таблицы, отформатированные как «C_______», тогда внешний запрос выполнял преобразование подстроки и int. то есть приведение (подстрока (2, 2) как int) как CompNo. Все выглядит хорошо, за исключением того, что сервер sql решил установить функцию Cast до того, как результаты были отфильтрованы, и поэтому я получаю ошибку преобразования. Табличная функция с множеством операторов может предотвратить это,
источник
Может быть, в очень сжатой форме. ITVF (встроенный TVF): больше, если вы - человек БД, вид параметризованного представления, возьмите один SELECT st
MTVF (Multi-Statement TVF): разработчик, создает и загружает переменную таблицы.
источник
если вы собираетесь выполнить запрос, вы можете присоединиться к функции Inline Table Valued, например:
это понесет небольшие накладные расходы и будет работать нормально.
Если вы попытаетесь использовать таблицу Multi Statement Valued в аналогичном запросе, у вас будут проблемы с производительностью:
поскольку вы будете выполнять функцию 1 раз для каждой возвращаемой строки, так как результирующий набор становится большим, он будет работать все медленнее и медленнее.
источник