函数在 sql server 上的 CHECK Constraint 中不起作用
Posted
技术标签:
【中文标题】函数在 sql server 上的 CHECK Constraint 中不起作用【英文标题】:function do not work in CHECK Constraint on sql server 【发布时间】:2015-01-13 18:44:47 【问题描述】:我的数据库有问题,我尝试简化我的数据库.. 经过多次尝试该函数使用'X'表,然后在'X'表上使用该函数。你不能使用函数使用同一张表...
--建表后:
create table Items(
ID int,
Price money
);
--插入一些值
insert into Items Values(1,4);
insert into Items Values(2,5);
--创建上表使用的函数
CREATE FUNCTION GetMaxPrice
(
-- Add the parameters for the function here
)
RETURNS Money
AS
BEGIN
-- Declare the return variable here
DECLARE @result money = 5;
select @result = max(Price)
from Items;
-- Return the result of the function
RETURN @result;
END
--然后alter table 添加约束 --接受任何低于或等于表中价格的价格
alter table Items add check(Price <= dbo.GetMaxPrice())
--之后,我尝试插入项目
insert into Items Values(3,4);
insert into Items Values(4,15); -- <-- here, I have problem. it inserted in Database..
--why inserted when max value is 5 ??
我使用 sql server 2012 Express,Win7
【问题讨论】:
【参考方案1】:您遇到的问题是,当检查约束触发时,新值存在于表中(在隐式事务中),因此当您插入 15 时,max(Price) 为 15,所以满足约束,INSERT 成功。我有一个彻底的谷歌试图找到记录在哪里,但没有找到任何确定的东西。
实现您想要的效果的另一种方法是使用 INSTEAD OF 触发器,示例如下。
不过,提个建议——这种验证让我觉得很容易出错。我会尝试将您的极限值与数据分开 - 可能在另一个表中。
希望这会有所帮助,
里斯
create table dbo.Items(
ID int,
Price money
);
insert into dbo.Items Values(1,4);
insert into dbo.Items Values(2,5);
go
create trigger trgItemsInsert on dbo.Items instead of insert as
begin
-- Lookup current max price
declare @MaxPrice money = (select max(Price) from dbo.Items)
if exists (select 1 from inserted where Price > @MaxPrice)
begin
-- If there is a price greater than the current max then reject the insert
declare @msg varchar(255) = 'Maximum allowed price is ' + cast(@MaxPrice as varchar(32)) + '.'
rollback
raiserror('%s', 16, 1, @msg)
end
else
begin
-- Otherwise perform the insert
insert into dbo.Items
select ID,Price from inserted
end
end
go
insert into dbo.Items Values(3,4);
insert into dbo.Items Values(4,15);
go
select * from dbo.Items
go
【讨论】:
【参考方案2】:似乎在初始插入之后才执行检查约束。所以数据库引擎
-
插入数据
检查约束
如果插入违反约束,则回滚插入
如果插入正常,提交数据
您可以在查询执行计划中看到这发生的顺序:
图片中突出显示的Assert的属性描述为“用于验证指定条件是否存在”。
我无法从 Microsoft 找到有关这方面的文档,如果有人知道在哪里可以找到它,请告诉我。
【讨论】:
【参考方案3】:试试这个代码。它对我来说很好用。
CREATE TABLE item
(
ID INT,
Price MONEY
);
--Insert some values
INSERT INTO item
VALUES (1,
4);
INSERT INTO item
VALUES (2,
5);
--create a function use above table
CREATE FUNCTION GetMax (@price MONEY)
RETURNS MONEY
AS
BEGIN
-- Declare the return variable here
DECLARE @result MONEY = 5;
SELECT @result = max(Price)
FROM item;
IF @price < @result
RETURN 1
RETURN 0
-- Return the result of the function
END
--then alter table to add constraint --accept any price less or equal price on table
ALTER TABLE item
WITH NOCHECK ADD CONSTRAINT ck1 CHECK(dbo.GetMax(Price)=(1))
--ALTER TABLE item
-- DROP CONSTRAINT ck1
--after that, i try to insert item
INSERT INTO item
VALUES (3,
4);
INSERT INTO item
VALUES (4,
15);
【讨论】:
有趣,遗憾的是没有任何关于此行为的 MS 文档。我想知道将列值传递给函数是否会影响 SQL Server 如何感知它的确定性。我仍然会避免这种方法并找到实现所需结果的替代方法。 有趣,我遇到了同样的问题,我通过将它作为参数传递来修复它,即使我没有在函数内部使用它。以上是关于函数在 sql server 上的 CHECK Constraint 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server 中系统视图sysobjects中type字段的说明