SQL 服务器在指定列上触发

SQL Server Trigger on Specified Columns

嘿嘿,

我正在尝试在 SQL Server 2014 中更新 table 中的一些列,我在 trigger 上写了一些代码,然后将新值插入到新的 table,使用以下代码程序:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Atrin Noori
-- =============================================
ALTER TRIGGER [dbo].[UsersOnUPDATE] 
   ON  [dbo].[Users]
   After UPDATE
AS 
BEGIN
    SET NOCOUNT ON;
    --
    DECLARE @user_key int
    SELECT @user_key = i.User_key FROM inserted i;
    --  
    IF UPDATE (User_fullname) 
    BEGIN
        DECLARE @newfullname nvarchar(MAX);
        DECLARE @oldfullname nvarchar(MAX);
        --
        SELECT @oldfullname = i.User_fullname FROM deleted i;
        SELECT @newfullname = i.User_fullname FROM inserted i;
        --
        INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldname, UL_newname, UL_IsDeleted)
        VALUES (@user_key, GETDATE(), @oldfullname, @newfullname, 0)
    END
    --
    ELSE IF UPDATE (User_password) 
    BEGIN
        DECLARE @newpassword nvarchar(10);
        DECLARE @oldpassword nvarchar(10);      
        -- 
        SELECT @oldpassword = i.User_password FROM deleted i;
        SELECT @newpassword = i.User_password FROM inserted i;
        --
        INSERT INTO UsersLogs (UL_user_key_r,UL_date,UL_oldpass,UL_newpass,UL_IsDeleted)
        VALUES (@user_key, GETDATE(), @oldpassword, @newpassword, 0)
    END 
    --
    ELSE IF UPDATE (User_username) 
    BEGIN
        DECLARE @newusername nvarchar(10);
        DECLARE @oldusername nvarchar(10);
        --
        SELECT @oldusername = i.User_username FROM deleted i;
        SELECT @newusername = i.User_username FROM inserted i;
        --
        INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldusername, UL_newusername, UL_IsDeleted)
        VALUES (@user_key, GETDATE(), @oldusername, @newusername, 0)
    END
    --
    ELSE IF UPDATE (User_position_id_r) 
    BEGIN
        DECLARE @newposid tinyint;
        DECLARE @oldposid tinyint;
        --
        SELECT @oldposid = i.User_position_id_r FROM deleted i;
        SELECT @newposid = i.User_position_id_r FROM inserted i;
        --
        INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldPosid, UL_newPosid, UL_IsDeleted)
        VALUES (@user_key, GETDATE(), @oldposid, @newposid, 0)
    END
END

当我手动更改每列中的值并插入到名为 UsersLogs 的新 table 中时,此触发器工作正常。 但是,当我使用我正在开发的 c# 应用程序来更新值时,它无法正常工作...

考虑:

I am trying to change the password of a user through my application and using a Stored Procedure.

Stored Procedure 工作正常并更新值,但是 UserOnUpdate 的触发器插入 oldnew User_Fullnme 的值(不是 oldpassword 和 newpassword)到新的 table (UsersLgos) 并将其他值设置为 null(可以为 null)。

我的意思是只有这部分代码会 运行:

IF UPDATE (User_fullname) 
    BEGIN
        DECLARE @newfullname nvarchar(MAX);
        DECLARE @oldfullname nvarchar(MAX);
        --
        SELECT @oldfullname = i.User_fullname FROM deleted i;
        SELECT @newfullname = i.User_fullname FROM inserted i;
        --
        INSERT INTO UsersLogs (UL_user_key_r, UL_date, UL_oldname, UL_newname, UL_IsDeleted)
        VALUES (@user_key, GETDATE(), @oldfullname, @newfullname, 0)
    END

根据我刚才所说的...我更改了密码并设置了一个条件来检查更新的列。

但问题是为什么触发器无法通过应用程序识别更新了哪一列?

注意:

I say it again: IT WORKS FINE WITH THE MANUAL CHANGES AND UPDATES

update() 触发函数,returns 如果列被更新则为真,即使具有相同的值。 所以看起来当你更新密码时,你可能正在更新 User_fullname 列和其他列(当然与以前的值相同)。所以更新(User_fullname)returns 真。

但是,还有您编写代码的方式,触发器适用于只有一列被更新并且在您的代码中按该顺序更新的情况,例如,如果 UPDATE (User_fullname) 为真,您的代码不检查其他条件,因为 ELSE IF。你可能想删除 else 并检查每一列,或者完全改变你的策略以在触发器中记录数据。

然后根据您的评论,删除所有 if else 并为所有列设置一个单独的插入语句:

INSERT INTO UsersLogs (UL_user_key_r,UL_date,UL_oldpass,UL_newpass,<all columns>)
select (@user_key, GETDATE(), case when deleted.password <> new.password then deleted.password else null end , case when deleted.password <> new.password then inserted.password else null end , ....)
from inserted i
join deleted d on i.userkey = d.userkey

UPDATE() 函数仅告诉您某个列是否在 UPDATE 语句 存在 ,而不是如果值真的改变了。此外,inserteddeleted 可能包含多行,或者 none.

所以,你的触发器应该看起来像这样:

ALTER TRIGGER [dbo].[UsersOnUPDATE] 
   ON  [dbo].[Users]
   After UPDATE
AS 

SET NOCOUNT ON;

IF NOT (UPDATE(User_fullname) OR UPDATE(User_password) OR UPDATE(User_username) OR UPDATE(User_position_id_r))
    RETURN;  -- this only tells you if the column was present

INSERT INTO UsersLogs
    (UL_user_key_r, UL_date, UL_IsDeleted,
    UL_oldname, UL_newname,
    UL_oldpass, UL_newpass,
    UL_oldusername, UL_newusername,
    UL_oldPosid, UL_newPosid)
SELECT
    i.user_key, GETDATE(), 0,
    d.fullname, i.fullname,
    d.User_password, i.User_password,
    d.User_username, i.User_username,
    d.User_position_id_r, i.User_position_id_r
FROM inserted i
JOIN deleted d ON i.user_key = d.user_key
WHERE EXISTS (    -- this checks for any differences
    SELECT i.fullname, i.User_password, i.User_username, i.User_position_id_r
    EXCEPT        -- this will deal with nulls correctly
    SELECT d.fullname, d.User_password, d.User_username, d.User_position_id_r
);

如果只更新了一些列,这确实会让您看到其他列具有相同的前后值。但是一些 CASE 表达式应该解决这个问题。

ALTER TRIGGER [dbo].[UsersOnUPDATE] 
   ON  [dbo].[Users]
   After UPDATE
AS 

SET NOCOUNT ON;

IF NOT (UPDATE(User_fullname) OR UPDATE(User_password) OR UPDATE(User_username) OR UPDATE(User_position_id_r))
    RETURN;  -- this only tells you if the column was present

INSERT INTO UsersLogs
    (UL_user_key_r, UL_date, UL_IsDeleted,
    UL_oldname, UL_newname,
    UL_oldpass, UL_newpass,
    UL_oldusername, UL_newusername,
    UL_oldPosid, UL_newPosid)
SELECT
    i.user_key, GETDATE(), 0,
    CASE WHEN d.fullname <> i.fullname THEN d.fullname END, CASE WHEN d.fullname <> i.fullname THEN i.fullname END
    CASE WHEN d.User_password <> i.User_password THEN d.User_password END, CASE WHEN d.User_password <> i.User_password THEN i.User_password END,
    CASE WHEN d.User_username <> i.User_username THEN d.User_username END, CASE WHEN d.User_username <> i.User_username THEN i.User_username END,
    CASE WHEN d.User_position_id_r <> i.User_position_id_r THEN d.User_position_id_r END, CASE WHEN d.User_position_id_r <> i.User_position_id_r THEN i.User_position_id_r END
FROM inserted i
JOIN deleted d ON i.user_key = d.user_key
WHERE EXISTS (    -- this checks for any differences
    SELECT i.fullname, i.User_password, i.User_username, i.User_position_id_r
    EXCEPT        -- this will deal with nulls correctly
    SELECT d.fullname, d.User_password, d.User_username, d.User_position_id_r
);

如果您想为每个更改的值单独的行,您可以使用 CROSS APPLY

逆透视