函数在 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 2016 SA账号登陆不上的办法

SQL Server 中系统视图sysobjects中type字段的说明

SQL Server系统表sysobjects介绍

SQL Server创建 学号 性别 课程编号 check约束 主键约束 UNIQUE约束

SQL Server 条件 CHECK 约束

SQL Server里的 ISNULL 与 NULLIF