不能在 INSERT 语句中手写 NEWID(),因其易漏写、触发器覆盖、批量插入报错、ORM映射失败;应使用 DEFAULT NEWID() 约束实现自动、安全、零侵入的 GUID 主键生成。

直接用 NEWID() 作为 UNIQUEIDENTIFIER 列的默认约束,而不是在 INSERT 语句里手动生成——否则主键冲突风险高、维护成本陡增,尤其在并发插入或带触发器/外键的场景下。
为什么不能在 INSERT 里写 NEWID()?
看似可行,实则埋雷:
- 每次
INSERT都得显式写出NEWID(),字段一多就容易漏写或写错位置 - 如果表有
INSTEAD OF INSERT触发器,且触发器逻辑没处理该列,值可能被清空或覆盖为NULL - 批量插入(如
INSERT INTO ... SELECT)时,若源数据未提供该列,又没设默认值,会报错:Cannot insert the value NULL into column 'id' - ORM(如 Entity Framework)映射时,若字段未设
DatabaseGeneratedOption.Identity,可能误传Guid.Empty导致重复主键
正确做法:用 DEFAULT NEWID() 约束
这是 SQL Server 原生支持、零侵入、自动生效的方式:
- 建表时直接定义:
CREATE TABLE users ( id UNIQUEIDENTIFIER DEFAULT NEWID() PRIMARY KEY, name NVARCHAR(50), email NVARCHAR(100) );
- 已有表加约束:
ALTER TABLE orders ADD CONSTRAINT DF_orders_id DEFAULT NEWID() FOR id;
- 插入时完全省略该列:
INSERT INTO users (name, email) VALUES ('Bob', 'b@example.com');,id自动填充 - 注意:
DEFAULT不会回填已有行,只对新插入生效;若需补全历史数据,得单独UPDATE
NEWID() vs NEWSEQUENTIALID():选哪个?
关键看是否把 UUID 当作聚集索引主键:
- 如果
PRIMARY KEY CLUSTERED是UNIQUEIDENTIFIER列,优先用NEWSEQUENTIALID()——它生成递增 GUID,大幅降低页拆分和索引碎片 - 但
NEWSEQUENTIALID()只能用于DEFAULT约束,不能出现在SELECT或函数中,也不能在 SSMS 表设计器里点选设置,必须用 T-SQL:ALTER TABLE logs ADD CONSTRAINT DF_logs_guid DEFAULT NEWSEQUENTIALID() FOR guid;
- 如果主键是
INT IDENTITY,而UNIQUEIDENTIFIER只是业务 ID(如订单号、API key),那NEWID()更安全——避免顺序性暴露插入时间或总量
常见陷阱:唯一索引 + NULL 值导致失败
很多人加了 DEFAULT NEWID() 还报唯一冲突,往往卡在这几个点:
- 列允许
NULL,但没设NOT NULL,导致部分老数据为NULL,后续建唯一索引时报错:Cannot create unique index. The duplicate key value is (NULL) - 试图对含
NULL的列建唯一索引——SQL Server 把多个NULL当作重复值,直接拒绝 - 表里已有重复 GUID 值(比如手动插入过相同值),加唯一约束前没清理:
SELECT id, COUNT(*) FROM users GROUP BY id HAVING COUNT(*) > 1 - 用
NEWID()生成短码(如截取10位)时,在函数内调用会报错:Invalid use of side-effecting operator in 'newid',因为函数不允许非确定性操作
真正要防的是「默认值没生效」和「唯一性校验被绕过」,而不是 UUID 本身撞车——NEWID() 碰撞概率低到可忽略,但人为漏设约束、容忍 NULL、跳过索引验证,才是线上主键冲突的真正源头。

















