azure sql 数据库不支持原生 sqltrigger 绑定,需通过 cdc + timertrigger 定期轮询 cdc.dbo_表名_ct 表实现变更捕获,配合 sqlinputbinding 拉取数据并用外部存储维护 lsn 状态。

Azure SQL 数据库本身不支持“弹性数据库触发器”这种原生绑定机制——Azure Database for MySQL 有专用触发器绑定,但 Azure SQL 没有同名的 SqlTrigger 绑定。你真正能用的,是基于 CDC(变更数据捕获)或更改跟踪(Change Tracking)的函数触发方案,配合 Azure Functions 的输入绑定或轮询逻辑。
为什么不能直接用 SqlTrigger 绑定监听 Azure SQL 变更
Azure Functions 当前(截至 2026 年 8 月)对 Azure SQL 的官方绑定中:SqlInputBinding 和 SqlOutputBinding 存在,但 SqlTrigger 并未 GA,仅在部分预览版 SDK 中以实验性方式存在,且不支持弹性消耗计划(Elastic Consumption Plan),也不兼容 Azure SQL 的 CDC 表结构自动发现。
常见错误现象包括:
- 部署时出现
Function binding error: 'sqlTrigger' is not a supported binding type - 本地调试通过,但部署到 Azure 后函数从不触发,日志中无轮询记录
- 即使启用 CDC,函数也报错
Cannot find capture instance for table 'dbo.Customers'
正确做法:用 CDC + SqlInputBinding 模拟触发行为
这不是“监听”,而是“按需拉取”——Azure Functions 通过定时触发器(如 TimerTrigger)定期查询 CDC 系统表,再用 SqlInputBinding 获取变更行。这是目前最稳定、生产可用的方式。
实操要点:
- 先在数据库启用 CDC:
EXEC sys.sp_cdc_enable_db;,再为表启用:EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 'Customers', @role_name = NULL; - CDC 表名固定为
cdc.dbo_Customers_CT,函数中CommandText必须显式引用它,不能只写SELECT * FROM Customers -
SqlInputBinding的ConnectionStringSetting必须指向启用了 CDC 的数据库,并确保执行账号有db_owner或至少select权限在cdc.*schema 下 - 避免在
CommandText中使用GET_MAX_LSN()等动态函数——绑定不支持运行时计算 LSN,应改用sys.fn_cdc_get_min_lsn('dbo_Customers')配合状态存储(如 Azure Blob 或 Table Storage)来维护上次处理的 LSN
租约与并发安全:别忽略 cdc.lsn_time_mapping
CDC 不保证变更时间戳精确到毫秒级,__$start_lsn 才是唯一可靠顺序标识。如果你依赖时间字段(如 ModifiedAt)做去重或排序,会漏变更或重复处理。
关键注意事项:
- 不要用
WHERE ModifiedAt > @lastRun查询变更——该列可能被业务逻辑覆盖、为空、或被批量导入绕过 - 必须用
sys.fn_cdc_map_time_to_lsn('largest less than or equal', @time)将时间转为 LSN,再结合cdc.fn_cdc_get_all_changes_dbo_Customers(@from_lsn, @to_lsn, 'all')拉取 - 每次成功处理后,把
@to_lsn存入外部存储(如azure-cdc-checkpointblob),否则函数重启会导致重复消费 - 若函数实例多于 1 个(如弹性计划自动扩缩),必须加分布式锁(如 Azure Blob Lease)防止多个实例同时读同一段 LSN 区间
最易被忽略的一点:CDC 表默认不包含 DELETE 操作的原始值——只留 __$operation = 1 标记。如果业务需要知道“谁被删了”,必须提前在源表上启用 WITH (TRACK_COLUMNS_UPDATED = ON),并在函数中主动 JOIN 原表(用主键)补全数据,否则拿到的是空行。











