SQL Server 捕获 ALTER TABLE 的唯一合法方式是创建数据库级 DDL 触发器:CREATE TRIGGER xxx ON DATABASE FOR ALTER_TABLE;表级、SCHEMA 级或使用 AFTER/INSTEAD OF 均非法,且必须用 FOR 关键字。

SQL Server 必须用 ON DATABASE FOR ALTER_TABLE,不能建在表上
想捕获 ALTER TABLE,唯一合法路径是数据库级 DDL 触发器:CREATE TRIGGER xxx ON DATABASE FOR ALTER_TABLE。写成 ON users 或 ON SCHEMA::dbo 都会报错——表级触发器只响应 DML,对 DDL 完全无感知。
常见错误包括:误用 AFTER ALTER_TABLE(SQL Server 不支持 AFTER 修饰 DDL 触发器),或尝试 INSTEAD OF ALTER_TABLE(语法非法,直接拒绝创建)。
-
FOR是唯一被接受的关键字,不是可选项 - 触发器必须由具备
CREATE DATABASE DDL TRIGGER权限的用户创建 - 若日志表跨库(比如写入监控库
audit_db),创建者还需ALTER ANY DATABASE权限
EVENTDATA() 提取字段要防空、防截断、防解析失败
EVENTDATA() 返回 XML,但其中的 /EVENT_INSTANCE/TSQLCommand 节点默认可能被截断(尤其含长 COMMENT 或多列定义时),且整个 XML 在某些极端场景下可能为 NULL。
安全做法是包裹 TRY...CATCH,并用 .value() 显式指定类型和默认值:
DECLARE @object_name sysname = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname');
DECLARE @tsql nvarchar(max) = ISNULL(EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(max)'), N'<unknown>');
- 永远检查
@object_name是否为 NULL,避免后续插入失败 - 不要用
CONVERT(nvarchar(max), EVENTDATA())存整段 XML——体积大、难查询、易超长 - 过滤
tempdb必须放在最开头:IF EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname') = 'tempdb' RETURN
禁止在触发器里发邮件、调外部服务或写远程库
触发器运行在 DDL 事务上下文中,任何阻塞型 I/O 都会让 ALTER TABLE 卡住,轻则超时,重则阻塞整个数据库变更窗口。
典型高危操作:
- 调用
sp_send_dbmail—— 会同步等待 SMTP 响应 - 执行
EXEC xp_cmdshell—— 启动进程需系统权限且不可控 - INSERT INTO 远程服务器链接(如
linked_server.audit_db.dbo.log)—— 网络延迟直接拖垮事务
正确做法只做一件事:向本地 ddl_change_log 表插入一行,字段至少含:event_time、event_type、object_name、schema_name、tsql_command、login_name。
MySQL 和 PostgreSQL 根本不支持这种捕获方式
MySQL 所有版本都不提供 DDL 触发器,BEFORE UPDATE 或 AFTER INSERT 对 ALTER TABLE 完全无效,语法上就不允许绑定到 DDL 事件。
PostgreSQL 没有 FOR ALTER_TABLE 这种触发器,只能用 CREATE EVENT TRIGGER 监听 ddl_command_end,但它必须由超级用户创建,且不能在事务块中执行(否则报 ERROR: cannot create event trigger within transaction block)。
关键差异:
- PG 的 event trigger 不介入事务,只在提交后异步触发;SQL Server 的 DDL 触发器在事务内执行,能
ROLLBACK - PG 的
NOTIFYpayload 限 8000 字节,传完整语句易截断;SQL Server 的EVENTDATA()虽也受限,但本地写表更可控 - MySQL 5.7+ 只能轮询
performance_schema.events_statements_history,非实时、不可靠、开销大
真正线上可用的“实时捕获”,目前只有 SQL Server 的 ON DATABASE FOR ALTER_TABLE 能做到语句级、事务内、零漏报——但前提是别在触发器里干不该干的事。

















