2016-11-23 129 views
1

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

+2

删除谁......你?另一位用户?这可能是权限问题和/或SQL注入问题,具体取决于您查看的方式。 – scsimon

回答

1

按照评论编辑。看起来你实际上是在寻找版本控制,它不应该通过触发器来完成,但它可以带来性能影响和更多的编码。

最合适的方法来打击您的疑虑。每隔X分钟维护一次事务备份,这样您可以在回滚时回滚

但是,sql-server确实有2个内置的变更追踪方法可供您探索。

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

对于您的情况特别令人担忧变化数据捕捉可能是可行的和更多的方式易于维护比触发器。我没有自己尝试过,因为更改跟踪足以满足我的需求,但是这里有一个链接到微软的文档https://msdn.microsoft.com/en-us/library/cc645937(v=sql.110).aspx

如果你坚持触发器在这里是一个例子,你将不得不维护原始主键的参考诚信或者你可能不知道哪个更改导致此问题

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 

或特定用户相同的命令只是更改角色名,以用户名这将是最

DENY UPDATE ON SchemaName.TableName TO UserName 

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

您也可以拒绝执行,修改,和一帮更这里是微软的文档:https://msdn.microsoft.com/en-us/library/ms173724.aspx

使用触发器,而不是安全权限是很多混乱,如果有人有足够的权限,要做出改变,他们仍然它可能会减缓他们的速度,但不会太大。所以如果你担心这种能力,确保你有很好的备份。

+0

我不会太担心数据库管理员进行更改。我更担心的是代码会有bug,或者有人会编写一些新代码并实现一些能够修改记录并导致混乱的东西。触发似乎是完美的,但有没有办法获得价值。例如,我希望允许在创建新版本时禁用1位列。这会保留旧版本的记录,但会将其禁用 –

+0

@DanHastings,这将成为每x分钟进行一次事务备份的原因。版本控制可以在数据库级别完成,但会对性能产生影响,并且还会为知道如何根据渐变维度规则编写代码的开发人员带来复杂性。如果需要版本控制,那么在应用程序级别处理它可能更适合于某些时候,DBA和开发人员需要相互信任:)我将向您展示一种使用SQL执行此操作的方法,尽管 – Matt