触发器:无法在条件内引用插入的 table 列值
Trigger: Inserted table column value cannot be referenced inside condition
我有以下触发器:
GO
ALTER TRIGGER [dbo].[FooTrigger]
ON [dbo].[Foo]
FOR INSERT
AS
DECLARE @Url nvarchar(MAX)
SELECT @Url = ins.Url from INSERTED ins;
IF EXISTS (SELECT *
FROM [dbo].[Foo] p
where p.Url = @Url)
BEGIN
RAISERROR ( 'error %s' , 16, 1, @Url)
ROLLBACK TRANSACTION;
RETURN
END;
PRINT 'Executed Trigger Insert.'
GO
如果我像
这样硬编码条件,它可以正常工作
IF EXISTS(SELECT * FROM [dbo].[Foo] foo WHERE foo.Url = 'test.com')
但是原来那个不行。它抛出消息:
在条件中使用 @Url 引用插入的 table 数据似乎有错误。但是 RAISERROR ( @Url , 16, 1);
正确地打印了值,所以我认为在这种情况下,引用是不被允许的。有什么解决方法吗?
首先,如果您要确保 url
是唯一的,则使用唯一约束:
alter table foo add constraint unq_foo_url unique (url);
其次,永远不要假设 inserted
只有一行。此代码:
SELECT @Url = ins.Url from INSERTED ins;
是一个等待发生的错误。
编辑:
如果您的 table 已经违反了唯一约束,那么您的问题就很麻烦了。你可以解决这个问题。您的代码的问题是 FOR INSERT
触发器实际上是 AFTER INSERT
触发器——新数据已经可见。
但是,你可以处理这个。这是一种方法:
CREATE TRIGGER [dbo].[FooInsertTrigger]
ON [dbo].[Foo]
AFTER INSERT
AS
BEGIN
IF (SELECT TOP (1) COUNT(*)
FROM inserted i JOIN
[dbo].[Foo] f
on i.url = f.url
GROUP BY i.url
ORDER BY COUNT(*) DESC
) > 1
BEGIN
RAISERROR('Duplicate URL found' , 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
PRINT 'Executed Trigger Insert.'
END;
以下情况下最大值count(*)
大于1:
inserted.url
匹配 foo
中的 1 行。这是正常情况。
inserted.url
匹配 foo
中的多行。这是抓到的。
这也将捕获 inserted
中存在倍数的情况。
通过使代码对于多次插入是安全的,更难提取有问题的 URL。但是错误处理正确。
Here 是一个 db<>fiddle.
我有以下触发器:
GO
ALTER TRIGGER [dbo].[FooTrigger]
ON [dbo].[Foo]
FOR INSERT
AS
DECLARE @Url nvarchar(MAX)
SELECT @Url = ins.Url from INSERTED ins;
IF EXISTS (SELECT *
FROM [dbo].[Foo] p
where p.Url = @Url)
BEGIN
RAISERROR ( 'error %s' , 16, 1, @Url)
ROLLBACK TRANSACTION;
RETURN
END;
PRINT 'Executed Trigger Insert.'
GO
如果我像
这样硬编码条件,它可以正常工作IF EXISTS(SELECT * FROM [dbo].[Foo] foo WHERE foo.Url = 'test.com')
但是原来那个不行。它抛出消息:
在条件中使用 @Url 引用插入的 table 数据似乎有错误。但是 RAISERROR ( @Url , 16, 1);
正确地打印了值,所以我认为在这种情况下,引用是不被允许的。有什么解决方法吗?
首先,如果您要确保 url
是唯一的,则使用唯一约束:
alter table foo add constraint unq_foo_url unique (url);
其次,永远不要假设 inserted
只有一行。此代码:
SELECT @Url = ins.Url from INSERTED ins;
是一个等待发生的错误。
编辑:
如果您的 table 已经违反了唯一约束,那么您的问题就很麻烦了。你可以解决这个问题。您的代码的问题是 FOR INSERT
触发器实际上是 AFTER INSERT
触发器——新数据已经可见。
但是,你可以处理这个。这是一种方法:
CREATE TRIGGER [dbo].[FooInsertTrigger]
ON [dbo].[Foo]
AFTER INSERT
AS
BEGIN
IF (SELECT TOP (1) COUNT(*)
FROM inserted i JOIN
[dbo].[Foo] f
on i.url = f.url
GROUP BY i.url
ORDER BY COUNT(*) DESC
) > 1
BEGIN
RAISERROR('Duplicate URL found' , 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
PRINT 'Executed Trigger Insert.'
END;
以下情况下最大值count(*)
大于1:
inserted.url
匹配foo
中的 1 行。这是正常情况。inserted.url
匹配foo
中的多行。这是抓到的。
这也将捕获 inserted
中存在倍数的情况。
通过使代码对于多次插入是安全的,更难提取有问题的 URL。但是错误处理正确。
Here 是一个 db<>fiddle.