直接在存储过程中增删改账本表会失败,必须用 with ledger = on 创建存储过程,且dml需为显式语句、禁止混合事务;可更新账本表的update/delete产生历史记录,但须调用sys.sp_verify_database_ledger验证完整性,否则不保证哈希链有效。

直接在存储过程中增删改账本表会失败
SQL Server 2022 的 LEDGER 表(无论是仅追加还是可更新)不支持在常规存储过程中执行 INSERT、UPDATE 或 DELETE 操作,除非该存储过程被显式标记为“账本就绪”。否则你会收到错误:The operation is not allowed on a ledger table.。这是因为账本表的写入路径受系统保护,必须经过 Ledger runtime 校验链,而普通存储过程默认绕过该校验。
必须用 WITH LEDGER = ON 创建存储过程
要在存储过程中操作账本表,创建时必须显式指定 WITH LEDGER = ON。这是硬性要求,不是可选配置:
- 该子句只能用于
CREATE PROCEDURE,不能用于ALTER PROCEDURE - 过程内所有对账本表的 DML 必须是显式语句(不能通过动态 SQL 绕过,
EXEC(@sql)会失败) - 过程不能包含对非账本表和账本表的混合事务写入(例如同时
UPDATE普通表 +INSERT账本表),否则验证阶段可能中断
示例:
CREATE PROCEDURE dbo.usp_insert_employee_ledger
@SSN char(11),
@FirstName nvarchar(50),
@LastName nvarchar(50),
@Salary money
WITH LEDGER = ON
AS
BEGIN
INSERT INTO dbo.Employees_LedgerTable (SSN, FirstName, LastName, Salary)
VALUES (@SSN, @FirstName, @LastName, @Salary);
END;
不可更新账本表的 UPDATE/DELETE 需额外注意
如果你用的是 LEDGER = ON + SYSTEM_VERSIONING = ON 的可更新账本表,UPDATE 和 DELETE 在存储过程中是允许的,但有隐含行为:
-
UPDATE不会修改原行,而是插入新版本 + 自动在历史表中存旧值(与普通时态表一致) -
DELETE实际是逻辑删除:行被标记为已删除,并写入历史表;原始表中仍保留带ledger_start_transaction_id和ledger_end_transaction_id的影子记录 - 这些操作产生的账本块摘要,会由系统自动写入
sys.database_ledger,但不会立即触发全量验证——验证需手动调用sys.sp_verify_database_ledger
调用后必须验证才能确认账本完整性
即使存储过程成功执行,也不能认为账本状态可靠。因为账本验证是异步且按块进行的,DML 完成后只生成待验证数据,不保证哈希链完整。你必须主动验证:
- 验证单个表:
EXEC sys.sp_verify_database_ledger @table_name = 'Employees_LedgerTable'; - 验证全部账本表:
EXEC sys.sp_verify_database_ledger; - 若返回值非 0,或结果集中
last_verified_block_id明显滞后于当前事务 ID,说明存在未覆盖的区块,需排查事务是否被回滚、日志截断或摘要上传失败
最容易被忽略的一点:验证本身不修复问题,只报错。一旦发现哈希不匹配,唯一办法是还原到上一个已验证快照,或从 Azure Blob 中恢复可信摘要——账本防篡改的代价就是不可逆。











