当 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 扫描。
我已经实现了时间范围验证,作为检查约束,使用 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 扫描。