Navicat勾选“Auto Increment”默认生成serial(integer+sequence+nextval默认值),而非GENERATED ALWAYS AS IDENTITY;前者无插入保护,后者符合SQL标准且禁止显式赋值,需手写DDL实现。
Navicat里勾选“Auto Increment”到底生成什么?
它默认生成 serial,不是 generated always as identity。两者表面都“自增”,但底层机制和行为差异很大:serial 本质是 integer + 隐式关联一个 sequence + default nextval(...);而 identity 是 sql 标准语法,带插入保护(比如不能直接 insert into t(id) values (1),除非加 overriding system value)。
Navicat 16+ 对 identity 支持较稳;旧版(如 12/15)在编辑含 identity 列的表时,可能灰掉字段或报错 ERROR: cannot insert into column "id" because it is a generated column —— 这不是你写错了,是 Navicat 没适配好。
- 快速建新表:直接在“设计表”里选字段 → 勾
Auto Increment→ 点保存,Navicat 会生成id serial PRIMARY KEY - 需要逻辑复制、严格标准兼容、或防止绕过自增插值:别依赖图形界面,手写 DDL:
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY - 已存在表想改:Navicat 不支持把
serial升级成identity,得用 SQL 手动ALTER COLUMN ... SET GENERATED ALWAYS AS IDENTITY(v10+)
为什么点了小钥匙图标,主键还是没生效?
Navicat 的“设为主键”操作只是标记,不是立即执行。它只在你点击保存(磁盘图标或 Ctrl+S)时才真正发 ALTER TABLE ADD CONSTRAINT ... PRIMARY KEY。
常见现象:点完小钥匙,状态栏没变,SELECT conname, contype FROM pg_constraint WHERE conrelid = 'your_table'::regclass 查不到 contype = 'p' —— 因为根本没提交。
- 务必确认底部状态栏显示 “Unsaved changes”,再点保存
- 如果表里已有重复值或 NULL,保存时会失败,报
ERROR: could not create unique index,得先清理数据 - Navicat 默认约束名是
table_pkey,迁移时容易冲突,建议手写 DDL 显式指定:CONSTRAINT my_custom_pk PRIMARY KEY
手动关联 sequence 和 default 值怎么写才安全?
如果你自己建了序列(比如 CREATE SEQUENCE user_id_seq START 1),再想把它绑到已有字段,默认值必须写成:nextval('user_id_seq'::regclass) —— ::regclass 很关键,它让 PostgreSQL 能按 schema 查找序列,避免跨 schema 找错对象。
别漏掉单引号,也别写成 nextval(user_id_seq)(没引号会当变量名解析,报错)或 nextval('public.user_id_seq')(硬编码 schema 名,迁移时易出问题)。
- 序列名要和
nextval()里的一致,大小写敏感;Navicat 创建serial时生成的序列名通常是table_col_seq(如users_id_seq) - 如果表已有数据,START 值必须 ≥ 当前最大 ID,否则后续插入会冲突
- 序列本身不绑定表,删表不会自动删序列,得手动
DROP SEQUENCE,且前提是没其他字段还在引用它
导出表结构时自增列突然“失效”了?
Navicat 导出 SQL 时,对 serial 通常能正确还原成 serial 类型;但对 identity 列,旧版导出可能降级成普通 integer + 手动 nextval() 默认值,丢失 GENERATED 语义。
这意味着:导出后再导入,原表的插入保护(如拒绝显式赋值)就没了,行为退化成 serial。
- 导出前检查 Navicat 版本:16+ 更可靠;15 及以下建议导出后人工核对 DDL 是否含
GENERATED ALWAYS AS IDENTITY - 跨环境迁移(比如从开发导到生产),优先用
pg_dump -s而非 Navicat 导出,它对identity支持更完整 -
serial的序列名不固定,Navicat 不会帮你重命名或同步更新,换库后若序列名冲突,得手动ALTER SEQUENCE ... RENAME TO
::regclass 的强制类型转换 —— 这两个点不处理,90% 的自增失败都卡在这儿。











