MySQL各部分的执行顺序

MySQL各部分的执行顺序,第1张

前几天参加了一个公司的面试,到了后面面试官出了一个SQL相关的题目:

数据的形式类似于以下这样(表名为views):

当时为了稳妥起见,我的第一反应是使用窗口函数,

然后面试官问:“还有没有什么简便的方法么?”

很明显他的意思是要用传统的groupby来完成这个查询,确实我之前的查询又是用窗口函数又是加了distinct确实是复杂一些。

于是我用group by再写了一遍。

看完我的查询之后,面试官又问了一句:“可以不需要使用嵌套查询吗?”

当时我的回答是”应该不行,如果不使用嵌套而直接在group by后面加having的话sql会报错,就和where如果使用别名查询就会报错一样“

后面面试完想了一下,发现自己当时回答得不好,不是正确的但也不完全错,不是正确的原因是按照sql的规则having后面是可以拿聚合函数做判断的,但是不完全错的原因是如果having用的是像我之前设置的别名来判断的话,确实是会出错的。(虽然mysql在5.6之后基于sql的规则对group by进行拓展,支持这种写法。但在其它sql上面用别名having还是不行的)

我们可以从SQL运行时各部分的执行顺序来进行分析,当我们选择执行一个SQL语句的时候,它会按照以下的顺序来进行 *** 作

这个执行顺序的设计是很巧妙的,我说一下我自己对于上述顺序的理解,

1. FROM

顾名思义,当执行查询语句的时候,首先需要知道的是它需要哪些表,正如我们去一个地方需要知道它的具体位置一样。如果需要多个表的话在这一部分也需要按照一定的顺序进行表的join *** 作。

2. WHERE

当确定我们需要读取哪一张表(或者多张表)的数据之后,我们就需要进行where的filter *** 作,根据filter尽量减少读取的数据数量。

那么问题来了,为什么where的优先级要比group by,having, select之类的要高呢?

第一个原因是可以减少不必要的查询量,加快执行语句的速度,类似于Apache Spark在对查询语句进行逻辑优化时需要用到的谓词下推类似的道理。举个栗子,比如我们可能需要userid从100到300的用户对于某一个页面的浏览次数,那么如果先执行group by再执行where的话,userid小于100的用户的数据也会被汇总进去,但实际上这些部分的数据是完全不需要的,计算它们完全是浪费系统资源(而且group by *** 作本身就是很耗资源的 *** 作)

3. GROUP BY

在完成where *** 作的过滤之后,如果语句中有group by的话则会对过滤后的数据进行聚合 *** 作,聚合 *** 作是多对一的转换,因此在聚合 *** 作过后,除了用于group by的字段之外,其它字段的原始数据将会丢失,只能得到它们相应的聚合结果(比如sum(), avg()这样)

在完成聚合 *** 作之后,参与group by的字段以及其它字段对应的聚合值已经处于已知状态,后续的 *** 作可以直接使用它们。

4. HAVING

HAVING *** 作主要做的是对group by之后的分组结果进行过滤,可以根据参与group by的字段进行过滤,也可以根据其它字段的聚合值进行过滤。(因为聚合值在这里已经算是已知数据)因此这里是可以拿聚合函数做判断的,比如最开始的那个查询的例子,可以直接写成以下的形式,

HAVING并不是一定要和group by成对出现的,它也可以单独存在,在没有group by的时候,此时默认只有一个组,但是需要注意的是这时having里面参与过滤的字段需要在select里面存在,不然having会不知道这是分组里面的内容而导致报错。

5. SELECT

选取结果集中相对应的字段,在select中为字段设置的别名在此阶段及之后的 *** 作中生效。

6. DISTINCT

去重 *** 作,放在select之后有个原因是去重 *** 作是要根据select里面所选字段来进行的。

7. ORDER BY

对得到的结果按照特定字段顺序进行排列,这里可以使用别名

8. LIMIT

设置显示结果集中的几条数据

通过分析MySQL中各部分的执行顺序,我们就不难理解为什么where不能有别名,而having可以用聚合函数来判断的原因,而且借此机会重新温习一遍SQL各部分对应的功能,加深理解,可以说是一举两得。

   查看服务器是否有自带的MySQL,如果有可以直接使用,如果自带的版本比较低,可以删除然后安装自己想要的版本 (在安装新版本MySQL之前,需要卸载服务器自带的MySQL包和MySQL数据库分支mariadb的包)

  1、rpm -qa|grep mysql -- 查询服务器是否有mysql,如有,则执行下面的语句进行删除

  2、rpm -qa |grep mariadb -- 查询服务器是否有mariadb,有则执行第三步进行删除

  3、rpm -e --nodeps 要删除的文件名( nodeps表示强制删除 )

   小贴士1: 如果使用rz命令时提示找不到命令,直接执行: yum -y install lrzsz 则可以在线下载。

   命令: rz 或者rz -be

   格式: rz -be 选择需要上传的文件

  批量或者单个上传文件,通过ZMODEM协议,除此之外,还可以通过ftp或者sftp进行上传

   按照下面的命令顺序执行,文件名修改成你压缩后的文件名称即可

   启动命令: systemctl start mysql

   报错信息: Job for mysqld.service failed because the control process exited with error code. See "systemctl status mysqld.service" and "journalctl -xe" for details.

   根据报错信息执行 : systemctl status mysqld.service" 或者 "journalctl -xe"命令查看报错详情,发现报错信息中存在: Data Dictionary upgrade from MySQL 5.7 in progress

  说明是因为新版本和之前服务器自带的版本对应的包存在冲突,删除对应的冲突目录即可,执行: rm -rf /var/lib/mysql/*(执行删除命令的时候要看清楚哦)

systemctl start mysql -- 启动服务器

  第一次成功启动MySQL会被设置默认一个密码,通过以下命令查看并进行登录。

   1、查看第一次启动的临时密码 :grep password /var/log/mysqld.log

   2、连接到服务器 : mysql -u root -p 回车,然后输出密码

   3、第一次连接会强制你必须修改连接密码 ,可以使用以下的语句进行修改密码:

  ALTER USER root@localhost IDENTIFIED WITH caching_sha2_password BY '123456' (MySQL8.x适合使用这个语句)

  UPDATE USER SET PASSWORD=PASSWORD('你的密码') WHERE USER='root' (MySQL5.x版本的修改)


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/8688860.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-19
下一篇 2023-04-19

发表评论

登录后才能评论

评论列表(0条)

保存