2011-05-27 119 views
2

执行一些MSSQL练习,我试图创建一个触发器。然而,我的解决方案在理论上对我来说是正确的,但它不起作用。卡在创建触发器

目标是为只有两列的表创建触发器。一列是主键,是标识,不允许空值。另一列是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 

然而,尽管如此,交易执行本身。我不确定为什么会发生这种情况,并且触发器不会自行发射。

那么有人在这里启发我的疑虑吗?

我也在触发器开始时使用了set ANSI_WARNINGS OFF。

回答

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) 
+0

工作,非常感谢你! – Parijat 2011-05-27 22:21:34

0

是的,这是一个问题。 COUNT的parens内部的表达式必须评估为不为null,否则将不计算。因此,使用*1或表达式中的任何不可空列都更安全。最常遇到的表达方式是'*',但您也可以遇到'1'。这些表达式在性能方面没有区别。但是,如果您使用可以评估为空的表达式(如可空列),则您的计数和其他聚合可以完全关闭。

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