我上次遇到过以下问题,只是想知道为什么
null<> '值'
在触发器中不起作用。 要解决问题,我必须使用isnull函数
isnull(null,'')<> ISNULL( '值', '')
下面要测试的所有代码:
-- create main table
CREATE TABLE T_SAMPLE
(
ID INT,
NAME NVARCHAR(20)
)
GO
-- populate data in main table
INSERT INTO T_SAMPLE
VALUES (1, 'ONE'),
(2,'TWO'),
(3,'THREE')
GO
-- create table to store changes
CREATE TABLE T_SMAPLE_TEST
(
NAME NVARCHAR(40)
)
GO
-- create trigger on main table
CREATE TRIGGER [dbo].[TRG_SAMPLE]
ON [dbo].[T_SAMPLE]
AFTER UPDATE
AS
BEGIN
INSERT INTO T_SMAPLE_TEST
SELECT D.NAME + ',' + I.NAME
FROM INSERTED I
INNER JOIN DELETED D
ON I.ID = D.ID
WHERE D.NAME <> I.NAME
END
GO
-- ######### test #########
-- below works fine
UPDATE T_SAMPLE
SET NAME = 'ONE2'
WHERE ID = 1
GO
-- test data by running below selects
SELECT * FROM T_SMAPLE_TEST
SELECT * FROM T_SAMPLE
-- but when try to update to null value from not null or vice versa, it doesn't work
UPDATE T_SAMPLE
SET NAME = NULL
WHERE ID = 1
GO
-- test data by running below selects
SELECT * FROM T_SMAPLE_TEST
SELECT * FROM T_SAMPLE
UPDATE T_SAMPLE
SET NAME = 'AGAIN'
WHERE ID = 1
GO
-- test data by running below selects
SELECT * FROM T_SMAPLE_TEST
SELECT * FROM T_SAMPLE
-- solution for this is to alter trigger as below
ALTER TRIGGER [dbo].[TRG_SAMPLE]
ON [dbo].[T_SAMPLE]
AFTER UPDATE
AS
BEGIN
INSERT INTO T_SMAPLE_TEST
SELECT D.NAME + ',' + I.NAME
FROM INSERTED I
INNER JOIN DELETED D
ON I.ID = D.ID
WHERE ISNULL(D.NAME,'') <> ISNULL(I.NAME,'')
END
GO
/*
DROP TABLE T_SMAPLE_TEST
DROP TABLE T_SAMPLE
DROP TRIGGER TRG_SAMPLE
*/
答案 0 :(得分:1)
问题是您无法使用NULL
检查=
值。 NULL
很特别,但仍然可以使用&#39; IS NULL&#39;来检查或者&#39;不是空的&#39;
请尝试以下代码:
ALTER TRIGGER [dbo].[TRG_SAMPLE]
ON [dbo].[T_SAMPLE]
AFTER UPDATE
AS
BEGIN
INSERT INTO T_SMAPLE_TEST
SELECT D.NAME + ',' + I.NAME
FROM INSERTED I
INNER JOIN DELETED D
ON I.ID = D.ID
WHERE I.NAME IS NOT NULL
END
GO
更新:我刚刚看到您对我评论的回复。这会诀窍吗?
ALTER TRIGGER [dbo].[TRG_SAMPLE]
ON [dbo].[T_SAMPLE]
AFTER UPDATE
AS
BEGIN
INSERT INTO T_SMAPLE_TEST
SELECT D.NAME + ',' + I.NAME
FROM INSERTED I
INNER JOIN DELETED D
ON I.ID = D.ID
WHERE I.NAME <> D.NAME AND D.NAME IS NOT NULL
END
GO
答案 1 :(得分:1)
那是因为NULL
被定义为未知值,这意味着它无法与之比较
以下所有语句都将产生一个空记录集:
SELECT 1 WHERE NULL = 1
SELECT 1 WHERE NULL <> 1
SELECT 1 WHERE NULL = NULL
SELECT 1 WHERE NULL <> NULL
虽然这些陈述将返回1:
SELECT 1 WHERE ISNULL(NULL, 1) = 1
SELECT 1 WHERE ISNULL(NULL, 0) <> 1
SELECT 1 WHERE ISNULL(NULL, 0) = ISNULL(NULL, 0)
SELECT 1 WHERE NULL IS NULL
顺便说一句,如果你将一个字符串与NULL连接起来,你将得到NULL,作为回报
因此,如果在更新之前名称为null或者更新为null,则SELECT D.NAME + ',' + I.NAME
将返回null
为避免这种情况,您可以使用此技术:
SELECT STUFF(
ISNULL(','+ D.NAME, '') +
ISNULL(',' + I.NAME, '')
, 1, 1, '')
只有当两个值都为null时才返回NULL,但如果它们中的任何一个不为null,它将仅返回它。 STUFF
用于删除第一个逗号,以防其中一个值为空。
如果您正在寻找避免使用ISNULL
的方法,您可以这样做:
WHERE
(
I.NAME <> D.NAME
OR I.NAME IS NULL
OR D.NAME IS NULL
)
AND NOT
(
I.NAME IS NULL
AND D.NAME IS NULL
)
这样,如果其中任何一个为null,则where子句将返回true,以及如果它们都不为null但它们具有不同的值。
当然,使用ISNULL
会提供更短,更易于维护的代码,但是您就是那个想要避免使用它的人......
答案 2 :(得分:1)
您不能使用与
的正常比较这些不会起作用:
if null <> value
if null = value
对null的测试必须是这样的:
if value is not null
if value is null
或者像这样
if isnull(value, '') = ''
if isnull(value, '') <> ''
在您的情况下替换
WHERE D.NAME <> I.NAME
用这个
WHERE isnull(D.NAME, '') <> isnull(I.NAME, '')
那应该解决你的问题
同时更改此
SELECT D.NAME + ',' + I.NAME
到这个
SELECT isnull(D.NAME, '') + ',' + isnull(I.NAME, '')
因为当其中一个名称为null时,整个连接字符串也将为null
答案 3 :(得分:1)
null比较的行为取决于ANSI_NULLS选项。 试试这段代码:
set ansi_nulls off
go
if 'jjj' <> null print 'it works with ansi_nulls off'
go
set ansi_nulls on
go
if 'jjj' <> null print 'it works ansi_nulls on'
所以你看,ansi_nulls的关闭行为与预期的一样,但它是遗留选项而且
在SQL Server的未来版本中,ANSI_NULLS将始终为ON和 任何明确将选项设置为OFF的应用程序都将生成 错误。
所以我认为由于某种原因,你可以使用此选项设置为关闭,但你的触发器是在ansi_nulls打开的情况下创建的,所以当你的其余代码可以正常工作时,当触发器触发它时使用ansi_nulls保存在创建时使用它