执行一些MSSQL练习,我正在尝试创建一个触发器。但是,我所遇到的解决方案在理论上对我来说是正确的,但它不起作用。
目的是为只有两列的表创建一个触发器。一列是主键,是Identity,不允许空值。另一列是ALLOWS NULL值。但是,它仅允许整个表中的单行的NULL值。基本上,触发器应触发此表上的插入/更新操作,当该表中的列已存在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。
答案 0 :(得分:2)
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)
答案 1 :(得分:0)
是的,这是一个问题。 COUNT的parens中的表达式必须求值为null,否则不会计算。因此,在表达式中使用*
或1
或任何不可为空的列更安全。最常见的表达式是'*'
,尽管你也可以遇到'1'。这些表达在性能方面没有区别。但是,如果使用可以计算为null的表达式(如可空列),则可以完全关闭计数和其他聚合。
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