mysql 部门表_MySQL高级

本文大纲

4e9e185f33a4a568fbbed24a4b38a4e8.png

环境

  • win10-64

  • MySQL Community Server 5.7.1

    mysqld –version可查看版本

  • 官方文档

SQL执行顺序

手写顺序

我们可以将手写SQL时遵循的格式归结如下:

select distinct
    from  join  on wheregroup byhavingorder bylimit <offset>,<rows>
  • distinct,用于对查询出的结果集去重(若查出各列值相同的多条结果则只算一条)

  • join,关联表查询,若将两个表看成两个集合,则能有7种不同的查询效果(将在下节介绍)。

  • group by,通常与合计函数结合使用,将结果集按一个或多个列值分组后再合计

  • having,通常与合计函数结合使用,弥补where条件中无法使用函数

  • order by,按某个标准排序,结合asc/desc实现升序降序

  • limit,如果跟一个整数n则表示返回前n条结果;如果跟两个整数m,n则表示返回第m条结果之后的n条结果(不包括第m条结果)

MySQL引擎解析顺序

而我们将SQL语句发给MySQL服务时,其解析执行的顺序一般是下面这样:

from
    
on
    
 join
    
where
    
group by
    
having
    
select
    
order by
    
limit
    offset,rows
b6cb96a8452d333442eae9709b77935d.png

了解这个对于后续分析SQL执行计划提供依据。

七种Join方式

46d1cac79b60eb12a8e1bfb5f8730842.png

下面我们创建部门表tbl_dept和员工表tbl_emp对上述7种方式进行逐一实现:

  • 部门表:主键id、部门名称deptName,部门楼层locAdd

mysql> CREATE TABLE `tbl_dept` (
    ->  `id` INT(11) NOT NULL AUTO_INCREMENT,
    ->  `deptName` VARCHAR(30) DEFAULT NULL,
    ->  `locAdd` VARCHAR(40) DEFAULT NULL,
    ->  PRIMARY KEY (`id`)
    -> ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  • 员工表:主键id,姓名name、所属部门deptId

mysql> CREATE TABLE `tbl_emp` (
    ->  `id` INT(11) NOT NULL AUTO_INCREMENT,
    ->  `name` VARCHAR(20) DEFAULT NULL,
    ->  `deptId` INT(11) DEFAULT NULL,
    ->  PRIMARY KEY (`id`),
    ->  KEY `fk_dept_id` (`deptId`)
    ->  #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `tbl_dept` (`id`)
    -> ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

插入一些测试数据:

mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('技术部',11);
Query OK, 1 row affected (0.07 sec)

mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('美工部',12);
Query OK, 1 row affected (0.08 sec)

mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('总裁办',13);
Query OK, 1 row affected (0.06 sec)

mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('人力资源',14);
Query OK, 1 row affected (0.11 sec)

mysql> INSERT INTO tbl_dept(deptName,locAdd) VALUES('后勤组',15);
Query OK, 1 row affected (0.10 sec)

mysql> insert into tbl_emp(name,deptId) values('jack',1);
Query OK, 1 row affected (0.11 sec)

mysql> insert into tbl_emp(name,deptId) values('tom',1);
Query OK, 1 row affected (0.08 sec)

mysql> insert into tbl_emp(name,deptId) values('alice',2);
Query OK, 1 row affected (0.08 sec)

mysql> insert into tbl_emp(name,deptId) values('john',3);
Query OK, 1 row affected (0.13 sec)

mysql> insert into tbl_emp(name,deptId) values('faker',4);
Query OK, 1 row affected (0.10 sec)

mysql> insert into tbl_emp(name) values('mlxg');
Query OK, 1 row affected (0.13 sec)

mysql> select * from tbl_dept;
+----+----------+--------+
| id | deptName | locAdd |
+----+----------+--------+
|  1 | 技术部   | 11     |
|  2 | 美工部   | 12     |
|  3 | 总裁办   | 13     |
|  4 | 人力资源 | 14     |
|  5 | 后勤组   | 15     |
+----+----------+--------+
5 rows in set (0.00 sec)

mysql> select * from tbl_emp;
+----+-------+--------+
| id | name  | deptId |
+----+-------+--------+
|  1 | jack  |      1 |
|  2 | tom   |      1 |
|  3 | alice |      2 |
|  4 | john  |      3 |
|  5 | faker |      4 |
|  7 | ning  |   NULL |
|  8 | mlxg  |   NULL |
+----+-------+--------+
7 rows in set (0.00 sec)

两表的关联关系如图所示:

6aa5601125ea5193281740716c4f43f1.png

1、左连接(A独有+AB共有)

查询所有部门以及各部门的员工数:

mysql> select t1.id,t1.deptName,count(t2.name) as emps from tbl_dept t1 left join tbl_emp t2 on t2.deptId=t1.id group by deptName order by id;
+----+----------+------+
| id | deptName | emps |
+----+----------+------+
|  1 | 技术部   |    2 |
|  2 | 美工部   |    1 |
|  3 | 总裁办   |    1 |
|  4 | 人力资源 |    1 |
|  5 | 后勤组   |    0 |
+----+----------+------+
5 rows in set (0.00 sec)

2、右连接(B独有+AB共有)

查询所有员工及其所属部门:

mysql> select t2.id,t2.name,t1.deptName from tbl_dept t1 right join tbl_emp t2 on t2.deptId=t1.id;
+----+-------+----------+
| id | name  | deptName |
+----+-------+----------+
|  1 | jack  | 技术部   |
|  2 | tom   | 技术部   |
|  3 | alice | 美工部   |
|  4 | john  | 总裁办   |
|  5 | faker | 人力资源 |
|  7 | ning  | NULL     |
|  8 | mlxg  | NULL     |
+----+-------+----------+
7 rows in set (0.04 sec)

3、内连接(AB共有)

查询两表共有的数据:

mysql> select deptName,t2.name empName from tbl_dept t1 inner join tbl_emp t2 on t1.id=t2.deptId;
+----------+---------+
| deptName | empName |
+----------+---------+
| 技术部   | jack    |
| 技术部   | tom     |
| 美工部   | alice   |
| 总裁办   | john    |
| 人力资源 | faker   |
+----------+---------+

4、A独有

即在(A独有+AB共有)的基础之上排除B即可(通过b.id is null即可实现):

mysql> select a.deptName,b.name empName from tbl_dept a left join tbl_emp b on a.id=b.deptId where b.id is null;
+----------+---------+
| deptName | empName |
+----------+---------+
| 后勤组   | NULL    |
+----------+---------+

5、B独有

与(A独有)同理:

mysql> select a.name empName,b.deptName from tbl_emp a left join tbl_dept b on a.deptId=b.id where b.id is null;
+---------+----------+
| empName | deptName |
+---------+----------+
| ning    | NULL     |
| mlxg    | NULL     |
+---------+----------+

6、A独有+B独有

使用union将(A独有)和(B独有)联合在一起:

mysql> select a.deptName,b.name empName from tbl_dept a left join tbl_emp b on a.id=b.deptId where b.id is null union select b.deptName,a.name emptName from tbl_emp a left join tbl_dept b on a.deptId=b.id where b.id is null;
+----------+---------+
| deptName | empName |
+----------+---------+
| 后勤组   | NULL    |
| NULL     | ning    |
| NULL     | mlxg    |
+----------+---------+

7、A独有+AB公共+B独有

使用union(可去重)联合(A独有+AB公共)和(B独有+AB公共)

mysql> select a.deptName,b.name empName  from tbl_dept a left join tbl_emp b on a.id=b.deptId union select a.deptName,b.name empName from tbl_dept a right join tbl_emp b on a.id=b.deptId;
+----------+---------+
| deptName | empName |
+----------+---------+
| 技术部   | jack    |
| 技术部   | tom     |
| 美工部   | alice   |
| 总裁办   | john    |
| 人力资源 | faker   |
| 后勤组   | NULL    |
| NULL     | ning    |
| NULL     | mlxg    |
+----------+---------+

索引与数据处理

什么是索引?

索引是一种数据结构,在插入一条记录时,它从记录中提取(建立了索引的字段的)字段值作为该数据结构的元素,该数据结构中的元素被有序组织,因此在建立了索引的字段上搜索记录时能够借助二分查找提高搜索效率;此外每个元素还有一个指向它所属记录(数据库表记录一般保存在磁盘上)的指针,因此索引与数据库表的关系可类比于字典中目录与正文的关系,且目录的篇幅(索引所占的存储空间存储空间)很小。

数据库中,常用的索引数据结构是BTree(也称B-Tree,即Balance Tree,多路平衡查找树。Binary Search Tree平衡搜索二叉树是其中的一个特例)。

建立索引之后为什么快?

索引是大文本数据的摘要,数据体积小,且能二分查找。这样我们在根据建立了索引的字段搜索时:其一,由表数据变为了索引数据(要查找的数据量显著减小);其二,索引数据是有序组织的,搜索时间复杂度由线性的O(N)变成了O(logN)(这是很可观的,意味着线性的2^32次操作被优化成了32次操作)。

MySQL常用索引类型

  • 主键索引(primary key),只能作用于一个字段(列),字段值不能为null且不能重复。

  • 唯一索引(unique key),只能作用于一个字段,字段值可以为null但不能重复

  • 普通索引(key),可以作用于一个或多个字段,对字段值没有限制。为一个字段建立索引时称为单值索引,为多个字段同时建立索引时称为复合索引(提取多个字段值组合而成)。

测试唯一索引的不可重复性和可为null:

mysql> create table `student` (
    -> `id` int(10) not null auto_increment,
    -> `stuId` int(32) default null,
    -> `name` varchar(100) default null,
    -> primary key(`id`),
    -> unique key(`stuId`)
    -> ) engine=innodb auto_increment=1 default charset=utf8;

mysql> insert into student(stuId,name) values('123456789','jack');
Query OK, 1 row affected (0.10 sec)
mysql> insert into student(stuId,name) values('123456789','tom');
ERROR 1062 (23000): Duplicate entry '123456789' for key 'stuId'

mysql> insert into student(stuId,name) values(null,'tom');
Query OK, 1 row affected (0.11 sec)

索引管理

创建索引

创建表(DDL)时创建索引

mysql> create table `student` (
    -> `id` int(10) not null auto_increment,
    -> `stuId` int(32) default null,
    -> `name` varchar(100) default null,
    -> primary key(`id`),
    -> unique key(`stuId`)
    -> ) engine=innodb auto_increment=1 default charset=utf8;

创建索引语句:create [unique] index on (,...)

mysql> create index idx_name on student(name);
Query OK, 0 rows affected (0.44 sec)
Records: 0  Duplicates: 0  Warnings: 0

更改表结构语句:alter table add [unique] index on (,....)

mysql> drop index idx_name on student;
Query OK, 0 rows affected (0.27 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table student add index idx_name(name);
Query OK, 0 rows affected (0.32 sec)
Records: 0  Duplicates: 0  Warnings: 0

删除索引

drop index on

查看索引

SHOW INDEX FROM

b0fe766b75d393008b3beb62395d76f5.png

SQL执行计划——Explain

使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。

能干嘛

通过EXPLAIN分析某条SQL语句执行时的如下特征:

  • 表的读取顺序(涉及到多张表时)

  • 数据读取操作的操作类型

  • 哪些索引可以使用

  • 哪些索引被实际使用

  • 表之间的引用

  • 每张表有多少行被优化器查询

怎么玩

格式为:explain :

733032f2e8a9a56deb2064f4ee31d9f1.png

表头解析

id

select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序。根据id是否相同可以分为下列三种情况:

  • 所有表项的id相同,如:

    f44c74e4ae34e066bc3ae73f4e9a0b11.png

    则上表中的3个表项按照从上到下的顺序执行,如读表顺序为t1,t3,t2。由第一节提到的SQL解析顺序也可验证,首先from t1,t2,t3表明此次查询设计到的表,由于没有join,接着解析where时开始读表,值得注意的是并不是按照where书写的顺序,而是逆序,即先解析t1.other_column=''于是读表t1,然后t1.id=t3.id读表t3,最后t1.id=t2.id读表t2。解析顺序如下:

from 
    t1,t2,t3
where
    t1.other_column='', t1.id=t3.id, t1.id=t2.id
select
    t2.*
  • 所有表项的id不同:嵌套查询,id的序号会递增,id值越大优先级越高,越先被执行。如:

    aaafb17adc92b047c2fb1256c5e3d2cb.png

    对于多层嵌套的查询,执行顺序由内而外。解析顺序:

from 
    t2
where
    t2.id=
    from
        t1
    where
        t1.id=
        from 
            t3
        where
            t3.other_column=''
        select
            t3.id
    select 
        t1.id
select 
    t2.*
  • 由第12,8,4行可知查表顺序为t3,t1,t2。

  • 有的表项id相同,有的则不同。id相同的表项遵循结论1,不同的则遵循结论2

    7801ebb0ba91522e384e01d81b52576f.png

    解析顺序:

from
    (
    from
        t3
    where
        t3.other_column=''
    select
        t3.id
     ) s1, t2    #s1是衍生表
where
    s1.id=t2.id
select
    t2.*
  • 由第6,11两行可以看出读表顺序为t3,s1,t2

select_type

该列常出现的值如下:

  • SIMPLE,表示此SQL是简单的select查询,查询中不包含子查询或者union

    621ffd4cd117133bd32eb10ece1d6984.png
  • PRIMARY,查询中若包含任何复杂的子部分,最外层查询被标记为PRIMARY

    c79efab8f599b1d578ab29bae69749ee.png
  • SUBQUERY,在select或where列表中包含的子查询

  • DERIVED,在from子句中的子查询被标记为DERIVED(衍生)。MySQL会递归执行这些子查询, 把结果放在临时表里

  • UNION,union右侧的select

  • UNION RESULT,union的结果

table

表名,表示该表项是关于哪张表的,也可以是如形式:

  • ,表示该表是表项id为N的衍生表

  • ,表示该表是表项id为M和N两者union之后的结果

partition

如果启用了表分区策略,则该字段显示可能匹配查询的记录所在的分区

type

type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 。

  • system,表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计

  • const,表示通过索引一次就找到了,const用于比较primary key或者unique key。因为只匹配一行数据,所以很快。若将主键置于where列表中,MySQL就能将该查询转换为一个常量

2d21b3d98866037c4ae5e928b4fd8ee5.png
mysql> select * from student;
+----+-----------+------+
| id | stuId     | name |
+----+-----------+------+
|  1 | 123456789 | jack |
|  3 |      NULL | tom  |
+----+-----------+------+
c5553f298e181b3a5135fe5b60522ad4.png

eq_ref,唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描

1f1942cb455088548b67dd52fa58843d.png

对于b中的每一条数据,从a的主键索引中查找id和其相等的

  • ref,非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体。(查找是基于有序性的能利用二分,而扫描则是线性的)

  • mysql> create table `person` (
        ->  `id` int(32) not null auto_increment,
        ->  `firstName` varchar(30) default null,
        ->  `lastName` varchar(30) default null,
        ->  primary key(`id`),
        ->  index idx_name (firstName,lastName)
        -> ) engine=innodb auto_increment=1 default charset=utf8;
    • 查询姓张的人:

      51d5981e09e5411a65101ae92b6e07ad.png
    • range,根据索引的有序性检索特定范围内的行,通常出现在between、、in等范围检索中

      c474708d9e8ab2948e33ab6ea8877d6f.png
    • index,在索引中扫描,只需读取索引数据。

      由于复合索引idx_name是基于(firstName,lastName)的,这种索引只能保证在整体上是按定义时的第一列(即firstName)有序的,当firstName相同时,再按lastName排序,如果不只两列则以此类推。也就是说在根据lastName查找时是无法利用二分的,只能做全索引扫描。

    • all,全表扫描,需要从磁盘上读取表数据。

备注:一般来说,得保证查询至少达到range级别,最好能达到ref。

possible_keys

MySQL可以利用以快速检索行的索引。

key

MySQL执行时实际使用的索引。

key_len

  • 表示索引中每个元素最大字节数,可通过该列计算查询中使用的索引的长度(如何计算稍后详细结束)。

    在不损失精确性的情况下,长度越短越好。

  • key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。

如何计算?首先我们要了解MySQL各数据类型所占空间:

  • 数值类型

    8af71bed777b2611c981b6ae3a6c6d61.png
  • 日期类型(datetime类型在MySQL5.6中字段长度是5个字节,在5.5中字段长度是8个字节)

    8af71bed777b2611c981b6ae3a6c6d61.png
  • 字符串类型

    latin1编码的字符占1个字节,gbk编码的字符占2个字节,utf8编码的字符占3个字节。

    c1 char(10)表示每行记录的c1字段固定占用10个字节;而c2 varchar(10)则不一定,如果某数据行的c2字段值只占3个字节,那么该数据行的c2字段实际占5个字节,因为该类型字段所占空间大小是可变的,所以需要额外2个字节来保存字段值的长度,并且因为varchar最大字节数为65535,因此字段值最多占65533个字节。

    因此,

    • 如果事先知道某字段存储的数据都是固定个数的字符则优先使用char以节省存储空间。

    • 尽量设置not null并将默认值设为‘’或0

以字符串类型字段的索引演示key_len的计算过程(以utf8编码为例):

  • 索引字段为char类型 + not null:key_len = 字段申明字符个数 * 3(utf8编码的每个字符占3个字节)

  • mysql> create table test(
        -> id int(10) not null auto_increment,
        -> primary key(id)
        -> ) engine=innodb auto_increment=1 default charset=utf8;

    mysql> alter table test add c1 char(10) not null;

    mysql> create index idx_c1 on test(c1);
  • d4bd2ca53b80c2f3acbedddfa23fec04.png
  • 索引字段为char类型 + 可以为null:key_len = 字段申明字符个数 * 3 + 1(单独用一个字节表示字段值是否为null)

  • mysql> alter table test add c2 char(10) default null;

    mysql> create index idx_c2 on test(c2);
  • 索引字段为varchar + not null,key_len = 字段申明字符个数 * 3 + 2(用来保存字段值所占字节数)

    mysql> alter table test add c3 varchar(10) not null;

    mysql> create index idx_c3 on test(c3);
  • 63a1c961030f8cfe0ca22a2df9f29f0d.png
  • varchar + 可以为null,key_len = 字段申明字符个数 * 3 + 2 + 1(用来标识字段值是否为null)

根据这个值,就可以判断索引使用情况,特别是在使用复合索引时判断组成该复合索引的多个字段是否都能被查询用到。

如:

mysql> desc person;
+-----------+-------------+------+-----+---------+----------------+
| Field     | Type        | Null | Key | Default | Extra          |
+-----------+-------------+------+-----+---------+----------------+
| id        | int(32)     | NO   | PRI | NULL    | auto_increment |
| firstName | varchar(30) | YES  | MUL | NULL    |                |
| lastName  | varchar(30) | YES  |     | NULL    |                |
+-----------+-------------+------+-----+---------+----------------+
da22004fe41c8573b22489df5df1bfd6.png
05fd076eff6e78250dd83e37075dd820.png

前者使用了部分复合索引,而后者使用了全部,这在索引类型一节中也提到过,是由最左前缀(定义复合索引时的第一列 )有序这一特性决定的。

ref

显示哪一列或常量被拿来与索引列进行比较以从表中检索行。

68f30190b4ea944d222e90b013f912d2.png

如上我们使用‘’到索引中检索行。

rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

Extra

包含不适合在其他列中显示但十分重要的额外信息:

  • Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”

mysql> explain select * from person order by lastName\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: person
   partitions: NULL
         type: index
possible_keys: NULL
          key: idx_name
      key_len: 186
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using index; Using filesort

使用\G代替;结尾可以使执行计划垂直显示。

mysql> explain select * from person order by firstName,lastName\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: person
   partitions: NULL
         type: index
possible_keys: NULL
          key: idx_name
      key_len: 186
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using index

Using temporary:使用了临时表保存中间结果。MySQL在对查询结果聚合时使用临时表。常见于排序 order by 和分组查询 group by。

  • mysql> insert into person(firstName,lastName) values('张','三');

    mysql> insert into person(firstName,lastName) values('李','三');

    mysql> insert into person(firstName,lastName) values('王','三');

    mysql> insert into person(firstName,lastName) values('李','明');

    mysql> select lastName,count(lastName) from person group by lastName;
    +----------+-----------------+
    | lastName | count(lastName) |
    +----------+-----------------+
    | 三       |               3 |
    | 明       |               1 |
    +----------+-----------------+

    mysql> explain select lastName,count(lastName) from person group by lastName\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: person
       partitions: NULL
             type: index
    possible_keys: idx_name
              key: idx_name
          key_len: 186
              ref: NULL
             rows: 4
         filtered: 100.00
            Extra: Using index; Using temporary; Using filesort
  • Using index:表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行(需要读磁盘),效率不错!如果同时出现Using where,表明索引被用来执行索引键值的查找;如果没有同时出现Using where,表明索引用来读取数据而非执行查找动作。

    索引覆盖:就是select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。

    如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。

  • Using where:查询使用到了where语句

  • Using join buffer:使用了连接缓存

  • Impossible where:where子句的值总是false,如

  • select * from person where id=1 and id=2;

索引失效

如果使用explain分析SQL的执行计划时发现访问类型type为ALL或实际使用到的索引key为NULL,则说明该查询没有利用索引而导致了全表扫描,这是我们需要避免的。以下总结了利用索引的一些原则:

1、全值匹配我最爱

根据常量在索引字段上检索时一定能够利用到索引。

afb01a79d135be4fa42aed4e972e24fb.png

这种方式

2、最佳左前缀法则

对于复合索引检索时一定要遵循左前缀列在前的原则。

mysql> alter table test add c5 varchar(10) default null, add c6 varchar(10) default null, add c7 varchar(10) default null;

mysql> create index idx_c5_c6_c7 on test(c5,c6,c7);

如果没有左前缀列则不会利用索引:

mysql> explain select * from test where c6=''\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: test
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where

mysql> explain select * from test where c6='' and c7=''\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: test
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where

而只要最左前缀列在前,其他列可以不按顺序也可以不要,但最好不要那么做(按照定义复合索引时的列顺序能达到最佳效率):

mysql> explain select * from test where c5='' and c7=''\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: test
   partitions: NULL
         type: ref
possible_keys: idx_c5_c6_c7
          key: idx_c5_c6_c7
      key_len: 33
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using index condition
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from test where c5='' and c7='' and c6=''\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: test
   partitions: NULL
         type: ref
possible_keys: idx_c5_c6_c7
          key: idx_c5_c6_c7
      key_len: 99
          ref: const,const,const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

最优的做法是:

mysql> explain select * from test where c5=''\G

mysql> explain select * from test where c5='' and c6=''\G

mysql> explain select * from test where c5='' and c6='' and c7=''\G

3、不在列名上添加任何操作

有时我们会在列名上进行计算、函数运算、自动/手动类型转换,这会直接导致索引失效。

mysql> explain select * from person where left(firstName,1)='张'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: person
   partitions: NULL
         type: index
possible_keys: NULL
          key: idx_name
      key_len: 186
          ref: NULL
         rows: 4
     filtered: 100.00
        Extra: Using where; Using index

mysql> explain select * from person where firstName='张'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: person
   partitions: NULL
         type: ref
possible_keys: idx_name
          key: idx_name
      key_len: 93
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using index

上面两条SQL同样是实现查找姓张的人,但在列名firstName上使用了left函数使得访问类型type从ref(非唯一性索引扫描)降低到了index(全索引扫描)

4、存储引擎无法使用索引中范围条件右边的列

0ecf610dd7a41b78d877361c0a41dfbf.png

由上图可知c6 > ‘a’右侧的列c7虽然也在复合索引idx_c5_c6_c7中,但由key_len:66可知其并未被利用上。通常索引利用率越高,查找效率越高。

5、尽量使用索引覆盖

尽量使查询列和索引列保持一致,这样就能避免访问数据行而直接返回索引数据。避免使用select *除非表数据很少,因为select *很大概率访问数据行。

47b2157a1452230c0132fe5a36c319d4.png

Using index表示发生了索引覆盖

6、使用 != 或 <> 时可能会导致索引失效

fb0153f17d9ab6506a301b414ca85694.png

7、not null对索引也有影响

3653b1e66c92f2bb3423717198a3bf7d.png
2ebf45d41ad362dac3bc0df8e86c3195.png

若name的定义不是not null则不会有索引未利用的情况。

8、like以通配符开头会导致索引失效

like语句以通配符%开头无法利用索引会导致全索引扫描,而只以通配符结尾则不会。

5f8009c6db823469458be0f304eb5eee.png

9、join on的列只要有一个没索引则全表扫描

4321908344cda0f0aa579aaa31f55b76.png

10、or两侧的列只要有一个没索引则全表扫描

6d1a0d8c32f179226da85e2a166b9980.png

11、字符串不加单引号索引失效

mysql> explain select * from staff where name=123;

打油诗:

全值匹配我最爱,最左前缀要遵循。

带头大哥不能死,中间兄弟不能断。

索引列上少计算,范围之后全失效。

LIKE百分比最右,覆盖索引不写*。

不等空值还有OR,ON的右侧要注意。

VAR引号不能丢,SQL优化有诀窍。

Anwen

https://juejin.im/post/5cf3f8b6f265da1bc94ed736

7b4611b9c99c733e7c5aef003f3339a4.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368398.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Apache Camel发布/订阅模式

发布/订阅是一种简单的消息传递模式&#xff0c;其中&#xff0c;发布者将消息发送到某个频道&#xff0c;而无需知道谁将接收消息。 然后&#xff0c;通道负责将消息的副本传递给每个订户。 此消息传递模型允许创建松耦合和可伸缩的系统。 这是一种非常常见的消息传递模式&am…

移动端(H5)弹框组件--简单--实用--不依赖jQuery

俗话说的好&#xff0c;框架是服务与大家的&#xff0c;包含的功能比较多&#xff0c;代码多。在现在追求速度的年代。应该根据自己的需求去封装自己所需要的组件。 下边就给大家介绍一下自己封装的一个小弹框组件&#xff0c;不依赖与jQuery&#xff0c;代码少&#xff0c;适…

c++ for循环 流程图_python 零基础必知--条件控制与循环语句

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理本文章来自腾讯云 作者&#xff1a;somenzz如果没有 if 语句和循环语句&#xff0c;请问你怎么编程&#xff1f;Python 中的条件控制和循环语句都非常简单&…

前端总结·基础篇·CSS(二)视觉

前端总结系列 前端总结基础篇CSS&#xff08;一&#xff09;布局前端总结基础篇CSS&#xff08;二&#xff09;视觉前端总结基础篇CSS&#xff08;三&#xff09;补充前端总结基础篇JS&#xff08;一&#xff09;原型、原型链、构造函数和字符串&#xff08;String&#xff09;…

【kindle笔记】之 《浪潮之巅》- 2018-1-

《浪潮之巅》 这本书推荐自最爱的政治课老师。 政治课老师张巍老师。我会一直记得你的。 以这样的身份来到这个学校&#xff0c;他人的质疑&#xff0c;自己的忐忑&#xff0c;老板的不公。犹犹豫豫谨小慎微地前进。 第一次听到这样的话是从您口中&#xff1a; 在座的诸位&…

Secure CRT 自动记录日志log配置

SecureCRT8.0的下载地址下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1i5q09qH 密码&#xff1a;4pa2 配置自动log操作如下&#xff1a; 1.options ---> Session Options 2. 设置log 文件属性 点击 日志 &#xff0c;在选项框中 Log file name中填入路径和…

禁用mysql的sleep函数_MySQL的sleep函数的特殊特现象

MySQL中的系统函数sleep&#xff0c;实际应用的场景不多&#xff0c;一般用来做实验测试&#xff0c;昨天在测试的时候&#xff0c;意外发现sleep函数的一个特殊现象。如果在查询语句中使用sleep函数&#xff0c;那么休眠的时间跟返回的记录有关。如下测试所示&#xff1a;mysq…

amoeba mysql读写分离_Mysql 实现读写分离的详细教程(amoeba)

Mysql 实现读写分离的详细教程(amoeba)发布时间&#xff1a;2018-08-17作者&#xff1a;laosun阅读(2220)继上篇文章&#xff0c;mysql实现主从配置之后的读写分离实现方式&#xff0c;文章地址。amoeba是盛大架构师陈思儒独立完成&#xff0c;除此之外还有很多中间件&#xff…

十道海量数据处理面试题与十个方法大总结

1. 给定a、b两个文件&#xff0c;各存放50亿个url&#xff0c;每个url各占64字节&#xff0c;内存限制是4G&#xff0c;让你找出a、b文件共同的url&#xff1f; 方案1&#xff1a;可以估计每个文件安的大小为50G64320G&#xff0c;远远大于内存限制的4G。所以不可能将其完全加载…

Spring集成文件轮询和测试

我最近实施了一个小项目&#xff0c;在该项目中&#xff0c;我们必须轮询文件夹中的新文件&#xff0c;然后在文件内容上触发服务流。 Spring Integration非常适合此要求&#xff0c;因为它带有一个通道适配器 &#xff0c;该适配器可以扫描文件夹中的新文件&#xff0c;然后通…

Spark参数配置总结

转载于:https://www.cnblogs.com/lz3018/p/8128017.html

eclipse mysql生成实体类_Eclipse实现数据库反向生成实体类(pojo)-------(插件安装和实现步骤的说明)...

一、插件安装1.下载插件&#xff1a;http://jaist.dl.sourceforge.net/sourceforge/jboss/HibernateTools-3.2.4.Beta1-R200810311334.zip2.解压压缩包分别将其中的features和plugins放到Eclipse安装目录下对应的这2个文件里&#xff0c;重启Eclipse使其生效3.随便建个project&…

一些小技巧-重构

用box-shadow制造浮雕效果用box-shadow做简单的背景修饰长页面背景图不够用...... 01 用box-shadow制造浮雕效果 demo示例&#xff1a; src"http://demo.zhangruojun.com/static/demo/demo001/" frameborder"0" width"414" height"650&qu…

VM虚拟机显示不能铺满问题

关于使用虚拟机&#xff08;VMware&#xff09;时桌面显示不能铺满整个窗口时的设置操作&#xff1a; 步骤&#xff1a;虚拟机菜单下的&#xff1a;编辑-->首选项-->显示&#xff08;如下图&#xff09; 可以根据自己需求设置全屏下面的三个选项&#xff0c;确定后如果不…

Weex系列-项目工程

转载于:https://www.cnblogs.com/hacjy/p/8136460.html

MySQL索引效率对比_mysql下普通索引和唯一索引的效率对比

今天在我的虚拟机中布置了环境&#xff0c;测试抓图如下&#xff1a;抓的这几个都是第一次执行的&#xff0c;刷了几次后&#xff0c;取平均值&#xff0c;效率大致相同&#xff0c;而且如果在一个列上同时建唯一索引和普通索引的话&#xff0c;mysql会自动选择唯一索引。谷歌一…

spring以及json,fastjson和jackson

&#xff08;一&#xff09; RestController 以及 RequestBody spring的这两个注解都需要使用对应的 message-converters 实现 pojo到字符串的转换&#xff0c; 需要配置实现了 GenericHttpMessageConverter 接口的实现类GenericHttpMessageConverter 父接口为HttpMessageConve…

turtle 函数 方法_学python第十一节:turtle深入 了解

学python第十一节&#xff1a;深入分析turtleTurtle是一个直观有趣的图形绘制函数。这节课对turtle的以下几点进行补充&#xff1a;在蟒蛇绘制代码中提到过import 库引用保留字的函数&#xff0c;是补充python程序功能的方式&#xff0c;使用2种编写格式&#xff1a; 第一种引用…

intellij idea中解决java.lang.VerifyError: Expecting a stackmap frame at branch target的方法

【实习第三周&#xff0c;被生活逼成了全栈hhhh从开发写到测试】 报错如下&#xff1a; 经过查找各类资料博客&#xff0c;针对不同的情况有不同的解决办法&#xff1a;1. java源代码是用jdk1.6下开发的&#xff0c;后来环境上替换安装了jdk1.7编译运行。运行报错。我的错误不属…

ArrayList使用内存映射文件

介绍 内存中的计算由于负担得起的硬件而开始兴起&#xff0c;大多数数据保留在RAM中以满足延迟和吞吐量的目标&#xff0c;但是将数据保留在RAM中会增加垃圾收集器的开销&#xff0c;尤其是在您不预先分配的情况下。 因此&#xff0c;有效地我们需要一种无垃圾/无垃圾的方法来避…