primary key 通过自动创建唯一索引并隐式添加 not null 和 unique 约束来强制行唯一性,拒绝重复或空值的插入与更新;单列主键适用于有独立id的场景,复合主键适用于多列组合才能唯一标识的业务;建表时定义更稳妥,添加前须确保无null和重复值;主键需配合unique约束、原子插入及数据预检等手段共同保障业务级唯一性。

靠 PRIMARY KEY 约束保证每一行唯一,核心是让数据库自动拒绝重复或空值的插入和更新。它不只是“标个记号”,而是通过强制规则+底层索引双重机制来落地。
主键如何从机制上确保唯一性
当你给一列(或几列)加了 PRIMARY KEY,数据库会立刻做两件事:
- 在这些列上自动创建一个唯一索引(Unique Index),所有写入操作都会被这个索引实时校验;
- 同时隐式加上NOT NULL + UNIQUE两个约束——哪怕你没显式写,数据库也绝不允许该列存 NULL 或重复值。
也就是说,只要主键定义生效,任何违反唯一性或空值的操作(比如 INSERT 相同主键值、UPDATE 成已存在的主键、INSERT NULL)都会直接报错,数据根本进不去。
单列主键 vs 复合主键:选哪种更合适
关键看业务中哪组信息天然能唯一标识一行:
-
单列主键:适合有明确 ID 字段的场景,比如
user_id INT PRIMARY KEY、order_no CHAR(12) PRIMARY KEY。简单高效,查询和关联都快; -
复合主键:适合没有独立 ID,但多列组合才有意义的场景。例如订单明细表:
PRIMARY KEY (order_id, product_id),表示“同一个订单里不能重复添加同一商品”——单看order_id或product_id都可重复,合起来才唯一。
注意:复合主键会让外键引用、索引大小、JOIN 条件变复杂,别为了“看起来规范”硬凑多个字段。
创建主键的两种常用方式
无论新表还是已有表,都能加主键:
-
建表时直接定义(推荐):
CREATE TABLE students (id INT PRIMARY KEY, name VARCHAR(50)); -
对已有表添加:
ALTER TABLE students ADD CONSTRAINT pk_students PRIMARY KEY (id);
注意:执行前必须确保id列已存在、无 NULL 值、且所有值不重复,否则命令失败。
主键不是万能的,得配合其他手段防漏
主键管的是“物理行唯一”,但业务逻辑可能有额外要求:
- 比如用户名要唯一,但又不想把它当主键(因为主键最好稳定不变),那就额外加个
UNIQUE约束:username VARCHAR(50) UNIQUE; - 并发写入时,主键冲突可能发生在应用层判断之后(即“先查后插”有竞态),这时要用
INSERT ... ON CONFLICT DO NOTHING(PostgreSQL)或INSERT IGNORE(MySQL)这类原子操作兜底; - 导入历史数据前,务必先用
SELECT COUNT(*) GROUP BY ... HAVING COUNT(*) > 1检查潜在重复,否则加主键会直接失败。
主键是数据完整性的第一道防线,但它需要和业务理解、索引设计、应用逻辑一起配合,才能真正稳住唯一性。










