【问题标题】:T-SQL: How to deny update on one column of a table via trigger?T-SQL:如何通过触发器拒绝更新表的一列?
【发布时间】:2012-10-16 11:07:22
【问题描述】:

问题:
在我们的 SQL-Server 2005 数据库中,我们有一个表 T_Groups。
T_Groups 具有字段 ID (PK) 和名称。

现在我们公司的一些白痴将名称用作映射表中的键...
这意味着现在人们可能不会更改组名,因为如果这样做了,映射就消失了……
现在,在解决此问题之前,我需要向 T_Groups 添加限制,因此无法更新组的名称。
请注意,插入应该仍然是可能的,并且不更改组名的更新也应该是可能的。

还要注意,应用程序的用户和开发人员同时拥有 dbo 和 sysadmin 权限,因此 REVOKE/DENY 将不起作用。

如何使用触发器做到这一点?

【问题讨论】:

  • 不是问题的答案,但我认为更好的解决方案是在 T_Groups.Name 上创建唯一约束,然后在映射表中,您只需添加一个外键.
  • @hvd:没错,这也不见了。就像我说的,是个白痴。
  • 我也不是这个意思,我的意思是作为触发器的替代品。如果在映射表中使用了名称,则外键已经阻止了对 T_Groups.Name 的更新,或者(使用ON UPDATE CASCADE)确保映射表根据需要进行更新。或者这是否也允许您希望禁止的事情?
  • @hvd:嗯,理论上它是一种替代方案,但它不仅仅是一张桌子,所以它需要很长时间,我可能会监督一张桌子。此外,该名称不属于其中,我不想插入限制只是为了以后必须删除它们,所以...

标签: sql sql-server tsql sql-server-2005 sql-server-2008-r2


【解决方案1】:
CREATE TRIGGER tg_name_me
ON tbl_name
INSTEAD OF UPDATE
AS
IF EXISTS (
   SELECT *
   FROM INSERTED I
   JOIN DELETED D ON D.PK = I.PK AND ISNULL(D.name,I.name+'.') <> ISNULL(I.name,D.name+'.')
)
RAISERROR('Changes to the name in table tbl_name are NOT allowed', 16,1);
GO

根据您用于访问数据库的应用程序框架,检查更改的更便宜的方法是Alexander's answer。一些框架会生成包含所有列的 SQL 更新语句,即使它们没有改变,例如

UPDATE TBL
   SET name = 'abc', -- unchanged
       col2 = null, -- changed
       ... etc all columns

UPDATE() 函数仅检查该列是否在语句中存在,而不是其值是否已更改。此特定语句使用 UPDATE() 会引发错误,但如果使用更复杂的触发器进行测试,如上所示。

【讨论】:

  • 如何用旧值替换名称而不是产生错误?
【解决方案2】:
CREATE TRIGGER dbo.yournametrigger ON T_Groups
FOR UPDATE
AS
BEGIN
  IF UPDATE(name)
  BEGIN
    ROLLBACK
    RAISERROR('Changes column name not allowed', 16, 1);
  END
  ELSE
  BEGIN
  --possible update that doesn't change the groupname
  END
END

【讨论】:

  • 如何用旧值替换名称而不是产生错误?
【解决方案3】:

这是一个使用更新触发器保留一些原始值的示例。 它的工作原理是每次将 orig_author 和 orig_date 的值设置为已删除伪表中的值。它仍然执行工作并使用循环。

CREATE TRIGGER [dbo].[tru_my_table]  ON [dbo].[be_my_table]
AFTER UPDATE
AS
    UPDATE [dbo].[be_my_table]
    SET
    orig_author = deleted.orig_author
    orig_date   = deleted.orig_date,
    last_mod_by = SUSER_SNAME(),
    last_mod_dt = getdate()
    from deleted
    WHERE deleted.my_table_id IN  (SELECT DISTINCT my_table_id FROM Inserted)

ALTER TABLE [dbo].[be_my_table] ENABLE TRIGGER [tru_my_table]
GO

【讨论】:

    【解决方案4】:

    此示例将通过触发器锁定 SABENTIS_LOCATION.fk_sabentis_location 上的任何更新,并将输出一条详细消息,指示哪些对象受到影响

    ALTER TRIGGER dbo.SABENTIS_LOCATION_update_fk_sabentis_location ON SABENTIS_LOCATION
    FOR UPDATE
    AS
    DECLARE @affected nvarchar(max)
    SELECT @affected=STRING_AGG(convert(nvarchar(50), a.id), ', ')
      FROM inserted a
      JOIN deleted b ON a.id = b.id
      WHERE a.fk_sabentis_location != b.fk_sabentis_location
    IF @affected != ''
    BEGIN
      ROLLBACK TRAN
      DECLARE @message nvarchar(max) = CONCAT('Update values on column fk_sabentis_location locked by custom trigger. Could not update entities: ', @affected);
      RAISERROR(@message, 16, 1)
    END
    

    一些例子似乎在使用:

    IF UPDATE(name)
    

    但如果字段是更新语句的一部分,这似乎评估为 TRUE,即使值本身没有更改会导致误报。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-29
      • 2013-06-05
      • 1970-01-01
      • 2017-10-08
      • 1970-01-01
      • 1970-01-01
      • 2021-08-28
      相关资源
      最近更新 更多