2016-11-23 44 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

相關問題