session_context 是 sql server 2016+ 提供的会话级键值存储,类似内存字典,需显式读写、键名大小写敏感、值为 sql_variant,连接断开自动清空,不跨连接共享,易因连接池复用导致上下文污染。

SESSION_CONTEXT 是什么,为什么不能直接当参数用
SESSION_CONTEXT 不是传统意义上的参数传递机制,而是一个会话级键值存储,类似内存里的临时字典。它不自动跨存储过程调用链传递——EXEC 一个新存储过程时,SESSION_CONTEXT 的内容仍然存在,但你必须显式读取,且得确保写入和读取用的是完全相同的键名(区分大小写)。
常见错误是:在 A 存储过程中 sp_set_session_context 写了 'UserId',但在 B 存储过程中却尝试读 'userid' 或 'user_id',结果返回 NULL。
- 键名严格区分大小写,建议统一用 PascalCase 或全大写(如
'USER_ID') - 值类型固定为
sql_variant,不能存表、游标等复杂类型 - 会话断开后所有键值自动清空,无法用于跨连接共享
怎么安全地写入和读取 SESSION_CONTEXT
写入必须用 sp_set_session_context,不能直接 INSERT;读取推荐用 SESSION_CONTEXT 函数配合 CAST 显式转换类型,避免隐式转换失败或截断。
示例:在登录后或主流程开头设置用户 ID
EXEC sp_set_session_context @key = N'UserId', @value = 12345, @read_only = 0;
在下游存储过程中读取:
批量分析录音转写,输出多维度拓客报告。触发词:录音分析、总结、音频总结、拜访记录总结。当用户提及「分析录音」「看看录音数据」「最近的录音」「通话记录」且意图为批量统计/分析时触发。仅出现「录音」或「拜访」时需结合上下文,若仅查看单条详情则不触发。
DECLARE @uid INT = CAST(SESSION_CONTEXT(N'UserId') AS INT);
-
@read_only = 1后不能再修改该键,适合设置后不可变的上下文(如租户 ID),但一旦设错就只能断开连接重来 - 读取前最好加
ISNULL或判断是否为NULL,尤其在调试阶段:IF SESSION_CONTEXT(N'UserId') IS NULL RAISERROR('Missing UserId in session context', 16, 1); - 不要在触发器里依赖
SESSION_CONTEXT做权限校验——触发器可能由系统进程或代理作业触发,上下文为空
多个存储过程之间怎么协同使用 SESSION_CONTEXT
没有自动“继承”机制,每个存储过程都得自己读。关键在于约定好键名、生命周期和清理时机。
典型协作模式:
- 入口存储过程负责写入(如
'CurrentOrgId'、'AuditUser') - 所有被调用的子过程统一从相同键读,不传参也不改写(除非业务明确需要更新上下文)
- 退出前可选清理:
EXEC sp_set_session_context @key = N'CurrentOrgId', @value = NULL;,避免残留影响后续操作 - 若嵌套调用很深,建议封装读取逻辑为标量函数(如
dbo.GetSessionInt(N'UserId')),减少重复CAST和NULL判断
注意:如果子过程执行完又调用另一个子过程,只要会话没断,SESSION_CONTEXT 依然有效——它绑定的是连接,不是调用栈。
SESSION_CONTEXT 在实际场景中容易被忽略的坑
最常被忽视的是连接池复用导致的上下文污染。比如 Web 应用用连接池,一个连接被回收后下次分配给别的请求,但 SESSION_CONTEXT 可能还带着上一个用户的 'UserId'。
- 必须在每次业务开始前重置或覆盖关键键,不能假设它是空的
- SQL Server 2016+ 才支持
SESSION_CONTEXT,旧版本要用CONTEXT_INFO(但只有 128 字节,且不支持键值对) - 动态 SQL 中引用
SESSION_CONTEXT要小心:字符串拼接时别漏掉N''前缀,否则 Unicode 键名匹配失败 - 不能用在内联表值函数(ITVF)中——函数内不允许调用
SESSION_CONTEXT,会报错Invalid use of a side-effecting operator
真正麻烦的不是怎么用,而是谁负责清、什么时候清、清不干净会怎样——这些细节往往只在压测或并发高时才暴露。










