问题描述
|
执行一些MSsql练习,而我正在尝试创建一个触发器。但是,从理论上讲,我所拥有的解决方案对我来说是正确的,但它不起作用。
目的是为只有两列的表创建触发器。一栏是主键,是身份,不允许空值。另一列是允许NULL值的列。但是,它只允许在整个表中为单行提供NULL值。基本上,应该为该表上的插入/更新操作触发一个触发器,当表中该列已存在一个空值时,该触发器将尝试将该列插入/更新为空值。
我在触发代码中捕获了这种情况,如下所示:
After Insert,Update
AS
set ANSI_WARNINGS OFF
If ( (select count(NoDupName) from TestUniqueNulls where NoDupName is null) > 1 )
BEGIN
Print \'There already is a row that contains a NULL value,transaction aborted\';
ROLLBACK TRAN
END
但是,事务仍然会自行执行。我不知道为什么会这样,并且触发器没有触发自身。
那么有人在这里启发我的疑虑吗?
我还使用了在触发器开始时设置ANSI_WARNINGS OFF的功能。
解决方法
count(col)
仅计算非空值,因此count(NoDupName) ... where NoDupName is null
将始终为零。您需要改为选择count(*)
。
我意识到这只是一个练习,但是索引视图可能是一个更好的机制。
CREATE VIEW dbo.NoMoreThanOneNull
WITH SCHEMABINDING
AS
SELECT NoDupName
FROM dbo.TestUniqueNulls
WHERE NoDupName IS NULL
GO
CREATE UNIQUE CLUSTERED INDEX ix ON dbo.NoMoreThanOneNull(NoDupName)
, 是的,这是一个陷阱。 COUNT的括号内的表达式的计算结果必须为非null,否则将不计算在内。因此,在表达式中使用*
或1
或任何不可为空的列更为安全。尽管您也可以遇到\'1 \',但最常遇到的表达式是\'*\'
。在性能方面,这些表达式之间没有区别。但是,如果您使用可以求值为null的表达式(如nullable列),则计数和其他聚合可能会完全不可用。
create table nulltest(a int null)
go
insert nulltest(a) values (1),(null),(2)
go
select * from nulltest
select COUNT(*) from nulltest
select COUNT(1) from nulltest
select COUNT(a) from nulltest
go
drop table nulltest