I have a Table Products with a Trigger to create a record on each update made to Products (something like a Log).
Everything works fine if I update one record at a time but if I try to update multiple records (Update Products SET Name = "BLABLABLA" WHERE Price > 10) I get the following error:
"Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <=, >, >= or when the subquery is used as an expression. The statement has been terminated."
My Trigger is this one:
CREATE TRIGGER [dbo].[NS_Art_Export_ProdUpdate] ON [dbo].[Produtos] FOR UPDATE
AS BEGIN
DECLARE @Key1 NVARCHAR(20), @Key2 NVARCHAR(20)
DECLARE @Changed BIT
SET @Key1 = (SELECT Counter FROM INSERTED)
SET @Key2 = (SELECT Prod_Gen FROM INSERTED)
SET @Changed = 0
IF UPDATE(Codigo) SET @Changed = 1
IF UPDATE(NOME) SET @Changed = 1
IF UPDATE(Familia) SET @Changed = 1
IF UPDATE(CodIVA) SET @Changed = 1
IF UPDATE(qtEmbal) SET @Changed = 1
IF UPDATE(Volume) SET @Changed = 1
IF UPDATE(PesoBrt) SET @Changed = 1
IF UPDATE(PesoLiq) SET @Changed = 1
IF UPDATE(Status) SET @Changed = 1
IF @Changed = 1
INSERT INTO Log_NSArtSoft
(Tabela, Tipo, Key1, Key2, Data_Update,
Data_Export, Flag_Update, Data_Export2)
VALUES
('Produtos', 'U', @Key1, @Key2, GETDATE(),
0, 0, 0)
END
GO
Can someone help me with this?
Thanks
Best Answer
The issue is that in SQL Server, Triggers fire once per operation, not once per row. So when you do multi-row operations, the subquery in
SET @Key1 = (SELECT Counter FROM INSERTED)
is returning aCounter
value per each row updated. You need to restructure theINSERT
as being a set-based operation as follows:Please keep in mind that the
UPDATE
function only indicates whether or not the column is in theSET
clause of theUPDATE
statement (in terms of anAFTER UPDATE
Trigger; theUPDATE
function returns true for all columns in the context of anINSERT
operation). It does not indicate whether or not the value of that column has changed. Meaning, for the query ofUPDATE table SET ColA = ColA
, callingUPDATE([ColA])
will return true even though the value is guaranteed to be the same.Assuming that you do not want to log rows in which no values for the specified columns have changed, you need to JOIN the
inserted
anddeleted
tables and test each column between those two pseudo-tables. When testing the columns, you need to wrap NULLable columns inISNULL()
, and when the column is a string type (notXML
) then you need to use a binary Collation to make sure that all changes get logged (even if they equate when linguistic rules are applied). Hence, theSELECT
statement would change as follows (I have no idea what the datatype of any of the columns are, so the particular usage below for each one is purely for example):P.S. It is also a good idea to include
SET NOCOUNT ON;
at the beginning of Triggers and Stored Procedures (unless a client application specifically needs that output, which is usually not the case).