Использование нескольких строк из нескольких таблиц для сохраняемого вычисляемого столбца со скалярной UDF - PullRequest
0 голосов
/ 10 октября 2018

Я пытаюсь создать новое поле в таблице транзакций в виде вычисляемого столбца Persisted , используя значение Scalar UDF в качестве значения для поля.

Я понимаю, что для столбца Persisted необходимо, чтобы значение было детерминированным, то есть имеющаяся у меня UDF для нескольких таблиц является недетерминированной, поскольку она не использует поля из исходной таблицы.

Функция :

USE [MyDatabase]
GO
/****** Object:  UserDefinedFunction [dbo].[fnCalcOutstandingBalance]    
Script Date: 08/10/2018 14:01:18 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[fnCalcOutstandingBalance](@ItemReferance int)

RETURNS INT
WITH SCHEMABINDING 
AS
Begin
DECLARE @AcceptedQty INT
DECLARE @SumOfQty INT
DECLARE @Result INT

SELECT @AcceptedQty = 

    ISNULL([Accepted Quantity],0)
    FROM 
    dbo.[Order Transactions Table]
    WHERE @ItemReferance = [Item Referance] 

SELECT @SumOfQty =
    ISNULL(sum(Quantity),0)
    FROM dbo.[Delivery Table]
    GROUP BY [Item Referance]
    HAVING @ItemReferance = [Item Referance]

    SET @Result = ISNULL(@AcceptedQty,0) - ISNULL(@SumOfQty,0)

return @Result

END

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

Добавление столбца :

ALTER TABLE [Order Transactions Table]
ADD CalcOB AS [dbo].[fnCalcOutstandingBalance]([Item Referance]) PERSISTED

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

Ответы [ 3 ]

0 голосов
/ 10 октября 2018

@ gbn ударил его из парка своим ответом, но позвольте мне добавить свои 0,02 доллара.Поскольку ваш скалярный UDF обращается к таблицам, я уверен, что вы не сможете сохранить этот столбец.Тем не менее, давайте проясним все на 100%:

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

Во-первых, даже если вы можете сохранить этот столбец, любые запросы, которые обращаются к этой таблице, будут выполняться медленнее, а иногда даже медленнее.Скалярные пользовательские функции T-SQL для вычисляемых столбцов, в качестве ограничений или для значений по умолчанию делают запросы, ссылающиеся на эту таблицу, непараллелизуемыми;только серийное исполнение!Кроме того, доступные оптимизации становятся существенно ограниченными после введения скалярного UDF T-SQL.Опять же - плохая, плохая плохая идея.

Как сказал gbn - индексированное представление - это путь (если вы можете потерять это левое соединение).Другой вариант - использовать функцию Inline Table Valued, когда вам нужно это значение;он будет работать лучше, чем вычисляемый столбец (при условии, что вы добавите соответствующие индексы. Функция будет выглядеть следующим образом:

CREATE FUNCTION dbo.fnCalcOutstandingBalance(@ItemReferance int)
RETURNS TABLE WITH SCHEMABINDING  AS RETURN
SELECT   Result = ISNULL(sum(Quantity),0) -
         (
           SELECT ISNULL([Accepted Quantity],0)
           FROM   dbo.[Order Transactions Table]
           WHERE  @ItemReferance = [Item Referance] 
         )
FROM     dbo.[Delivery Table]
GROUP BY [Item Referance]
HAVING   @ItemReferance = [Item Referance];

Чтобы использовать эту функцию, вы должны понимать APPLY . Вотхорошее прочтение о том, почему скалярные пользовательские функции T-SQL ужасны для вычисляемых столбцов и ограничений.

Вычисляемый столбец с [scalar udf] может повлиять на производительность запроса - Кун Ченг (SQLCAT)

Еще один скрытый убийца параллелизма: скалярные UDF в проверочных ограничениях - Эрик Дарлинг

Другая причина, по которой скалярные функции в вычисляемых столбцах - плохая идея - ЭрикДорогая

Осторожно-ряд-ряд-операции-udf-clothing - Брайан Моран

Будьте осторожны с ограничениями, вызывающими UDFs - Тибор Караси

Почему в План выполнения включен скалярный вызов udf для сохраняемого вычисляемого столбца? - Переполнение стека

0 голосов
/ 11 октября 2018

Для всех, кому это интересно, мне удалось найти обходной путь для этой проблемы с помощью курсора (спасибо @ gbn ) для обработки вычислений на существующих данных и для заполнения новогополе ( CalculatedOB ) с соответствующим вычисленным значением.

Я использовал триггеры (для [Таблица заказов транзакций]. [Принятые количества] и [ДоставкаТаблица]. [Количество] ) для обработки любых будущих изменений непогашенного остатка.

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

Курсор для заполнения существующих данных:

declare @refid int;
declare @Result int;
declare refcursor cursor for
select [Item Referance] from [Order Transactions Table];

open refcursor

fetch next from refcursor into @refid

while @@FETCH_STATUS = 0
begin 
print @refid

fetch next from refcursor into @refid
set @Result = [dbo].[fnCalcOutstandingBalance](@refid)

update [Order Transactions Table] set CalculateOB = @Result 
    where [Item Referance] = @refid
end 

close refcursor;
deallocate refcursor;

Пример запуска обновления:

CREATE TRIGGER [dbo].[UPDATE_AcceptedQty]
ON [dbo].[Order Transactions Table]
for update
AS

DECLARE @ItemRef int;
declare @result int;

IF UPDATE ([Accepted Quantity])
Begin

SELECT @ItemRef=i.[Item Referance] from INSERTED i;

SET @result = [dbo].[fnCalcOutstandingBalance](@ItemRef)

UPDATE [Order Transactions Table] set CalculateOB = @Result 
    where [Item Referance] = @ItemRef

END

GO

Сочетание этихдва метода позволили мне имитировать функциональность столбца Computed без ограничений требований детерминизма или снижения производительности.

Большое Спасибо @gbn и @Alan Burstein за их вклад!

0 голосов
/ 10 октября 2018

Вы можете попробовать WITH SCHEMABINDING в UDF.
Это означает, что базовые таблицы не могут быть изменены без удаления UDF (и вычисляемого столбца и т. Д.)

Без этого это определенно предотвратит PERSISTED.

Вы понимаете, что такое использование UDF имеет огромное значение для производительности и параллелизма?

После комментариев

CREATE VIEW dbo.SomeView
AS
SELECT
   ott.Col1, ott.Col2, ...,
   OutstandingBalance = ISNULL(ott.[Accepted Quantity],0) - ISNULL(SUM(dt.Quantity),0)
FROM
   dbo.[Order Transactions Table] ott
   LEFT JOIN
   dbo.[Delivery Table] dt ON ott.[Item Referance] = dt.[Item Referance]
GROUP BY
   ott.Col1, ott.Col2, ott.[Accepted Quantity], ...

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

...