INNER JOIN on inserted trigger table includes all table rows
10:26 04 Jun 2025

My objective is to create a trigger which fires after an UPDATE, and which set a ModifiedTimeStamp field to the current date for all and only effectively updated / inserted rows.

I had a previous version with a cursor, which "works" despite being deeply ugly:

ALTER TRIGGER [dbo].[TG_INS_UPD_Customer_ModifiedDateTime]
   ON  [dbo].[Customer]
   AFTER INSERT,UPDATE
AS 
BEGIN
SET NOCOUNT ON;

DECLARE @Id INT 

DECLARE BrandModifiedDateTimeCursor CURSOR LOCAL FOR 
    SELECT
        BrandId
    FROM
        inserted

OPEN BrandModifiedDateTimeCursor

FETCH NEXT FROM BrandModifiedDateTimeCursor   
    INTO @Id

WHILE @@FETCH_STATUS = 0  
BEGIN

    UPDATE [dbo].[Brand] 
    SET  ModifiedTimeStamp = GETDATE() 
    WHERE BrandId = @Id

    FETCH NEXT FROM BrandModifiedDateTimeCursor   
        INTO @Id
END

CLOSE BrandModifiedDateTimeCursor
DEALLOCATE BrandModifiedDateTimeCursor

END

I wanted to migrate to a new version which only uses an UPDATE, clearer, shorter and way more efficient, which looked like this:

ALTER TRIGGER [dbo].[TG_INS_UPD_Customer_ModifiedDateTime]
   ON  [dbo].[Customer]
   AFTER INSERT,UPDATE
AS 
BEGIN
    UPDATE b
    SET ModifiedTimeStamp = GETDATE()
    FROM [dbo].[Brand] b
    INNER JOIN inserted i ON i.BrandId = b.BrandId
END

But for some reason I do not understand, that version fires for ALL the rows in the table instead of just updated / inserted ones, yet they are supposed to do the same thing.

Why, and what is the correct way to proceed?

Here is the MERGE query

ALTER PROCEDURE [dbo].[spSTASyncBrand]
    -- Add the parameters for the stored procedure here
    @LastModificationDate DATETIME
AS
BEGIN
    SELECT
    *
INTO
    #BrandsToMerge
FROM 
    [185.153.244.42\BC].[BEETLE_SYNC_PROD].[dbo].[vNavBrand]

MERGE dbo.Brand AS brd 
USING (SELECT * FROM #BrandsToMerge) AS src
ON (src.Code = brd.Code)  
WHEN MATCHED AND 
    (brd.[Name] <> src.[Name]
    OR ISNULL(brd.Phone,'') <> src.Phone
    OR ISNULL(brd.Fax,'') <> src.Fax
    OR ISNULL(brd.ContactEmail,'') <> src.ContactEmail
    OR ISNULL(brd.AccountingEmail,'') <> src.AccountingEmail
    OR ISNULL(brd.Address1,'') <> src.Address1
    OR ISNULL(brd.Address2,'') <> src.Address2
    OR ISNULL(brd.ZipCode,'') <> src.ZipCode
    OR ISNULL(brd.City,'') <> src.City
    OR ISNULL(brd.Siret,'') <> src.Siret
    OR ISNULL(brd.Ape,'') <> src.Ape
    OR ISNULL(brd.TVAIntra,'') <> src.TVAIntra
    OR ISNULL(brd.TVAIntra,'') <> src.TVAIntra
    OR ISNULL(brd.BankInfos,'') <> src.BankInfos
    OR ISNULL(brd.Capital,'') <> src.Capital
    OR ISNULL(brd.CompanyInfos1,'') <> src.CompanyInfos1
    OR ISNULL(brd.CompanyInfos2,'') <> src.CompanyInfos2
    OR ISNULL(brd.CompanyInfos3,'') <> src.CompanyInfos3
    OR ISNULL(brd.CompanyInfos4,'') <> src.CompanyInfos4
    OR ISNULL(brd.CompanyInfos5,'') <> src.CompanyInfos5
    OR ISNULL(brd.CompanyInfos6,'') <> src.CompanyInfos6
    OR ISNULL(brd.CompanyInfos7,'') <> src.CompanyInfos7
    OR brd.IsDeleted = 1) THEN
UPDATE 
SET 
    [Name] = src.[Name],
    Phone = src.Phone,
    Fax = src.Fax,
    ContactEmail = src.ContactEmail,
    AccountingEmail = src.AccountingEmail,
    Address1 = src.Address1,
    Address2 = src.Address2,
    ZipCode = src.ZipCode,
    City = src.City,
    Siret = src.Siret,
    Ape = src.Ape,
    TVAIntra = src.TVAIntra,
    Capital = src.Capital,
    BankInfos = src.BankInfos,
    CompanyInfos1 = src.CompanyInfos1,
    CompanyInfos2 = src.CompanyInfos2,
    CompanyInfos3 = src.CompanyInfos3,
    CompanyInfos4 = src.CompanyInfos4,
    CompanyInfos5 = src.CompanyInfos5,
    CompanyInfos6 = src.CompanyInfos6,
    CompanyInfos7 = src.CompanyInfos7,
    IsDeleted = 0

WHEN NOT MATCHED BY TARGET THEN 
INSERT 
    ([Code]
    ,[Name]
    ,[Phone]
    ,[Fax]
    ,[ContactEmail]
    ,[AccountingEmail]
    ,[Address1]
    ,[Address2]
    ,[ZipCode]
    ,[City]
    ,[Siret]
    ,[Ape]
    ,[TvaIntra]
    ,[BankInfos]
    ,[Capital]
    ,[CompanyInfos1]
    ,[CompanyInfos2]
    ,[CompanyInfos3]
    ,[CompanyInfos4]
    ,[CompanyInfos5]
    ,[CompanyInfos6]
    ,[CompanyInfos7]
    ,[IsDaughterCompany]
    )
VALUES 
    (src.Code,
    src.[Name],
    src.[Phone],
    src.[Fax],
    src.[ContactEmail],
    src.[AccountingEmail],
    src.[Address1],
    src.[Address2],
    src.[ZipCode],
    src.[City],
    src.[Siret],
    src.[Ape],
    src.[TvaIntra],
    src.[BankInfos],
    src.[Capital],
    src.[CompanyInfos1],
    src.[CompanyInfos2],
    src.[CompanyInfos3],
    src.[CompanyInfos4],
    src.[CompanyInfos5],
    src.[CompanyInfos6],
    src.[CompanyInfos7],
    0
    )

WHEN NOT MATCHED BY SOURCE THEN
UPDATE
SET
    IsDeleted=1;
END

It just blindly checks all fields for a difference. I was also wondering if there was not an issue here.

sql sql-server triggers