触发器不能直接替代identity,但能绕过其限制;它通过instead of insert+update...output原子操作手动接管编号生成,需独立序列表维护状态,不支持identity_insert,高并发下比select max()+1更安全,但复杂度和维护成本远高于sequence。

触发器不能直接替代 IDENTITY,但能绕过它的限制
SQL Server 的 IDENTITY 列本身不支持动态步长、条件跳号、多表共享或业务逻辑干预(比如“节假日跳过”“按部门编号前缀”)。触发器是唯一能在 INSERT 时介入并完全控制值生成的机制,但它不是“增强版自增”,而是“手动接管自增逻辑”。这意味着你必须自己维护计数状态,且无法享受 IDENTITY 带来的性能优化和事务一致性保障。
必须用单独的序列表 + 触发器,不能依赖 @@IDENTITY 或 SCOPE_IDENTITY()
常见错误是试图在触发器里读取刚插入的 @@IDENTITY 再加 1——这既不可靠(并发下会重复),也无法用于首行。正确做法是建一张独立的序列控制表:
CREATE TABLE SequenceControl (
SeqName NVARCHAR(50) PRIMARY KEY,
CurrentValue BIGINT NOT NULL
);
INSERT INTO SequenceControl VALUES ('OrderNo', 100000);
然后在触发器中用 UPDATE ... OUTPUT 原子更新并获取新值:
CREATE TRIGGER trg_GenerateOrderNo
ON Orders
INSTEAD OF INSERT
AS
BEGIN
DECLARE @NextNo BIGINT;
UPDATE SequenceControl
SET @NextNo = CurrentValue = CurrentValue + 1
OUTPUT INSERTED.CurrentValue
WHERE SeqName = 'OrderNo';
INSERT INTO Orders (OrderNo, CustomerID, OrderDate)
SELECT @NextNo, i.CustomerID, i.OrderDate
FROM inserted i;
END;
- 必须用
INSTEAD OF INSERT,否则原 INSERT 会失败(因为目标列不允许显式插入) -
UPDATE ... OUTPUT是关键:它保证并发安全,避免 SELECT+UPDATE 的竞态 - 不能用
SELECT MAX()+1,高并发下必然重复
触发器实现的自增序列不支持 SET IDENTITY_INSERT ON
一旦你用触发器接管了主键/编号列,该列就不再是 IDENTITY 列。这意味着:
- 你不能再用
SET IDENTITY_INSERT Orders ON手动插入指定编号——因为根本没定义IDENTITY - 如果原表已有
IDENTITY列,必须先DROP它再加普通列,否则触发器和IDENTITY冲突 - 迁移旧数据时,需先停写、填充序列表的
CurrentValue为MAX(YourColumn)+1,否则会覆盖或重复
真正需要“高度自定义”时,优先考虑 SEQUENCE 对象而非触发器
SQL Server 2012+ 的 SEQUENCE 已覆盖大部分定制需求,比触发器更轻量、更可靠:
CREATE SEQUENCE OrderSeq
START WITH 100000
INCREMENT BY 1
MINVALUE 100000
NO CYCLE;
使用时直接在 INSERT 中调用:
INSERT INTO Orders (OrderNo, CustomerID) VALUES (NEXT VALUE FOR OrderSeq, 123);
-
SEQUENCE支持缓存(CACHE 50),性能远高于触发器查表 - 可跨表复用,可重置(
ALTER SEQUENCE ... RESTART WITH),可设置循环 - 唯一缺点:不能在 INSERT 中嵌入复杂逻辑(如“若客户VIP则用另一序列”),这时才退回到触发器
触发器方案真正难搞的点不在写法,而在于你得自己处理所有边界:空表初始化、并发锁粒度、日志膨胀、故障恢复后序列一致性——这些都容易被忽略,直到上线压测才暴露。











