SQL Server阻止更新或删除所有记录

时间:2016-11-23 16:33:21

标签: sql sql-server locking

我正在运行SQL Server 2008.我编写的代码应该非常安全,以防止任何记录被删除或更新,但如果我可以在数据库级别执行此操作会更高兴。是否可以标记一个表,以便一旦插入一行就永远不能修改或删除它?

1 个答案:

答案 0 :(得分:1)

根据评论进行修改。看起来你实际上正在寻找真正不应该通过触发器进行的版本控制,但它可能会对性能产生影响并且可以进行更多编码。

最合适的方法可以解决您的问题。每隔X#分钟维护事务备份,以便在崩溃时可以回滚

然而,sql-server确实有2个可以探索的更改跟踪方法。

  • 更改跟踪 - 简单地识别记录是否已被修改,并且在将更改同步到数据库时非常有用。基本上,它为每个操作的每一行增加一个BIGINT,因此您只需要检查大于上一个同步数的记录。
  • 更改数据捕获 - 这将捕获插入/更新/删除以及记录(行)的状态。

对于您的特别担心更改数据捕获可能比触发器更易于维护和维护。我自己没有尝试过,因为Change Tracking足以满足我的需求,但这里有一个指向Microsoft文档的链接https://msdn.microsoft.com/en-us/library/cc645937(v=sql.110).aspx

如果你坚持使用Triggers就是一个例子,你必须保持原始主键的参照完整性,否则你可能不知道哪个更改导致了问题

CREATE TABLE TblName (
    PrimaryKeyID INT IDENTITY(1,1) NOT NULL PRIMARY KEY
    ,Col1 INT NULL
    ,Col2 INT NULL
    ,OriginalPrimaryKeyId INT NULL
    ,CreateDate DATETIME DEFAULT(GETDATE())
    ,UpdateDate DATETIME DEFAULT(GETDATE())
    ,IsLatestVersion BIT NOT NULL DEFAULT(1)
)
GO

CREATE TRIGGER dbo.TrigForInsertEnforceVersionColTblName ON dbo.TblName
FOR INSERT
AS
BEGIN
BEGIN TRY
    IF (SELECT COUNT(*) FROM TblName WHERE IsLatestVersion <> 1 AND OriginalPrimaryKeyId IS NULL) > 0
    BEGIN
       ;THROW 51000, 'Attempted to insert a record identified as Previous Version without referencing another record', 1
    END

END TRY
BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION
    --this will mean the loss of some Primary Keys but it is better than an
    --INSTEAD of INSERT because you wont have to handle the insert code

    ;THROW
END CATCH

END
GO

CREATE TRIGGER dbo.TriggerName ON dbo.TblName
INSTEAD OF UPDATE, DELETE

AS 
BEGIN

BEGIN TRY

    IF EXISTS (
       SELECT *
       FROM
          TblName t
          INNER JOIN inserted i
          ON t.PrimaryKeyID = i.PrimaryKeyID
          AND (t.OriginalPrimaryKeyId <> i.OriginalPrimaryKeyId
             OR t.IsLatestVersion <> i.IsLatestVersion)
    )
    BEGIN
       ;THROW 51000, 'OriginalPrimaryKeyId Column or IsLatestVersion Column was attempted to be updated', 1
    END

    --don't have to test count can just run the update statement
    IF ((SELECT COUNT(*) FROM inserted) > 0)
    BEGIN
       --It's an UPDATE Operations so insert new row but maintain original primary key
       --so you know what the new row is a version of
       INSERT INTO dbo.TblName (Col1, Col2, OriginalPrimaryKeyId)
       SELECT
          i.Col1
          ,i.Col2
          ,OriginalPrimaryKeyId = CASE
                WHEN t.OriginalPrimaryKeyId IS NULL THEN t.PrimaryKeyID
                ELSE t.OriginalPrimaryKeyId
          END
       FROM
          inserted i
          INNER JOIN TblName t
          ON i.PrimaryKeyID = t.PrimaryKeyID
    END

    UPDATE t
    SET IsLatestVersion = 0
        ,UpdateDate = GETDATE()
    FROM
       TblName t
       INNER JOIN deleted d
       ON t.PrimaryKeyID = d.PrimaryKeyID

END TRY
BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION

    ;THROW
END CATCH

END

权限讨论:

对于任何行级别并阻止包括数据库所有者角色或管理员在内的所有人,您必须创建一个触发器。但是这些角色也总是可以删除触发器并修改表格。也许简单的权限就足够了,比如

关于整个角色,如果您将用户置于角色中,这将是最好的

GRANT INSERT ON SchemaName.TableName TO RoleName
DENY UPDATE ON SchemaName.TableName TO RoleName
DENY DELETE ON SchemaName.TableName TO RoleName

对于特定用户,相同的命令只将RoleName更改为用户名

DENY UPDATE ON SchemaName.TableName TO UserName

这将授予插入但撤销更新或删除记录的能力。

您还可以拒绝执行,更改,以及更多内容,这是Microsoft的文档:https://msdn.microsoft.com/en-us/library/ms173724.aspx

使用触发器而不是安全权限是非常麻烦的,如果有足够权限的人想要进行更改,他们仍然可以放慢速度,但不会太多。因此,如果您担心这种能力,请确保您有良好的备份。