我正在运行SQL Server 2008.我编写的代码应该非常安全,以防止任何记录被删除或更新,但如果我可以在数据库级别执行此操作会更高兴。是否可以标记一个表,以便一旦插入一行就永远不能修改或删除它?
答案 0 :(得分:1)
根据评论进行修改。看起来你实际上正在寻找真正不应该通过触发器进行的版本控制,但它可能会对性能产生影响并且可以进行更多编码。
最合适的方法可以解决您的问题。每隔X#分钟维护事务备份,以便在崩溃时可以回滚。
然而,sql-server确实有2个可以探索的更改跟踪方法。
对于您的特别担心更改数据捕获可能比触发器更易于维护和维护。我自己没有尝试过,因为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
使用触发器而不是安全权限是非常麻烦的,如果有足够权限的人想要进行更改,他们仍然可以放慢速度,但不会太多。因此,如果您担心这种能力,请确保您有良好的备份。