Ahmad_kelany Ответов: 2

Как сделать так, чтобы вычисляемый столбец (скалярная функция) сохранялся ?


Всем привет,
У меня есть таблица InvoiceHeader со следующим определением:
InvoiceId int NOT NULL (ключ)

и таблица InvoiceDetail со следующим определением:
DetailId int NOT NULL (ключ)
InvoiceId int NOT NULL (FKey)
Пакеты smallint NULL
PackagePrice денег нуль
Единицы измерения smallint NULL
Цена денег нуль
Discount1 денег нуль
Discount2 денег нуль

Я добавил вычисляемый столбец в таблицу InvoiceDetail:
Ценность
формула:
(((CONVERT([decimal](18,6),[Packages])*CONVERT([decimal](18,6),[PackagePrice])+CONVERT([decimal](18,6),[Units])*CONVERT([decimal](18,6),[UnitPrice]))*(((100)-CONVERT([decimal](18,6),[Discount1]))/(100)))*(((100)-CONVERT([decimal](18,6),[Discount2]))/(100)))


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

Я знаю, что причина этого-плохой дизайн в первую очередь, но это не для меня, чтобы изменить сейчас :(
и все эти преобразования, наконец, сделали меня способным сделать эту колонку стойкой.

Затем я добавил функцию для расчета InvoiceTotal :

ALTER function [dbo].[GetInvoiceTotal](@InvoiceID as int)
returns decimal(18,2)
as
begin
declare @total as decimal(18,2)
select @total =CONVERT(decimal(18,2) , sum(convert(decimal(18,2) ,invd.Value)))
from InvoiceDetails invd  
where invd.InvoiceID = @InvoiceID
return convert(decimal(18,2) , Isnull(Convert(decimal(18,2) , @total ) , 0.00) )

end;


Затем в таблицу InvoiceHeader был добавлен вычисляемый столбец :
InvoiceTotal
как ДБО.GetInvoiceTotal(InvoiceId)

но когда я пытаюсь сделать это упорствовал я получаю ошибку:
cannot be persisted because the column is non-deterministic.


Что я уже пробовал:

Я читал документацию по адресу :
http://msdn.microsoft.com/en-us/library/ms178091.aspx

Функция isnull всегда детерминирована,
Все агрегатные функции являются детерминированными, если они не заданы с помощью предложений OVER и ORDER BY.
Проблемы возникают с : datetime, smalldatetime или sql_variant... ни один из которых здесь не используется.
Я скрыл h*ll кода, чтобы сделать упорство.

Что я упускаю ?

Santosh kumar Pithani

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

Ahmad_kelany

Привет,
Спасибо что ответили,

Его тип-десятичный.

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

2 Ответов

Рейтинг:
13

Richard Deeming

Ваш UDF недетерминирован-при тех же входных данных (идентификатор счета-фактуры), он может и будет возвращать разные значения.

Детерминированные и недетерминированные функции | Microsoft Docs[^]

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

Представьте себе, что вы делаете это в системе с 20 миллионами счетов - фактур-вся база данных остановится, когда вы попытаетесь изменить данные.


Ahmad_kelany

Спасибо за разъяснение,
Теперь это имеет смысл.

Хорошего дня.

Рейтинг:
1

Santosh kumar Pithani

ALTER FUNCTION [dbo].[GetInvoiceTotal](@InvoiceID AS INT)
RETURNS DECIMAL(18,2)
AS
BEGIN
DECLARE @total AS DECIMAL(18,2)
SELECT @total =ISNULL(SUM(CAST(invd.Value AS DECIMAL(18,2))),0.00)
                FROM InvoiceDetails invd  
                          WHERE invd.InvoiceID = @InvoiceID
RETURN @total
END;


Ahmad_kelany

Это было то, что я сделал в самом начале, но, к сожалению, это тоже не работает.