sql server、mysql、postgresql均不提供可靠机制判断当前操作是否由触发器引发;@@nestlevel等指标非触发器专属,tg_level仅标识行/语句级,无法识别调用源;唯一可控方案是调用方主动透传上下文(如session_context或current_setting)。

无法在触发器内部可靠判断“当前操作是否由另一个触发器触发”——SQL Server、MySQL、PostgreSQL 都不提供类似 IS_TRIGGERED_BY_TRIGGER 的系统函数或上下文变量。所谓“嵌套触发器”的调用链,对当前正在执行的触发器来说是不可见的。
SQL Server 中为什么不能查 @@NESTLEVEL 或触发器名来判断?
有人尝试用 @@NESTLEVEL > 2 推断“被触发器调用”,但这是危险的误判:
-
@@NESTLEVEL统计的是所有嵌套层级(包括存储过程、用户定义函数、显式 EXEC),不是触发器专属深度 - 一个普通存储过程里执行
INSERT,也会让该 INSERT 触发的触发器看到@@NESTLEVEL≥ 2 - SQL Server 允许禁用嵌套触发器(
sp_configure 'nested triggers', 0),此时即使有嵌套也看不到,但业务逻辑仍可能被其他方式绕过 - 触发器名、对象 ID、调用栈(
sys.dm_exec_input_buffer)在触发器内不可访问,或需要极高权限且不稳定
MySQL 触发器里连调用堆栈痕迹都没有
MySQL 触发器运行时上下文极度受限:
- 没有等效于
@@NESTLEVEL的变量 -
CURRENT_USER()和USER()都只反映连接身份,与调用来源无关 - 无法访问
INFORMATION_SCHEMA.PROCESSLIST(权限不足 + 视图不可用于触发器) - 哪怕你在存储过程中执行
INSERT,触发器里也拿不到该存储过程的名字、参数或任何调用标记
PostgreSQL 的 TG_LEVEL 不是你要的那个“层级”
PostgreSQL 有 TG_LEVEL 变量,但它只表示“触发器是行级还是语句级”,值恒为 'ROW' 或 'STATEMENT',和“谁调用了我”完全无关。
真正能拿到的只有:TG_OP(操作类型)、TG_TABLE_NAME、TG_NAME(当前触发器名)——这些都描述“我在响应什么”,不描述“谁把我叫醒”。
如果你看到某篇博客说“用 TG_NAME LIKE '%_audit%' 过滤掉审计触发器”,那是靠命名约定硬编码规避,不是检测机制。
真正可控的替代方案:人工透传调用上下文
想区分“用户直连执行” vs “由某存储过程/应用模块发起”,唯一靠谱的做法是调用方主动声明:
- SQL Server:在业务存储过程开头执行
sp_set_session_context @key = N'caller_type', @value = N'proc_update_order',触发器里读SESSION_CONTEXT(N'caller_type') - PostgreSQL:用
SET app.user_context = 'web_api_v2',触发器中查current_setting('app.user_context', true) - MySQL:无原生 session context,只能靠应用层在 SQL 前加注释(如
/* caller=order_service */ INSERT ...),再配合代理层解析(如 ProxySQL)或自定义 UDF 提取 —— 但后者需编译安装,生产环境慎用
注意:所有这类透传都依赖调用方“守规矩”。如果某段代码跳过设置直接 DML,触发器就收不到上下文 —— 这不是缺陷,而是设计使然:数据库不替应用做决策,也不承担追踪全链路的责任。










