当 table 为空时,此 SQL 时间范围检查约束如何失败?

How can this SQL check constraint for time ranges fail, when table is empty?

我已经实现了时间范围验证,作为检查约束,使用 SQL 中的函数,使用 this guide,几乎一字不差。

首先创建函数:

create function dbo.ValidateStatusPeriodInfoTimeRange
(
    @btf_id VARCHAR(32),
    @start_time BIGINT,
    @end_time BIGINT
)
returns bit
as
begin
declare @Valid bit = 1;

if exists( select *
           from   dbo.StatusPeriodInfoOccurrence o
           where  o.btf_id = @btf_id
           and    @start_time <= o.end_time and o.start_time <= @end_time )
   set @Valid = 0;
return @Valid;

结束

然后是约束,使用函数:

alter table dbo.StatusPeriodInfoOccurrence with nocheck add constraint 

CK_StatusPeriodInfoOccurrence_ValidateTimeRange 
    check (dbo.ValidateStatusPeriodInfoTimeRange(btf_id, start_time, end_time) = 1);

当我尝试将元素插入完全空的 table 时,我得到:

The INSERT statement conflicted with the CHECK constraint 
"CK_StatusPeriodInfoOccurrence_ValidateTimeRange". The conflict occurred in 
database "D600600TD01_BSM_Surveillance", table "dbo.StatusPeriodInfoOccurrence".

我试图弄清楚我是否在函数本身做错了什么,并创建了这个查询来检查它的 return 值:

DECLARE @ReturnValue INT
EXEC @ReturnValue =  ValidateStatusPeriodInfoTimeRange
@btf_id = 'a596933eff9143bceda5fc5d269827cd',
@start_time = 2432432,
@end_time = 432432423
SELECT @ReturnValue

但是这个 returns 1,它应该。

我不知道如何继续调试它。所有部分似乎都可以工作,但整体却不行。关于插入语句如何与检查约束冲突的任何想法?

编辑: 这是我的插入语句:

INSERT INTO StatusPeriodInfoOccurrence (btf_id, start_time, end_time) VALUES ('a596933eff9143bceda5fc5d269827cd',2432432,432432423); 

还有一个带有标识自动递增的附加主键列。

如评论中所述,在 记录插入 table 之后检查约束 ,然后提交或回滚事务,具体取决于结果一个检查,在你的例子中总是会失败,如查询:

select *
from   dbo.StatusPeriodInfoOccurrence o
where  o.btf_id = @btf_id
and    @start_time <= o.end_time and o.start_time <= @end_time

将 return 总是至少一行(插入的那一行)。

所以,知道了,你应该检查查询 returns 是否不止一条记录 ,所以 if 语句中的条件应该变成:

if (select count(*)
          from   dbo.StatusPeriodInfoOccurrence o
          where  o.btf_id = @btf_id
          and    @start_time <= o.end_time and o.start_time <= @end_time ) > 1

这个解决方案工作正常(在我的数据库上测试过)。

CHECK 约束在插入行之后发生,因此在当前形式下,约束失败,因为插入的行恰好与约束匹配。为了使其作为约束(而不是触发器)工作,必须有一种方法来区分我们正在检查的行与所有其他行。 显示了如何在不依赖 IDENTITY 的情况下执行此操作,但如果你确实有明确排除该行可能会更简单:

create function dbo.ValidateStatusPeriodInfoTimeRange
(
    @id INT,
    @btf_id VARCHAR(32),
    @start_time BIGINT,
    @end_time BIGINT
)
returns bit
as
begin
declare @Valid bit = 1;

if exists( select *
           from   dbo.StatusPeriodInfoOccurrence o
           where  o.id <> @id AND o.btf_id = @btf_id
           and    @start_time <= o.end_time and o.start_time <= @end_time )
   set @Valid = 0;
return @Valid;
end;

约束定义为

check (dbo.ValidateStatusPeriodInfoTimeRange(id, btf_id, start_time, end_time) = 1)

无论采用何种方法,(btf_id, start_time)(btf_id, end_time) 上的索引都是保持这种可扩展性的好主意,否则每次插入都需要进行完整的 table 扫描。