7.2.4 【MySQL】匹配范围值

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 7.2.4 【MySQL】匹配范围值

回头看我们 idx_name_birthday_phone_number 索引的 B+ 树示意图,所有记录都是按照索引列的值从小到大的顺序排好序的,所以这极大的方便我们查找索引列的值在某个范围内的记录。比方说下边这个查询语句:

SELECT * FROM person_info WHERE name > 'Asa' AND name < 'Barlow';

由于 B+ 树中的数据页和记录是先按 name 列排序的,所以我们上边的查询过程其实是这样的:


找到 name 值为 Asa 的记录。


找到 name 值为 Barlow 的记录。


由于所有记录都是由链表连起来的(记录之间用单链表,数据页之间用双链表),所以他们之间的记录都可以很容易的取出来喽~


找到这些记录的主键值,再到 聚簇索引 中 回表 查找完整的记录。


不过在使用联合进行范围查找的时候需要注意,如果对多个列同时进行范围查找的话,只有对索引最左边的那个列进行范围查找的时候才能用到 B+ 树索引,比方说这样:

FROM person_info WHERE name > 'Asa' AND name < 'Barlow' AND birthday > '1980-01-01';

上边这个查询可以分成两个部分:


1. 通过条件 name > 'Asa' AND name < 'Barlow' 来对 name 进行范围,查找的结果可能有多条 name 值不同的记录,


2. 对这些 name 值不同的记录继续通过 birthday > '1980-01-01' 条件继续过滤。


这样子对于联合索引 idx_name_birthday_phone_number 来说,只能用到 name 列的部分,而用不到 birthday 列的部分,因为只有 name 值相同的情况下才能用 birthday 列的值进行排序,而这个查询中通过 name 进行范围查找的记录中可能并不是按照 birthday 列进行排序的,所以在搜索条件中继续以 birthday 列进行查找时是用不到这个 B+ 树索引的。

7.2.5 精确匹配某一列并范围匹配另外一列

对于同一个联合索引来说,虽然对多个列都进行范围查找时只能用到最左边那个索引列,但是如果左边的列是精确查找,则右边的列可以进行范围查找,比方说这样:

SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday > '1980-01-01' AND birthd

这个查询的条件可以分为3个部分:


1. name = 'Ashburn' ,对 name 列进行精确查找,当然可以使用 B+ 树索引了。


2. birthday > '1980-01-01' AND birthday < '2000-12-31' ,由于 name 列是精确查找,所以通过 name ='Ashburn' 条件查找后得到的结果的 name 值都是相同的,它们会再按照 birthday 的值进行排序。所以此时对 birthday 列进行范围查找是可以用到 B+ 树索引的。


3. phone_number > '15100000000' ,通过 birthday 的范围查找的记录的 birthday 的值可能不同,所以这个条件无法再利用 B+ 树索引了,只能遍历上一步查询得到的记录。


同理,下边的查询也是可能用到这个 idx_name_birthday_phone_number 联合索引的:

SELECT * FROM person_info WHERE name = 'Ashburn' AND birthday = '1980-01-01' AND AND p

7.2.6 用于排序

在写查询语句的时候经常需要对查询出来的记录通过 ORDER BY 子句按照某种规则进行排序。一般情况下,我们只能把记录都加载到内存中,再用一些排序算法,比如快速排序、归并排序等等在内存中对这些记录进行排序,有的时候可能查询的结果集太大以至于不能在内存中进行排序的话,还可能暂时借助磁盘的空间来存放中间结果,排序操作完成后再把排好序的结果集返回到客户端。在 MySQL 中,把这种在内存中或者磁盘上进行排序的方式统称为文件排序(英文名: filesort ),跟 文件 这个词儿一沾边儿,就显得这些排序操作非常慢了(磁盘和内存的速度比起来,就像是飞机和蜗牛的对比)。但是如果 ORDER BY 子句里使用到了我们的索引列,就有可能省去在内存或文件中排序的步骤,比如下边这个简单的查询语句:

SELECT * FROM person_info ORDER BY name, birthday, phone_number LIMIT 10;

这个查询的结果集需要先按照 name 值排序,如果记录的 name 值相同,则需要按照 birthday 来排序,如果birthday 的值相同,则需要按照 phone_number 排序。大家可以回过头去看我们建立的idx_name_birthday_phone_number 索引的示意图,因为这个 B+ 树索引本身就是按照上述规则排好序的,所以直接从索引中提取数据,然后进行 回表 操作取出该索引中不包含的列就好了。

7.2.6.1 使用联合索引进行排序注意事项

对于 联合索引 有个问题需要注意, ORDER BY 的子句后边的列的顺序也必须按照索引列的顺序给出,如果给出ORDER BY phone_number, birthday, name 的顺序,那也是用不了 B+ 树索引,这种颠倒顺序就不能使用索引的。


同理, ORDER BY name 、 ORDER BY name, birthday 这种匹配索引左边的列的形式可以使用部分的 B+ 树索引。当联合索引左边列的值为常量,也可以使用后边的列进行排序,比如这样:

SELECT * FROM person_info WHERE name = 'A' ORDER BY birthday, phone_number LIMIT 10;

这个查询能使用联合索引进行排序是因为 name 列的值相同的记录是按照 birthday , phone_number 排序的。

7.2.6.2 不可以使用索引进行排序的几种情况

ASC、DESC混用


对于使用联合索引进行排序的场景,我们要求各个排序列的排序顺序是一致的,也就是要么各个列都是 ASC 规则排序,要么都是 DESC 规则排序。


想想这个 idx_name_birthday_phone_number 联合索引中记录的结构:


先按照记录的 name 列的值进行升序排列。


如果记录的 name 列的值相同,再按照 birthday 列的值进行升序排列。


如果记录的 birthday 列的值相同,再按照 phone_number 列的值进行升序排列。


如果查询中的各个排序列的排序顺序是一致的,比方说下边这两种情况:

ORDER BY name, birthday LIMIT 10

这种情况直接从索引的最左边开始往右读10行记录就可以了。

ORDER BY name DESC, birthday DESC LIMIT 10 ,

这种情况直接从索引的最右边开始往左读10行记录就可以了。

但是如果我们查询的需求是先按照 name 列进行升序排列,再按照 birthday 列进行降序排列的话,比如说这样的查询语句:

SELECT * FROM person_info ORDER BY name, birthday DESC LIMIT 10;

这样如果使用索引排序的话过程就是这样的:


先从索引的最左边确定 name 列最小的值,然后找到 name 列等于该值的所有记录,然后从 name 列等于该值的最右边的那条记录开始往左找10条记录。


如果 name 列等于最小的值的记录不足10条,再继续往右找 name 值第二小的记录,重复上边那个过程,直到找到10条记录为止。


WHERE子句中出现非排序使用到的索引列


如果WHERE子句中出现了非排序使用到的索引列,那么排序依然是使用不到索引的,比方说这样:

SELECT * FROM person_info WHERE country = 'China' ORDER BY name LIMIT 10;

这个查询只能先把符合搜索条件 country = 'China' 的记录提取出来后再进行排序,是使用不到索引。注意和下边这个查询作区别:

SELECT * FROM person_info WHERE name = 'A' ORDER BY birthday, phone_number LIMIT 10;

虽然这个查询也有搜索条件,但是 name = 'A' 可以使用到索引 idx_name_birthday_phone_number ,而且过滤剩下的记录还是按照 birthday 、 phone_number 列排序的,所以还是可以使用索引进行排序的。


排序列包含非同一个索引的列

有时候用来排序的多个列不是一个索引里的,这种情况也不能使用索引进行排序,比方说:

SELECT * FROM person_info ORDER BY name, country LIMIT 10;

name 和 country 并不属于一个联合索引中的列,所以无法使用索引进行排序。

排序列使用了复杂的表达式

要想使用索引进行排序操作,必须保证索引列是以单独列的形式出现,而不是修饰过的形式,比方说这样:

SELECT * FROM person_info ORDER BY UPPER(name) LIMIT 10;

使用了 UPPER 函数修饰过的列就不是单独的列,这样就无法使用索引进行排序。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
存储 NoSQL 关系型数据库
7.4.4 【MySQL】索引字符串值的前缀
7.4.4 【MySQL】索引字符串值的前缀
66 0
|
2月前
|
SQL 关系型数据库 MySQL
MySQL 查询某个字段含有字母数字的值
MySQL 查询某个字段含有字母数字的值
77 0
|
7月前
|
关系型数据库 MySQL
mysql中判断NULL和空字符串
mysql中判断NULL和空字符串
49 0
|
关系型数据库 MySQL
Mysql匹配字符类
Mysql匹配字符类
93 2
|
SQL 关系型数据库 MySQL
Mysql拼接字段
Mysql拼接字段
142 0
|
SQL 关系型数据库 MySQL
MySql匹配特定的字符
MySql匹配特定的字符
88 0
|
关系型数据库 MySQL
MySql范围值检查
MySql范围值检查
81 0
|
关系型数据库 MySQL
mysql 字段判断是否包含某个字符串
• 整体数据 • 查询support_goods_level中包含1或者2的数据 • FIND_IN_SET(str,strlist) • 查询结果 • LOCATE(substr,str) • 查询结果 • 非期望情况 • POSITION(substr IN str) • 查询结果 • 非期望情况 • INSTR(str,substr) • 查询结果 • 非期望情况
mysql 字段判断是否包含某个字符串
|
存储 SQL 关系型数据库
MySQL存储的字段是不区分大小写的,你知道吗?
00 简单回顾 之前写过一篇关于mysql 对表大小写敏感的问题,其实在mysql中字段存储的内容是不区分大小写的,本篇进行简单的总结。 想回顾一下: MySQL在Linux下数据库名、表名、列名、别名大小写规则是这样的: 1、数据库名与表名是严格区分大小写的; 2、表的别名是严格区分大小写的; 3、列名与列的别名在所有的情况下均是忽略大小写的; 4、字段内容默认情况下是大小写不敏感的。 01 一个例子 简单例子: CREATE TABLE `tb_user` ( `id` BIGINT (20) UNSIGNED NOT NULL AUTO_INCREMENT COMM
1128 0
|
SQL 关系型数据库 MySQL
mysql 替换字段中的部分字符,mysql替换字符串,mysql 替换字段内容,mysql replace
mysql 替换字段中的部分字符,mysql替换字符串,mysql 替换字段内容,mysql replace
273 0