账本表在存储过程中dml操作透明自动,无需手动处理哈希链或隐藏列;须用\_ledger视图查询、sys.sp\_verify\_database\_ledger验证完整性,append\_only表禁用update/delete。

存储过程里直接操作LEDGER表和普通表没区别
只要表是用 LEDGER = ON 创建的(无论是可更新还是仅追加),你在存储过程中对它执行 INSERT、UPDATE、DELETE,SQL Server 会自动维护账本历史、生成哈希链、填充 ledger_start_transaction_id 等隐藏列。你不需要在存储过程里手动调用任何 Ledger 相关函数或视图。
常见错误现象:有人试图在存储过程中显式插入 ledger_start_transaction_id 或 ledger_end_transaction_id 列,结果报错 Cannot insert explicit value for identity column in table 'xxx' when IDENTITY_INSERT is set to OFF —— 因为这些列是 GENERATED ALWAYS,禁止写入。
- 所有 DML 操作都按常规方式写,SQL Server 内部自动处理账本逻辑
- 存储过程权限需满足目标表的 DML 权限,外加
VIEW LEDGER CONTENT才能查_Ledger视图 - 如果存储过程里用了临时表或表变量,它们不能是账本表(账本不支持内存优化表)
需要验证账本完整性时,必须用系统存储过程
存储过程运行完 DML 后,若你想在同个事务里确认数据没被篡改(比如关键审计场景),不能靠 SELECT 对比哈希值 —— 必须调用 sys.sp_verify_database_ledger。这个存储过程是唯一能触发实时哈希重算与比对的入口。
注意:它不返回详细差异,只返回 0(通过)或 1(失败),且必须传入有效的 JSON 摘要。典型用法是先查 sys.database_ledger_latest_digest 获取最新摘要,再传给验证过程。
-
sys.sp_verify_database_ledger不支持事务内回滚后验证 —— 它验证的是已提交块 - 若传入
@table_name参数,只验证该表;不传则验证整个数据库所有账本表 - 摘要 JSON 必须包含至少一个
block_id和对应hash,格式错误直接报错
查询账本历史必须用 _Ledger 视图,别碰历史表
每个账本表都会自动生成一个名为 [schema].[table]_Ledger 的视图(如 dbo.EmployeeSalary_Ledger),这是唯一受支持的查询接口。直接查底层历史表(如 MSSQL_LedgerHistoryFor_EmployeeSalary)不仅绕过安全控制,还可能读到未验证的中间状态。
常见错误现象:在存储过程中写 SELECT * FROM MSSQL_LedgerHistoryFor_EmployeeSalary,结果发现列名和类型跟文档对不上,或者某些 GENERATED ALWAYS 列显示为 NULL —— 这是因为历史表结构由系统管理,不保证向后兼容。
-
_Ledger视图自动包含ledger_operation_type_desc(INSERT/UPDATE/DELETE)、ledger_transaction_id、commit_time等关键审计字段 - 它 JOIN 了
sys.database_ledger_transactions,所以你能直接拿到操作人principal_name - 不要在视图上加 NOLOCK —— 账本视图本身已做快照隔离,加提示反而可能破坏一致性
APPEND_ONLY 表在存储过程中要避开 UPDATE/DELETE
如果你的账本表创建时指定了 LEDGER = ON (APPEND_ONLY = ON)(比如日志类表),那在存储过程中执行 UPDATE 或 DELETE 会直接报错:Operation not allowed on append-only ledger table。这不是权限问题,是引擎级硬限制。
容易踩的坑:开发时用的是可更新账本表,上线前改成 APPEND_ONLY,但没同步修改所有调用它的存储过程,导致批量作业突然失败。
- APPEND_ONLY 表只允许
INSERT,且所有列(包括GENERATED ALWAYS时间戳)都不可显式赋值 - 如果业务逻辑真需要“逻辑删除”,得用额外状态列(如
is_active BIT),而不是删行 - 迁移存量数据到 APPEND_ONLY 表时,不能用
INSERT ... SELECT原表 —— 需用sys.sp_copy_data_in_batches存储过程
账本机制在存储过程中是透明的,但验证、查询、建模这三处必须严格遵循约束 —— 尤其是 _Ledger 视图的使用和 sys.sp_verify_database_ledger 的调用时机,稍有偏差就失去防篡改意义。











