PROGRAM_NAME() 返回客户端连接时指定的Application Name,不自动记录日志,需显式设置才有效;未设置时返回默认值或NULL,生产中应结合参数传入或HOST_NAME()等辅助字段增强溯源可靠性。
PROGRAM_NAME() 函数本身不能直接记录修改来源
是一个会话级系统函数,返回当前连接的客户端应用程序名称(如
、
、
),但它**不自动写入任何日志**,也不参与审计或触发器的隐式捕获。你看到的“程序名”其实是客户端在建立连接时通过连接字符串中的
参数传入的——如果没设,SQL Server 默认填
或空字符串。
常见错误现象包括:
在触发器里直接用
,结果全是
或统一值,因为很多 ORM(如 Entity Framework)或中间件根本没设置
误以为开启默认跟踪或 SQL Server Audit 就能自动带出程序名,其实审计事件里
字段依赖的就是这个会话属性,源头没传,后续就为空
必须由客户端显式设置 Application Name 才有效
只有当连接字符串中明确包含
,
才能返回有意义的值。不同场景下的设置方式差异较大:
SSMS 连接:在“连接到服务器”对话框点“选项 → 额外连接参数”,填入
.NET SqlConnection:在连接字符串末尾加
Python pyodbc:连接时传参
(注意不是所有驱动都支持同名参数,pyodbc 用
关键字)
Java JDBC:使用
作为连接属性
没设或设错,
返回空或不可区分的默认值,后续所有基于它的判断都会失效。
在触发器或审计中安全使用 PROGRAM_NAME() 的要点
即使客户端设置了
,在服务端逻辑中调用
仍需注意上下文和权限:
它只在当前会话生命周期内有效;跨批处理(如触发器 + 后续 EXEC)可能因会话重用导致值漂移
在启用
的存储过程或触发器中,
返回的是**原始调用者**的程序名,不是执行上下文切换后的账户名——这点常被误认为是 bug
若用在 DML 触发器里记录修改来源,建议搭配
和
一起存,避免单点失效
SQL Server Audit 的
和
字段可与
联合查询,但审计策略本身不支持过滤
,只能事后 WHERE 筛选
替代方案:为什么光靠 PROGRAM_NAME() 不够可靠
真实生产环境里,仅依赖
做溯源存在明显短板:
Web 应用通常共用连接池,所有请求看起来都是同一个
(如
)
代理层(如 API 网关、负载均衡器)会掩盖真实客户端,
只反映中间层名称
无法区分同一程序内的不同业务模块(比如同一个 ERP 客户端里有采购/销售两个功能入口)
更稳妥的做法是:在业务层 SQL 中显式传入来源标识,例如用参数
,再由存储过程写入变更日志表。这比依赖会话元数据更可控,也绕过了连接字符串配置的运维耦合问题。
PROGRAM_NAME()SQL Server Management StudioMicrosoft AccessMyApp.exeApplication Name=xxxMicrosoft SQL Server Management StudioSELECT PROGRAM_NAME()NULLApplication Nameprogram_nameApplication Name=YourAppPROGRAM_NAME()Application Name=ReportService;Application Name=InventoryAppappname='OrderProcessor'application_nameapplicationName=ShippingModulePROGRAM_NAME()Application NamePROGRAM_NAME()EXECUTE ASPROGRAM_NAME()HOST_NAME()ORIGINAL_LOGIN()database_principal_nameclient_hostnameprogram_nameprogram_namePROGRAM_NAME()Application NameIIS AppPoolPROGRAM_NAME()@source_module = 'PurchaseOrderEntry'