MySQL 主键与聚集索引,为什么不是一个东西?(10/38)

五道主键练习题逐题实操揭晓——主键和聚集索引,不要混淆

数据库内核实践第 10 课封面:数据库内核10:MySQL 主键与聚集索引,为什么不是一个东西?

五道主键练习题逐题实操揭晓——主键和聚集索引,不要混淆

《数据库内核原理》第 10 篇:主键五道练习题的答案与解析。

第 9 篇《InnoDB 主键,你真的懂吗?五道练习题自测》昨天挖的坑,今天填一下。

建表时,可不可以不声明主键?

create table user(
         name varchar(10)
)engine=innodb;

insert into user values('shenjian');
insert into user values('shenjian');

建表时,不声明主键,插入两个相同的元素。

提问,连续执行上面的语句,执行结果是:

A 建表语句(1)报错

B 插入语句(2)报错

C 插入语句(3)报错

D 均不报错

实操截图:不声明主键建表成功,两条相同记录均插入成功

实操如上,所以答案是【D 均不报错】

InnoDB必须要有聚集索引,行记录按照聚集索引物理上排序。必须要有聚集索引,并不代表一定要有主键。

(1)如果定义了主键,主键就是聚集索引;

(2)如果没有定义主键,第一个非空(not null)且唯一(unique)列是聚集索引;

(3)如果没有符合条件的列,会自动创建一个隐藏的row-id作为聚集索引;

本例就属于第三种情况。

建表时,可不可以不声明主键非空?

create table user(
         id int,
         name varchar(10),
         primary key(id)
)engine=innodb;

insert into user(name) values('shenjian');
insert into user(name) values('shenjian');

建表时,不声明非空,插入两个相同的元素。

提问,连续执行上面的语句,执行结果是:

A 建表语句(1)报错

B 插入语句(2)报错

C 插入语句(3)报错

D 均不报错

实操截图:第二次插入报主键冲突错误

实操如上,所以答案是:【C 插入语句(3)报错】

建表语句可以不主动设置主键非空(上例中的id),InnoDB会自动添加非空(not null)且唯一(unique)的限制。

实操截图:show create table 显示主键列 id 自动带非空限制且默认值为 0

使用show create table查看,会发现主键列要求非空,且默认值是0,因此:

insert into user(name) values('shenjian');

id默认为0,第一次插入成功,第二次插入主键冲突。

这一题很多人答错。

建表时,可不可以选择多个字段做主键?

create table user(
         id int not null,
         name varchar(10) not null,
         primary key(id, name)
)engine=innodb;

insert into user values(1, 'shenjian');
insert into user values(1, 'zhangsan');
insert into user values(2, 'shenjian');

建表时,声明联合主键(a,b),插入若干元素,有些a重复,有些b重复。

提问,连续执行上面的语句,执行结果是:

A 建表语句(1)报错

B 插入语句(2)报错

C 插入语句(3)报错

D 插入语句(4)报错

E 均不报错

实操截图:联合主键 (id, name) 下三条插入均成功

实操如上,所以答案是:【E 均不报错】

这就是所谓的联合主键,不要求每个列唯一,而要求联合主键各个列的“组合唯一”。

联合索引,联合主键,类似的。

可不可以主动插入自增主键?

create table user(
         id int auto_increment,
         name varchar(10) not null,
         primary key(id)
)engine=innodb;

insert into user(name) values('shenjian');
insert into user(id, name) values(10,'shenjian');
insert into user(name) values('shenjian');

建表时,自增ID为主键,插入若干元素,有些包含自增ID,有些不包含。

提问,连续执行上面的语句,执行结果是:

A 建表语句(1)报错

B 插入语句(2)报错

C 插入语句(3)报错

D 插入语句(4)报错

E 均不报错

实操截图:手动指定自增主键值 10 插入成功

实操如上,所以答案是:【E 均不报错】

自增ID经常被用来作为主键,插入行记录时,不指定值,InnoDB自动增加。同时,也可以手动指定值:

(1)手动指定的值与已有值重复,则主键冲突;

(2)与已有值不冲突,则插入成功;

(3)未来不指定值插入,会从手动插入值往后继续递增;

实操截图:手动插入 10 后,后续自增 ID 从 11 开始,中间出现空洞

如上例,手动插入10后,未来插入的行会从11开始,自增ID中间出现空洞。

建表时,可不可以使用联合自增主键?

create table user(
         id int auto_increment,
         name varchar(10) not null,
         primary key(name, id)
)engine=innodb;

insert into user(name) values('shenjian');
insert into user(id, name) values(10,'shenjian');
insert into user(name) values('shenjian');

建表时,声明联合主键(a,b),并且有一个是自增ID,插入若干元素,包含自增ID,有些不包含。

提问,连续执行上面的语句,执行结果是:

A 建表语句(1)报错

B 插入语句(2)报错

C 插入语句(3)报错

D 插入语句(4)报错

E 均不报错

实操截图:自增列不在联合主键第一列,建表语句报错

实操如上,所以答案是:【A 建表语句(1)报错】

这里不是指自增ID不能作为联合主键,而是如果作为联合主键,必须放在第一列。

实操截图:改为 primary key(id, name) 后建表成功

primary key(name, id)

改为

primary key(id, name)

就可以执行成功了。

这一题很多人答错。

总结

一:InnoDB建表时,可不可以不声明主键?

可以不声明主键,但必须要有聚集索引:

(1)有主键,主键是聚集索引;

(2)没有主键,首个非空唯一列是聚集索引;

(3)没有符合条件的列,row-id是聚集索引;

主键和聚集索引不是一个东西,不要混淆。

二:InnoDB建表时,可不可以不声明主键非空?

可以不声明主键非空,会自动加上非空限制。

三:InnoDB建表时,可不可以选择多个字段做主键?

可以使用联合主键,组合列唯一即可。

四:InnoDB插入时,可不可以主动插入自增主键?

可以指定自增列的值,但可能导致空洞。

五:InnoDB建表时,可不可以使用联合自增主键?

可以,但自增ID必须在联合主键的第一列。

希望大家对主键有更系统性的认识。

相关文章:

第 24 篇《InnoDB 缓冲池 buffer pool:LRU 的两次改良》

第 25 篇《InnoDB 写缓冲 change buffer:写请求怎么免磁盘 IO?》

【DCEEA】,你答对了吗?

原文是第 9 篇《InnoDB 主键,你真的懂吗?五道练习题自测》,评论里好多同学都答错了。

检验一下

检索练习 · 聚集索引的来源

InnoDB 建表没有定义主键时,聚集索引怎么确定?

检索练习 · 主键与非空

建表时主键列不声明 not null,连续两次 insert 不指定该列,结果是?

检索练习 · 联合主键

对联合主键 (id, name) 依次插入 (1,’shenjian’)、(1,’zhangsan’)、(2,’shenjian’),结果是?

检索练习 · 联合自增主键

自增列作为联合主键的一员时,有什么要求?

主键的坑填完了,下一个问题自然浮现:既然普通索引都存着主键值,主键太长会怎样?第 11 篇:数据库主键为什么不宜太长?

知其然,知其所以然。思路比结论更重要。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

京ICP备2025137728号-3