COUNT

COUNT(*) 和 COUNT(1)

根据 MySQL 官方文档的描述:

InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.

This optimization only applies to MyISAM tables, because an exact row count is stored for this storage engine and can be accessed very quickly. COUNT(1) is only subject to the same optimization if the first column is defined as NOT NULL.

也就是说,在 InnoDB 中,COUNT(1) 和 COUNT(*) 并没有本质上的区别,在 MyISAM 中,只有在第一行数据不为 NULL 的时候,COUNT(1) 才会出现性能上的优化(在 MyISAM 中,表的行数被单独存储,因此统计行数无需遍历表,而 InnoDB 因为只存储了大概的行数,COUNT 会遍历整个表)

实际上,SELECT COUNT(2)、COUNT(3)、COUNT('OK I am just count, but the result always be the same.'),结果都是一样的。根据 What is the Difference Between COUNT(*), COUNT(1), COUNT(column name), and COUNT(DISTINCT column name)? 一文指出:

So what does the value in the parenthesis of COUNT() mean? It’s the value that the COUNT() function will assign to every row in the table. The function will then count how many times the asterisk (*) or (1) or (-13) has been assigned. Of course, it will be assigned a number of times that’s equal to the number of rows in the table. In other words, COUNT(1) assigns the value from the parentheses (number 1, in this case) to every row in the table, then the same function counts how many times the value in the parenthesis (1, in our case) has been assigned; naturally, this will always be equal to the number of rows in the table. The parentheses can contain any value; the only thing that won’t work will be leaving the parentheses empty.

COUNT(field) 和 COUNT(DISTINCE field)

COUNT(field) 用于统计该列的所有不为 NULL 的数据,与之不同的是,COUNT(*) 则包含了为 NULL 的数据。

COUNT(DISTINCE field) 正如字面意思,用于统计该列的不重复值得数量。

关于 COUNT(field) 有一个有趣的用法:

SELECT COUNT(CASE WHEN order_price > 1000 THEN 1 END)
AS significant_orders
FROM orders;

这里,当 order_price 大于 1000 时,COUNT 里面的字段被赋值为 1,否则为 NULL,最后结果则仅包含了值为 1 的行。

索引

索引的应用场景
  1. 表在业务中查询较多
  2. 更新频率较低的字段(增删改在存在索引的字段上效率受影响)
  3. 常常出现在 where 语句中的字段
  4. 在 join 查询中出现在 on 语句中的两边字段
索引建立的原则

MySQL索引原理及慢查询优化一文指出建立索引的 5 大原则:

  1. 最左前缀匹配原则,非常重要的原则,mysql 会一直向右匹配直到遇到范围查询 (>、<、between、like) 就停止匹配,比如 a = 1 and b = 2 and c > 3 and d = 4 如果建立 (a,b,c,d) 顺序的索引,d 是用不到索引的,如果建立(a,b,d,c) 的索引则都可以用到,a,b,d 的顺序可以任意调整。

  2. = 和 in 可以乱序,比如 a = 1 and b = 2 and c = 3 建立 (a,b,c) 索引可以任意顺序,mysql 的查询优化器会帮你优化成索引可以识别的形式。

  3. 尽量选择区分度高的列作为索引,区分度的公式是 count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是 1,而一些状态、性别字段可能在大数据面前区分度就是 0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要 join 的字段我们都要求是 0.1 以上,即平均 1 条扫描 10 条记录。

  4. 索引列不能参与计算,保持列“干净”,比如 from_unixtime(create_time) = '2014-05-29' 就不能使用到索引,原因很简单,b+ 树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成 create_time = unix_timestamp('2014-05-29')

  5. 尽量的扩展索引,不要新建索引。比如表中已经有 a 的索引,现在要加 (a,b) 的索引,那么只需要修改原来的索引即可。

使用索引也无法加快查询速度的情况?

原因:

  1. 字段区分度低(重复性高)
  2. SQL 写法问题(范围查询,计算查询等)

针对字段区分度低的案例:

举这个例子的目的在于颠覆我们对列的区分度的认知,一般上我们认为区分度越高的列,越容易锁定更少的记录,但在一些特殊的情况下,这种理论是有局限性的。

select
*
from
stage_poi sp
where
sp.accurate_result=1
and (
sp.sync_status=0
or sp.sync_status=2
or sp.sync_status=4
);

0.先看看运行多长时间,951 条数据 6.22 秒,真的很慢。

951 rows in set (6.22 sec)

1.先 explain,rows 达到了 361 万,type = ALL 表明是全表扫描。

+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | sp | ALL | NULL | NULL | NULL | NULL | 3613155 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+

2.所有字段都应用查询返回记录数,因为是单表查询 0 已经做过了 951 条。

3.让 explain 的 rows 尽量逼近 951。

看一下 accurate_result = 1 的记录数:

select count(*),accurate_result from stage_poi  group by accurate_result;
+----------+-----------------+
| count(*) | accurate_result |
+----------+-----------------+
| 1023 | -1 |
| 2114655 | 0 |
| 972815 | 1 |
+----------+-----------------+

我们看到 accurate_result 这个字段的区分度非常低,整个表只有 -1,0,1 三个值,加上索引也无法锁定特别少量的数据。

再看一下 sync_status 字段的情况:

select count(*),sync_status from stage_poi  group by sync_status;
+----------+-------------+
| count(*) | sync_status |
+----------+-------------+
| 3080 | 0 |
| 3085413 | 3 |
+----------+-------------+

同样的区分度也很低,根据理论,也不适合建立索引。

问题分析到这,好像得出了这个表无法优化的结论,两个列的区分度都很低,即便加上索引也只能适应这种情况,很难做普遍性的优化,比如当 sync_status 0、3 分布的很平均,那么锁定记录也是百万级别的。

4.找业务方去沟通,看看使用场景。业务方是这么来使用这个 SQL 语句的,每隔五分钟会扫描符合条件的数据,处理完成后把 sync_status 这个字段变成 1,五分钟符合条件的记录数并不会太多,1000 个左右。了解了业务方的使用场景后,优化这个 SQL 就变得简单了,因为业务方保证了数据的不平衡,如果加上索引可以过滤掉绝大部分不需要的数据。

5.根据建立索引规则,使用如下语句建立索引

alter table stage_poi add index idx_acc_status(accurate_result,sync_status);

6.观察预期结果,发现只需要 200ms,快了 30 多倍。

952 rows in set (0.20 sec)

我们再来回顾一下分析问题的过程,单表查询相对来说比较好优化,大部分时候只需要把 where 条件里面的字段依照规则加上索引就好,如果只是这种“无脑”优化的话,显然一些区分度非常低的列,不应该加索引的列也会被加上索引,这样会对插入、更新性能造成严重的影响,同时也有可能影响其它的查询语句。所以我们第4步调差SQL的使用场景非常关键,我们只有知道这个业务场景,才能更好地辅助我们更好的分析和优化查询语句。

参考:MySQL索引原理及慢查询优化 - 美团技术团队 (meituan.com)

MySQL 面试题汇总(持续更新中)的更多相关文章

  1. C++常考面试题汇总(持续更新中)

    c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...

  2. 《WCF技术剖析》博文系列汇总[持续更新中]

    原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...

  3. 中国.NET:各地微软技术俱乐部汇总(持续更新中...)

    中国.NET:各地微软技术俱乐部汇总(持续更新中...)   本文是转载文,源地址: https://www.cnblogs.com/panchun/p/JLBList.html by ​史记微软. ...

  4. 【前端面试】Vue面试题总结(持续更新中)

    Vue面试题总结(持续更新中) 题目参考链接 https://blog.csdn.net/weixin_45257157/article/details/106215158 由于已经有很多前辈深造VU ...

  5. 痞子衡嵌入式:史上最强i.MX RT学习资源汇总(持续更新中...)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MX RT学习资源. 类别 资源 简介 官方汇总 i.MXRT产品主页 恩智浦官方i.MXRT产品主页,最权威的资料都在这里,参考手 ...

  6. Mysql注入小tips --持续更新中

    学习Web安全好几年了,接触最多的是Sql注入,一直最不熟悉的也是Sql注入.OWASP中,Sql注入危害绝对是Top1.花了一点时间研究了下Mysql类型的注入. 文章中的tips将会持续更新,先说 ...

  7. [Linux] Linux指令汇总(持续更新中...)

    写在前面: 以前真心没有玩过Linux系统,总感觉整天摆弄Linux的同学都是大牛.如今,在公司里实习需要远程登录Linux服务器,所有的代码都要在开发板上完成,所以被逼无奈也不得不定下心来好好学学L ...

  8. MySql面试题(持续更新)

    1. 左连接,右连接,内连接的概念. 左连接:以左表为主,保留左表的所有数据,并且依次拿每行数据去匹配右表所有行,如果没匹配的,右边表的数据为null. 右连接:以右表为主,保留右表的所有数据,并且依 ...

  9. 基础学习:MYSQL命令大全(持续更新中---最近一次:2019.12.6)

    启动mysql : mysql -hlocalhost -uroot -p创建数据库:create database 数据库名字;指定要操作的数据库:use 数===据库名字;查看数据表建表语句:sh ...

  10. LeetCode All in One 题目讲解汇总(持续更新中...)

    终于将LeetCode的免费题刷完了,真是漫长的第一遍啊,估计很多题都忘的差不多了,这次开个题目汇总贴,并附上每道题目的解题连接,方便之后查阅吧~ 477 Total Hamming Distance ...

随机推荐

  1. [gym102978D]Do Use FFT

    前置知识 (以下内容并不严谨,可以参考论文<转置原理的简单介绍>) 对于一个算法,其为线性算法当且仅当仅包含以下操作: 1.$read\ i$,将$r_{i}$的值赋为(下一个)读入的元素 ...

  2. [cf997E]Good Subsegments

    一个区间为好区间当且仅当$\max_{l\le i\le r}a_{i}-\min_{l\le i\le r}a_{i}=r-l$,考虑固定右端点$r$,维护所有左端点$l$的上述式子左-右的值,那么 ...

  3. form-create 3.0 版本发布,好用的Vue3版本动态表单生成组件

    form-create 是一个可以通过 JSON 生成具有动态渲染.数据收集.验证和提交功能的表单生成组件.支持2个UI框架,并且支持生成任何 Vue 组件.内置20种常用表单组件和自定义组件,再复杂 ...

  4. ASP.NET 使用 Dispose 释放资源的四种方法

    Dispose 和 Finalize 是运行的 .NET 和 .NET Core 应用程序释放占用的资源的两种方法.通常,如果应用程序中有非托管资源,应该显式地释放这些资源占用的资源. 由于 Fina ...

  5. Timer定时器的使用

    import java.util.Timer; import java.util.TimerTask; public class Demo2 { //执行时间,时间单位为毫秒,读者可自行设定,不得小于 ...

  6. 关于"丢失的牛"这个题的教学反思

    某天上课讲到这样一个题:丢失的牛1~n,乱序排列,告诉从第二个位置到最后一个位置, 每个位置的前面的数字中比它小的数的个数,求每个位置的数字是多少N<=8000 FormatInput第一行给出 ...

  7. 【.NET 与树莓派】MPD 的 Mini-API 封装

    在前面的水文中,一方面,老周向各位同学介绍了通过 TCP 连接来访问 MPD 服务:另一方面,也简单演示了 ASP.NET Core 的"极简 API"(Mini API).本篇老 ...

  8. [NOI Online 2021 提高组] 积木小赛

    思路不说了. 想起来自己打比赛的时候,没睡好.随便写了个\(HASH\),模数开小一半分都没有. 然后学了\(SAM\),发现这个判重不就是个水题. \(SAM\)是字串tire的集合体. 随便\(d ...

  9. 洛谷 P6276 - [USACO20OPEN]Exercise P(组合数学+DP)

    洛谷题面传送门 废了,又不会做/ll orz czx 写的什么神仙题解,根本看不懂(%%%%%%%%% 首先显然一个排列的贡献为其所有置换环的乘积.考虑如何算之. 碰到很多数的 LCM 之积只有两种可 ...

  10. 51nod 1709 复杂度分析

    51nod 1709 复杂度分析 考虑定义 $ F(x) $ 为 \(x\) 为根的子树所有点与 $ x $ 的深度差(其实就是 $ x $ 到每个子树内点的距离)的 1 的个数和. 注意,$ F(x ...