Проверьте ограничение, которое вызывает функцию не работает при обновлении - PullRequest
0 голосов
/ 07 июня 2018

Я создал ограничение, которое не позволяет распределениям в одной таблице превышать запасы в другой таблице (подробности см. В моем предыдущем вопросе Здесь ).Но по какой-то причине ограничение работает, как и ожидалось, только когда я вставляю новые выделения, но при обновлении это не предотвращает нарушение.

Вот мое ограничение:

([dbo].[fn_AllocationIsValid]([Itemid]) = 1)

А вот и функция:

CREATE FUNCTION [dbo].[fn_AllocationIsValid] (@itemId as int)  
RETURNS int  AS  
BEGIN 
DECLARE @isValid bit;

SELECT @isValid = CASE WHEN ISNULL(SUM(Allocation), 0) <= MAX(Inventory) THEN 1 ELSE 0 END
FROM Allocations A 
JOIN Items I ON I.Id = A.ItemId
WHERE I.Id = @itemId
GROUP BY I.Id;

RETURN @isValid;
END

Спасибо.

ДОБАВЛЕНО:

Вот мои таблицы:

CREATE TABLE [allocations] (
[userID] [bigint] NOT NULL ,
[itemID] [int] NOT NULL ,
[allocation] [bigint] NOT NULL ,
CONSTRAINT [PK_allocations] PRIMARY KEY  CLUSTERED 
(
    [userID],
    [itemID]
)  ON [PRIMARY] ,
CONSTRAINT [FK_allocations_items] FOREIGN KEY 
(
    [itemID]
) REFERENCES [items] (
    [id]
) ON DELETE CASCADE  ON UPDATE CASCADE ,
CONSTRAINT [CK_allocations] CHECK ([dbo].[fn_AllocationIsValid]([Itemid], [Allocation]) = 1)
) ON [PRIMARY]

CREATE TABLE [dbo].[Items](
[Id] [int] NOT NULL,
[Inventory] [int] NOT NULL
) ON [PRIMARY]
GO

INSERT INTO Items (Id, Inventory) VALUES (2692, 336)

INSERT INTO Allocations (UserId, ItemId, Allocation) VALUES(4340, 2692, 336)
INSERT INTO Allocations (UserId, ItemId, Allocation) VALUES(5895, 2692, 0)

Следующее выполнение оператора должно завершиться неудачно, но это не так:

update allocations set allocation = 5
where userid = 5895 and itemid = 2692

1 Ответ

0 голосов
/ 07 июня 2018

Хорошо, я только что кое-что узнал.

Так что получается, что с CHECK CONSTRAINTS и UPDATES, CONSTRAINT проверяется, только если один из столбцов, на которые есть ссылки в CONSTRAINT, изменился.

В вашем случае ваш CONSTRAINT проверяет UDF, которому вы передаете ItemID.

В вашем UPDATE, по-видимому, вы изменяете только значение Allocation, а не ItemID, поэтому оптимизатор думает: «Если ItemID не изменился, то нет необходимости проверять ограничение", и это не так, и ОБНОВЛЕНИЕ завершается успешно, даже несмотря на то, что CONSTRAINT должна была его потерпеть.

Я проверил это, перестроив вашу функцию и ограничение и добавив к ней Allocation:

ALTER FUNCTION [dbo].[fn_AllocationIsValid] (@itemId as int, @Allocation int)  
RETURNS int  AS  
BEGIN 
DECLARE @isValid bit;

SELECT @isValid = CASE WHEN ISNULL(SUM(Allocation), 0) <= MAX(Inventory) THEN 1 ELSE 0 END
FROM Allocations A 
JOIN Items I ON I.Id = A.ItemId
WHERE I.Id = @itemId
GROUP BY I.Id;

RETURN @isValid;
END

И:

ALTER TABLE [dbo].[Allocations]  WITH CHECK ADD  CONSTRAINT [CK_Allocations] 
CHECK  (([dbo].[fn_AllocationIsValid]([Itemid], Allocation)=(1)))
GO

Обратите внимание, что мне сначала пришлось УБРАТЬ исходное ограничение и обрезать / заново заполнить таблицу, но я не хочу, чтобы я показал вам, как это сделать.

Также обратите внимание, что Allocation не участвует ни в какой логике функции.Я вообще не менял логику, я просто добавил параметр для @Allocation.Параметр никогда не используется.

Затем, когда я сделал UPDATE, который поднял сумму Allocation до значения MAX, я получил ожидаемую ошибку:

Оператор UPDATE конфликтовалс ограничением CHECK "CK_Allocations".Конфликт произошел в базе данных "Tab_Test", таблице "dbo.Allocations".

Почему?Поскольку @Allocation не не используется в логике функции, столбец Allocation равен с ссылкой в CONSTRAINT, поэтому оптимизатор проверяет ограничение, когда значениеиз Allocation изменений.

Некоторые утверждают, что из-за подобных вещей всегда предпочтительнее использовать TRIGGER вместо CHECK CONSTRAINT, который вызывает UDF.Я не уверен, и я не видел никаких воспроизводимых экспериментов, которые доказывают это.Но я оставляю это на ваше усмотрение, каким путем вы хотите пойти с этим.

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

PS: Приписывая должное доверие, я узнал все это сНекоторая помощь из сообщения на форуме в моем комментарии к вопросу, которая привела к этому блогу по теме .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...