前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL索引的一些小细

MySQL索引的一些小细

作者头像
AsiaYe
发布2019-11-06 17:07:58
5040
发布2019-11-06 17:07:58
举报
文章被收录于专栏:DBA随笔DBA随笔

//

MySQL索引的一些小细节

//

忙活了一天,晚上抽空做了几个关于索引和主键的小测试,记录一下,希望对大家有帮助。(所有测试都是在MySQL5.7的环境下测试的)

1

主键可不可以不设置not null和auto_increment?

看例子吧:

代码语言:javascript
复制
mysql>>create table index_test1(
    id int,
    name varchar(10),
    primary key (id));
Query OK, 0 rows affected (0.01 sec)

从结果来看,建表语句是可以的,但是当我们进行插入的时候,如果不指定id的值:

代码语言:javascript
复制
mysql>>insert into index_test1 (name) values  ('yeyz');  
ERROR 1364 (HY000): Field 'id' doesn't have a default value

不指定默认值,则会进行提醒。

如果不想要提醒,则只需要设置主键自增即可:

代码语言:javascript
复制
mysql>>create table index_test3 (
      id int auto_increment,
      name varchar(10) not null,
      primary key (id));
Query OK, 0 rows affected (0.01 sec)

mysql>>insert into index_test3 (name) values ('yeyz');
Query OK, 1 row affected (0.00 sec)

mysql>>insert into index_test3 (id,name) values (10,'yeyz');
Query OK, 1 row affected (0.00 sec)

mysql>>insert into index_test3 (name) values ('yyz');       
Query OK, 1 row affected (0.00 sec)

mysql>>select * from index_test3;
+----+------+
| id | name |
+----+------+
|  1 | yeyz |
| 10 | yeyz |
| 11 | yyz  |
+----+------+
3 rows in set (0.00 sec)

结论:

主键列可以不设置not null和auto_increment选项,但是这种情况下,插入不包含主键列的记录,会提示主键没有默认值。此时,需要单独提供主键的默认值。

如果主键列为int类型,并且包含auto_increment选项,则可以直接插入不包含主键列的记录。

2

主键为多个字段时,是否允许其中一个字段自增?

这个例子我看到结果的时候还是觉得挺神奇的,如下:

代码语言:javascript
复制
mysql>>create table index_test4 (
      id int auto_increment,
      name varchar(10) not null,
      primary key (name,id));
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key

mysql>>create table index_test4 (
      id int auto_increment,
      name varchar(10) not null,
      primary key (id,name)); 
Query OK, 0 rows affected (0.02 sec)

当我们使用id和name两个字段作为主键的时候,如果id是自增列,那么设置主键为(name,id)则会报错,而设置主键为(id,name)则不会报错,这是一个很奇怪的现象。

然后,当我们进行数据插入的时候,如果只插入了name列,则主键列会自动补齐:

代码语言:javascript
复制
mysql >>insert into index_test4 (name) values ('yeyz');
Query OK, 1 row affected (0.00 sec)

mysql >>insert into index_test4 (id,name) values (2,'yeyz');
Query OK, 1 row affected (0.00 sec)

mysql--dba_admin@127.0.0.1:yeyztest 20:14:19>>select * from index_test4;
+----+------+
| id | name |
+----+------+
|  1 | yeyz |
|  2 | yeyz |
+----+------+
2 rows in set (0.00 sec)

如果只插入了主键列,则name列必须有默认值,没有默认值会报错,如下:

代码语言:javascript
复制
mysql >>insert into index_test4 (id) values (7);        
ERROR 1364 (HY000): Field 'name' doesn't have a default value

mysql >>alter table index_test4 modify name varchar(10) default 'abc';
Query OK, 5 rows affected (0.03 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql >>insert into index_test4 (id) values (7);                      
Query OK, 1 row affected (0.00 sec)

3

反向查找能用到索引么?

反向查找(!=)能用到索引么?答案是能。看例子:

代码语言:javascript
复制
mysql >>show create table index_test3\G
*************************** 1. row ***************************
       Table: index_test3
Create Table: CREATE TABLE `index_test3` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql >>select *from index_test3;
+----+------+
| id | name |
+----+------+
|  1 | yeyz |
| 10 | yeyz |
| 12 | yyz  |
+----+------+
3 rows in set (0.00 sec)

mysql >>explain select * from index_test3 where id!=1;
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table       | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | index_test3 | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    3 |   100.00 | Using where |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到,explain的结果中,type是range,则说明使用到了索引。

4

当字段有null值时,反向查找可能会得到错误结果

看看下面这个例子:

代码语言:javascript
复制
mysql >>select * from index_test3;
+----+------+
| id | name |
+----+------+
|  1 | yeyz |
|  3 | NULL |
|  4 | NULL |
|  5 | NULL |
|  6 | NULL |
| 10 | yeyz |
| 12 | yyz  |
| 13 | yeyz |
| 14 | yeyz |
| 15 | yyz  |
| 16 | yeyz |
| 17 | yeyz |
| 18 | yyz  |
| 19 | yeyz |
| 20 | yeyz |
| 21 | yyz  |
+----+------+
16 rows in set (0.00 sec)

mysql >>select *from index_test3 where name!='yeyz';
+----+------+
| id | name |
+----+------+
| 12 | yyz  |
| 15 | yyz  |
| 18 | yyz  |
| 21 | yyz  |
+----+------+
4 rows in set (0.00 sec)

这个查询,并没有过滤出来name的值为null的列。要想得到值为null的列,需要添加or这个关键字,如下:

代码语言:javascript
复制
mysql >>select *from index_test3 where name!='yeyz' or name is null;
+----+------+
| id | name |
+----+------+
|  3 | NULL |
|  4 | NULL |
|  5 | NULL |
|  6 | NULL |
| 12 | yyz  |
| 15 | yyz  |
| 18 | yyz  |
| 21 | yyz  |
+----+------+
8 rows in set (0.00 sec)

5

唯一索引包含null值的时候,还是会用到索引的。

废话不多说,直接看例子:

代码语言:javascript
复制
mysql >>show create table index_test6\G
*************************** 1. row ***************************
       Table: index_test6
Create Table: CREATE TABLE `index_test6` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql >>explain select * from index_test6 where id is null;
+----+-------------+-------------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+
| id | select_type | table       | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+-------------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | index_test6 | NULL       | ref  | id            | id   | 5       | const |    1 |   100.00 | Using index condition |
+----+-------------+-------------+------------+------+---------------+------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

这里的index_test6包含唯一索引id,但是当我们使用id is null作为条件的时候,依旧使用到了索引。

最后,写一个需要注意的现象,当我们对唯一索引使用!=某一个特定值的方法进行匹配,不会使用索引,当唯一索引改为主键的时候,同样的语句就会使用到索引,还是以上面的index_test6为例,我们修改id列为主键,此时发现,同样的语句,就可以使用索引了,过程如下:

代码语言:javascript
复制
mysql >>show create table index_test6\G
*************************** 1. row ***************************
       Table: index_test6
Create Table: CREATE TABLE `index_test6` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql >>explain select * from index_test6 where id != 10;
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table       | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | index_test6 | NULL       | ALL  | id            | NULL | NULL    | NULL |   10 |    90.00 | Using where |
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
#删除唯一索引,改为主键。
mysql >>alter table index_test6 modify id int not null auto_increment primary key;
Query OK, 10 rows affected (0.03 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql >>alter table index_test6 drop index `id`;     
Query OK, 10 rows affected (0.03 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql >>show create table index_test6\G
*************************** 1. row ***************************
       Table: index_test6
Create Table: CREATE TABLE `index_test6` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql >>explain select * from index_test6 where id != 10;
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table       | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | index_test6 | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    9 |   100.00 | Using where |
+----+-------------+-------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DBA随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档