session_context 是 sql server 2016+ 提供的会话级键值对存储机制,支持 sysname 键和 sql_variant 值(如 int、datetime、nvarchar(128)、uniqueidentifier),不支持表、xml、varchar(max) 等大对象;需用 sp_set_session_context 写入、session_context() 读取并显式转换类型。

SESSION_CONTEXT 是什么,它能存哪些值?
SESSION_CONTEXT 不是全局变量,也不是会话级 SET 变量,而是 SQL Server 2016+ 提供的一个键值对存储机制,专用于在单个连接生命周期内跨批、跨存储过程、跨触发器传递轻量上下文信息。它只支持 sysname(即 NVARCHAR(128))类型的键和 sql_variant 类型的值——这意味着你**不能直接存表、游标、XML 实例或大对象(如 VARCHAR(MAX))**,但可以存 INT、DATETIME、NVARCHAR(128)、UNIQUEIDENTIFIER 等常见标量类型。
典型使用场景包括:审计日志中记录当前操作人(而非登录名)、多租户应用中透传 tenant_id、避免重复查用户权限上下文。
实操建议:
- 写入必须用
sp_set_session_context,不能直接 INSERT 或 UPDATE;否则报错The session context is read-only - 键名区分大小写(
'UserId'和'userid'是两个键),且必须是合法标识符(不能含空格、点、括号等) - 值为
NULL时,SESSION_CONTEXT(N'key')返回 NULL,但该键仍存在于上下文中——清空需显式调用sp_set_session_context @key, NULL, @readonly = 0
如何安全地写入和读取 SESSION_CONTEXT?
写入要带 @readonly = 0(默认值),否则后续无法覆盖;读取直接用标量函数 SESSION_CONTEXT(N'key'),返回 sql_variant,常需显式转换。
常见错误现象:Cannot convert a value of type 'sql_variant' to type 'int' —— 这是因为没 CAST/CONVERT。
示例(安全读写):
EXEC sp_set_session_context @key = N'UserId', @value = 12345; EXEC sp_set_session_context @key = N'TenantId', @value = N'tenant-abc'; <p>-- 读取时必须转换 SELECT CAST(SESSION_CONTEXT(N'UserId') AS INT) AS UserId, CAST(SESSION_CONTEXT(N'TenantId') AS NVARCHAR(128)) AS TenantId;</p>
使用场景注意:
批量分析录音转写,输出多维度拓客报告。触发词:录音分析、总结、音频总结、拜访记录总结。当用户提及「分析录音」「看看录音数据」「最近的录音」「通话记录」且意图为批量统计/分析时触发。仅出现「录音」或「拜访」时需结合上下文,若仅查看单条详情则不触发。
- 在连接池环境下(如 .NET 的 SqlConnection),每次
Open()可能复用旧连接,务必在业务逻辑开头检查并重置必要上下文,避免脏数据残留 - 若在触发器中读取
SESSION_CONTEXT,需确认触发器执行路径确实继承了调用方的会话上下文(SQL Server 默认继承,但某些跨数据库调用或启用了EXECUTE AS时可能丢失)
为什么 SESSION_CONTEXT 在跨存储过程调用时有时“消失”?
它不会自动消失,但有三个典型原因导致读不到:
- 调用链中某处执行了
EXECUTE AS(尤其EXECUTE AS OWNER或EXECUTE AS USER),会切换执行上下文,新上下文不继承原会话的SESSION_CONTEXT - 存储过程中显式执行了
REVERT,且之前用EXECUTE AS切换过上下文,可能导致会话状态重置 - 连接被显式关闭又重建(比如 ADO.NET 中
Connection.Close()后再Open()),新连接无历史上下文
验证方法:在目标存储过程开头加一句 SELECT SESSION_CONTEXT(N'YourKey'),看是否为 NULL;若为 NULL,再检查调用链是否含 EXECUTE AS 语句。
绕过方案(谨慎使用):
- 改用
CONTEXT_INFO(二进制 128 字节,需自行序列化/反序列化,兼容性更好但更难维护) - 在调用
EXECUTE AS前,把关键值存入临时表或表变量,再在目标上下文中读取
性能和并发要注意什么?
SESSION_CONTEXT 是内存结构,读写开销极低,官方文档明确说明“无显著性能影响”。但它不是线程安全的“共享缓存”——每个会话独享一份,不存在并发修改冲突问题。
真正容易被忽略的点:
- 键名长度限制为 128 字符,超长会被截断,且截断后不报错,只静默失败(例如
sp_set_session_context @key = N'x' + REPLICATE(N'a', 128)实际存的是前 128 个字符) - 整个会话的
SESSION_CONTEXT总大小无硬性限制,但大量写入(如千级键)会影响内存局部性,极端情况下可能拖慢查询计划编译 - 在 Always On 可用性组中,
SESSION_CONTEXT不同步到辅助副本——它纯属会话本地状态,别指望在只读副本上读到主库写入的值
复杂点在于:它看起来像全局变量,实际却是连接粒度的、易被权限切换意外清空的隐式状态。用之前,先想清楚谁写、谁读、谁可能切断这条链。










