优先选NEWSEQUENTIALID();它在单机内生成递增GUID,降低聚集索引页分裂,但仅限默认约束使用,不可直接调用。

SQL Server里用NEWID()还是NEWSEQUENTIALID()?
直接说结论:如果表有聚集索引(尤其是主键就是聚集索引),优先选 NEWSEQUENTIALID();否则 NEWID() 也能用,但容易导致页分裂和性能下降。
原因很简单:NEWID() 生成的是完全随机的 GUID,插入时大概率落在数据页中间,触发页拆分;NEWSEQUENTIALID() 生成的是递增 GUID(只在本机有效),插入位置更可预测,对聚集索引更友好。
注意:NEWSEQUENTIALID() 只能用在列默认约束中,不能在普通 SQL 语句里直接调用(比如 SELECT NEWSEQUENTIALID() 会报错)。
在存储过程中给新记录赋 GUID 主键的写法
常见错误是先 DECLARE @id UNIQUEIDENTIFIER,再 SET @id = NEWID(),最后 INSERT ... VALUES (@id, ...)——这没问题,但不够简洁;更推荐用 OUTPUT 或默认约束自动处理。
实操建议:
- 建表时就给主键列加默认值:
id UNIQUEIDENTIFIER DEFAULT NEWSEQUENTIALID() PRIMARY KEY - 存储过程里直接
INSERT INTO table (name, email) VALUES (@name, @email),让 SQL Server 自动填充id - 如果必须拿到刚生成的 ID 做后续操作(比如插入子表),用
OUTPUT:INSERT INTO users (name) OUTPUT INSERTED.id VALUES (@name)
- 避免在循环中反复调用
NEWID()——虽然语法合法,但每次调用都是独立随机值,无法保证顺序性或业务语义
跨数据库兼容性问题:MySQL/PostgreSQL 怎么办?
GUID 不是 SQL 标准,各数据库实现不同。SQL Server 的 NEWID() 在 MySQL 里对应 UUID(),但返回格式带短横线(如 '6F9619FF-8B86-D011-B42D-00C04FC964FF'),而 SQL Server 存的是二进制(UNIQUEIDENTIFIER 类型),直接插入可能失败。
关键差异点:
- MySQL:
UUID()返回字符串,需配合CHAR(36)或BINARY(16)字段;用UUID_TO_BIN()转二进制更省空间 - PostgreSQL:
gen_random_uuid()(需启用pgcrypto扩展),类型是UUID,原生支持 - Oracle:
SYS_GUID()返回RAW(16),不是标准 UUID 字符串格式
所以,如果存储过程要跨库移植,别硬写 NEWID(),改用应用层生成标准 UUID v4 字符串再传入更可控。
为什么不能把 GUID 当作“业务有序号”来用?
有人试图靠 ORDER BY id DESC 查最新记录,结果发现顺序不可靠——因为 NEWID() 完全随机,NEWSEQUENTIALID() 只保证单机内大致递增,重启服务、换机器后就不连续了。
真正需要时间顺序的场景,必须额外加 created_at DATETIME2 DEFAULT GETDATE() 列,并按它排序。
另外,GUID 作为聚集索引键会导致非叶子节点变大,间接影响所有二级索引大小。如果表很大、查询频繁,且没有强分布式 ID 需求,其实 BIGINT IDENTITY + 联合唯一约束更轻量。
GUID 主键真正的价值在于合并多源数据、离线生成、避免暴露业务增长量——不是为了“看起来像主键”。

















