函数在 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 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章