不能可靠检测。SQL Server、MySQL、PostgreSQL均无IS_TRIGGERED_BY_TRIGGER类机制;@@NESTLEVEL统计全栈深度而非触发器专属,TG_LEVEL仅标识行/语句级,无法识别调用源;唯一可控方案是调用方主动透传上下文(如SESSION_CONTEXT或current_setting)。

不能可靠检测。SQL Server、MySQL、PostgreSQL 都没有提供 IS_TRIGGERED_BY_TRIGGER 或类似机制,所有所谓“判断方式”要么误判率高,要么依赖外部人工配合。
为什么 @@NESTLEVEL 在 SQL Server 里不靠谱
有人用 @@NESTLEVEL > 2 推断“当前在触发器里被调用”,但这是危险的误判:
-
@@NESTLEVEL统计的是整个执行栈深度,包括存储过程、函数、EXEC等,不是触发器专属指标 - 一个普通存储过程里执行
INSERT,该语句触发的触发器也会看到@@NESTLEVEL >= 2 - 即使禁用嵌套触发器(
sp_configure 'nested triggers', 0),@@NESTLEVEL仍可能因其他嵌套而升高 - 触发器内部无法访问
sys.dm_exec_input_buffer或调用栈,也没权限查sys.dm_exec_sessions
为什么 TG_LEVEL 在 PostgreSQL 里没用
TG_LEVEL 只返回 'ROW' 或 'STATEMENT',它只说明当前触发器是行级还是语句级,和“谁调用了我”完全无关:
-
TG_NAME是当前触发器名,不是调用者名 -
TG_OP和TG_TABLE_NAME描述“我在响应什么”,不描述“谁把我叫醒” - 网上有些方案靠
TG_NAME LIKE '%_audit%'过滤,本质是硬编码命名约定,不是检测逻辑 - 想区分“由应用发起”还是“由另一个触发器发起”,PG 没有等效变量或函数
唯一可控的做法:调用方主动透传上下文
你无法让触发器“自己感知”来源,但可以让上游明确声明——这是目前最稳定、可审计、可维护的方式:
- SQL Server:业务存储过程开头执行
sp_set_session_context @key = N'caller_type', @value = N'api_update_user',触发器里读SESSION_CONTEXT(N'caller_type') - PostgreSQL:应用连接后执行
SET app.user_context = 'web_api_v2',触发器里用current_setting('app.user_context', true) - MySQL:没有原生 session context,可用
user_variables模拟(如@caller_type := 'batch_job'),但需确保每次操作前重置,且注意事务边界 - 关键点:这个值必须由可信路径设置,不能依赖客户端自由填写;日志中应同时记录
ORIGINAL_LOGIN()、HOST_NAME()辅助交叉验证
真正难的不是写个 IF 判断,而是接受“数据库层天然不暴露调用链”这个事实。任何试图绕过透传、靠运行时猜来源的方案,上线后都会在某个低概率场景下失效——而且很难复现。










