如何避免覆盖数据库内容?附shiftreport.Records表结构示例
要避免shiftreport.Records表里的数据被意外覆盖,结合你给出的表结构,我整理了几个实用的方案,从数据库层面到业务逻辑层面都覆盖到了:
你的表结构(整理后)
CREATE TABLE [shiftreport].[Records] ( [Id] [bigint] IDENTITY(1,1) NOT NULL, [EntryText] [varchar](max) NULL, [Created] [datetime] NULL, [Updated] [datetime] NULL, CONSTRAINT [PK_Records] PRIMARY KEY CLUSTERED ([Id] ASC) ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] ALTER TABLE [shiftreport].[Records] ADD CONSTRAINT [DF__Records__Created__2DB1C7EE] DEFAULT (getutcdate()) FOR [Created] -- 补全Updated字段的默认约束(通常的做法) ALTER TABLE [shiftreport].[Records] ADD CONSTRAINT [DF__Records__Updated__2EA5EC27] DEFAULT (getutcdate()) FOR [Updated]
1. 乐观锁机制(推荐用于需要更新但要避免并发覆盖的场景)
这是防止数据覆盖最常用的手段,通过添加版本号字段,确保只有当记录的当前版本和你读取时的版本一致时,才允许执行更新操作。
操作步骤:
- 先给表添加版本号字段:
ALTER TABLE [shiftreport].[Records] ADD [Version] INT NOT NULL DEFAULT 1;
- 更新数据时带上版本号校验:
UPDATE [shiftreport].[Records] SET [EntryText] = '新的班次报告内容', [Updated] = GETUTCDATE(), [Version] = [Version] + 1 WHERE [Id] = 123 -- 目标记录的ID AND [Version] = 2; -- 你读取记录时获取到的版本号
如果在你读取记录后,有其他操作已经更新过这条记录,Version值会不匹配,更新语句就不会执行,完美避免了覆盖问题。
2. 限制数据库权限(适合不需要更新旧记录的场景)
如果你的业务逻辑只需要新增班次报告,不需要修改已有的历史记录,可以直接从权限层面入手,禁止操作账号执行UPDATE和DELETE:
-- 假设你的应用操作账号是ShiftReportAppUser GRANT SELECT, INSERT ON [shiftreport].[Records] TO ShiftReportAppUser; -- 拒绝更新和删除权限 DENY UPDATE, DELETE ON [shiftreport].[Records] TO ShiftReportAppUser;
这样就算代码里不小心写了更新语句,也会因为权限不足执行失败,从根源上杜绝覆盖。
3. 业务逻辑优化:优先新增而非更新
班次报告这类数据,通常更适合保留完整历史,而不是修改旧记录。比如需要修改报告时,可以新增一条新记录替代旧记录,或者标记旧记录为无效:
操作示例:
- 先添加一个标记字段:
ALTER TABLE [shiftreport].[Records] ADD [IsActive] BIT NOT NULL DEFAULT 1;
- 修改报告时,先标记旧记录为无效,再新增新记录:
-- 标记旧记录失效 UPDATE [shiftreport].[Records] SET [IsActive] = 0, [Updated] = GETUTCDATE() WHERE [Id] = 123; -- 新增修正后的报告 INSERT INTO [shiftreport].[Records] ([EntryText]) VALUES ('修正后的班次报告内容');
这种方式既能保留历史数据,又完全避免了覆盖旧内容的风险。
4. 触发器阻止非法更新(适合强制规则的场景)
如果需要严格控制哪些字段可以更新,或者阻止特定条件下的修改,可以创建数据库触发器:
比如,禁止修改Created字段,并且只允许更新EntryText和Updated字段:
CREATE TRIGGER [trg_PreventUnwantedUpdates] ON [shiftreport].[Records] INSTEAD OF UPDATE AS BEGIN SET NOCOUNT ON; -- 检查是否修改了Created字段,是则抛出错误 IF UPDATE([Created]) BEGIN RAISERROR('禁止修改Created字段', 16, 1); RETURN; END -- 只允许更新EntryText和Updated字段 UPDATE r SET r.[EntryText] = i.[EntryText], r.[Updated] = GETUTCDATE() FROM [shiftreport].[Records] r INNER JOIN inserted i ON r.[Id] = i.[Id]; END
这样任何试图修改Created或其他非允许字段的操作都会被阻止,只有合法的更新才能执行。
内容的提问来源于stack exchange,提问作者Philipp
相关产品推荐
相关产品推荐

