第一章:MySQL查询

51 阅读11分钟

分页:

limit:

执行顺序:最后执行

好处:(减少每次返回数据量,提高查询效率,减少网络传输量,不用扫描全表,只要检索到对应数量的数据即可)

--limit格式: Limit 位置偏移量,条目数
--查询第一页数据
select * from employees limit 0,20;
--需求:每页显示pageSize条记录,此时显示第pageNo页:
--公式:Limit (pageNo-1) * pageSize,pageSize

offset:

--limit格式: Limit 位置偏移量,条目数
limit 4,3			从第四条开始取三条
--limit...offset...格式: Limit 条目数 offset 位置偏移量
limit 3 offset 4	取三条,从第四条开始

拓展:

多表查询(重点):

为什么需要多表查询(为什么要按业务水平划分表)?

  • 多个表合在一起容易产生冗余字段
  • 查询时,都查同一个表,影响查询效率
  • 没有多表查询,分开提交多个SQL,请求多次,效率低

非等值连接:

自连接:

自己连接自己

外连接:

内连接是等值连接

七种 Join 的实现(面试重点):

会写这几种连接就差不多了

下图是oracle的实现方式,mysql只有inner join和左右连接,最下面的两个图需要配合union去实现

联合查询(union和union all):

可以把上面的七种查询互相加起来(拼图)

函数

单行函数:

数值函数

字符串函数

连接

mysql 大小写敏感吗

一、MySQL是大小写敏感的吗

MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。如果你稍加不注意就会出现在本机开发的程序运行一切正常,发布到服务器行就出现表名找不到的问题,一头雾水。

由于这个原因,在阿里巴巴规约中这样要求:

【强制】表名、字段名必须使用小写字母或数字 , 禁止出现数字开头,禁止两个下划线中间只 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。

因此,数据库名、 表名、字段名,都不允许出现任何大写字母,避免引起不必要的麻烦。

二、MySQL的大小写敏感是由参数控制的

mysql大小写敏感配置相关的两个参数,lower_case_file_system 和 lower_case_table_names。

查看当前mysql的大小写敏感配置,可以使用如下语句

show global variables like '%lower_case%';

+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| lower_case_file_system | ON    |
| lower_case_table_names | 0     |
+------------------------+-------+

参数说明如下:

  1. lower_case_file_system,代表当前系统文件是否大小写敏感,只读参数,无法修改。ON 大小写不敏感,OFF 大小写敏感。
    • 此变量描述数据所在的操作系统的文件目录是否区分大小写。 OFF表示文件名区分大小写,ON表示它们不区分大小写。此变量是只读的,因为它反映了文件系统的属性,设置它对文件系统没有影响。
  1. lower_case_table_names,代表表名是否大小写敏感,可以修改,参数有0、1、2三种。
    • 0 大小写敏感。(Unix,Linux默认) 创建的库表将原样保存在磁盘上。如create database TeSt;将会创建一个TeSt的目录,create table AbCCC …将会原样生成AbCCC.frm文件,SQL语句也会原样解析。
    • 1 大小写不敏感。(Windows默认) 创建的库表时,MySQL将所有的库表名转换成小写存储在磁盘上。 SQL语句同样会将库表名转换成小写。 如需要查询以前创建的Testtable(生成Testtable.frm文件),即便执行select * from Testtable,也会被转换成select * from testtable,致使报错表不存在。
    • 2 大小写不敏感(OS X默认) 创建的库表将原样保存在磁盘上, 但SQL语句将库表名转换成小写。
三、MySQL大小写敏感如何设置

在Linux系统中修改my.cnf文件,在Windows下修改my.ini文件,新增或修改以下内容。

lower_case_table_names = 0 或 lower_case_table_names = 1 

然后重启MySQL服务才可以生效。

四、开发注意事项
  • 如果要将lower_case_table_names从0(敏感)修改为1(不敏感)时,必须先对旧数据表的表名进行处理,把所有数据库的表名先改为小写,最后再设置lower_case_table_names为1,否则依然会出现无法找到表名的问题。
  • 在Windows上lower_case_table_names默认值为1(不敏感),在macOS上默认值为2(不敏感)。在Linux上不支持值2,服务器强制该值为0(敏感)。
  • 并且MySQL官方也提示说:如果在数据目录驻留在不区分大小写的文件系统(例如Windows或macOS)上,则不应将lower_case_table_names设置为0。
  • 否则将出现MySQL服务无法启动的问题。
五、总结

由于操作系统不同导致大小写敏感的默认设置不一致,我们在开发时一定要注意,应该养成严格的意识,SQL语句一律采用小写字母,避免无意义的踩坑。

日期和时间函数

获取日期、时间

日期与时间戳的转换

获取月份、星期、星期数、天数等函数

流程控制函数

Case。。。When。。。

聚合函数:

常用的聚合函数

AVG()

SUM()

MAX()

MIN()

COUNT()

详解:

AVG() 函数:

求平均值(要考虑空值的问题):

COUNT()函数:

COUNT(*)返回表中记录总数,适用于任意数据类型

SELECT COUNT(*) FROM employees WHERE department_id = 50;

COUNT(expr字段名) 返回expr不为空的记录总数。

SELECT COUNT(commission_pct) FROM employees WHERE department_id = 50;

小结:

首先count是循环函数,一行一行去累加个数,每遍历一次就加一,类似累加器。

count(*)是有就累加。

count(1)是把遍历的格子用“1”去代替,然后进行累加,把1改成2结果也是一样,类似一个常数罢了。

count(列名)是把遍历的格子的非null值进行累加。

问题:用count(*) count(1) count(非空列名)谁好呢?

对于MyISAM引擎的表是没有区别的,都是O(1)。这种引擎内部有一计数器在维护着行数。

Innodb引擎的表用count(),count(1)直接读行数,复杂度是O(n),因为innodb真的要去数一遍。但好于具体的count(列名)。 三者效率:count() = count(1) > count(字段)

问题:能不能使用count(列名)替换count(*)?

不要使用 count(列名)来替代 count(*) , count(*) 是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。

说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

GROUP BY 分组

先后顺序不影响实际结果

这一种写法是不对的,在select里的列除了聚合函数列之外,都要在group by里面,否则查出来的数据结果是错的,原因是job_id列会有多个,但是department_id组只有一个,导致数据错误(在oracle这样使用是会报错的)

总结:

1、SELECT 中出现的非聚合函数的字段必须声明在 GROUP BY 中。

反之,GROUP BY 中声明的字段可以不出现在SELECT中。

2、GROUP BY 声明在 FROM 和 WHERE 后面,ORDER BY 前面、LIMIT 前面。

3、MYSQL中GROUP BY 使用 WITH ROLLUP(汇总)时,不能同时使用 ORDER BY。

HAVING的使用(用来过滤数据的)

写在 GROUP BY 后面,用作过滤数据

SQL的执行原理(执行顺序)

执行顺序,1->2->3,123里面又从上到下,FROM关联多张表是笛卡尔积的ON先过滤等值连接完之后再使用JOIN去补充其他数据

启示:优化SQL语句的时候,把能够筛选掉最多数据的条件,放在优先级高的关键字去筛选。

详细:

SELECT 是先执行 FROM 这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤:

  1. 首先先通过 CROSS JOIN 求笛卡尔积,相当于得到虚拟表 vt(virtual table)1-1;

  2. 通过 ON 进行筛选,在虚拟表 vt1-1 的基础上进行筛选,得到虚拟表 vt1-2;

  3. 添加外部行。如果我们使用的是左连接、右链接或者全连接,就会涉及到外部行,也就是在虚拟表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3。 (补充数据

当然如果我们操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得到是我们的原始数据。

然后进入第三步和第四步,也就是 GROUP 和 HAVING 阶段 。在这个阶段中,实际上是在虚拟表 vt2 的基础上进行分组和分组过滤,得到中间的虚拟表 vt3 和 vt4 。

当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 SELECT 和 DISTINCT 阶段 。

首先在 SELECT 阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表 vt5-1 和 vt5-2 。

当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 ORDER BY 阶段 ,得到虚拟表 vt6 。

最后在 vt6 的基础上,取出指定行的记录,也就是 LIMIT 阶段 ,得到最终的结果,对应的是虚拟表

vt7 。

当然我们在写 SELECT 语句的时候,不一定存在所有的关键字,相应的阶段就会省略。

同时因为 SQL 是一门类似英语的结构化查询语言,所以我们在写 SELECT 语句的时候,还要注意相应的关键字顺序,所谓底层运行的原理,就是我们刚才讲到的执行顺序。

子查询【嵌套查询】(重点)

子查询概念

子查询指一个查询语句嵌套在另一个查询语句内部的查询。

SQL 中子查询的使用大大增强了 SELECT 查询的能力,因为很多时候查询需要从结果集中获取数据,或者 需要从同一个表中先计算得出一个数据结果,然后与这个数据结果(可能是某个标量,也可能是某个集合)进行比较。

  • 子查询(内查询)在主查询之前一次执行完成。
  • 子查询的结果被主查询(外查询)使用 。

注意事项

    • 子查询要包含在括号内
    • 将子查询放在比较条件的右侧
    • 单行操作符对应单行子查询,多行操作符对应多行子查询

子查询分类

角度1:从子查询返回的结果条目数

单行子查询 和 多行子查询

角度2:子查询是否被执行多次

相关子查询 和 不相关子查询

相关子查询的需求:查询工资大于本部门平均工资的员工消息(员工的部门不一定都相同,所以子查询执行多次)

不相关子查询的需求:查询工资大于本公司平均工资的员工信息

单行子查询

单行比较操作符

单行子查询的操作符只能去匹配一个记录,否则会报错

多行子查询

多行比较操作符

相关子查询

如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为 关联子查询 。

相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。

执行流程

每次都进行一次子查询的执行(子查询执行多次)。

例子:

结论:

EXISTS 与 NOT EXISTS关键字(也是属于相关子查询)

关联子查询通常也会和 EXISTS操作符一起来使用,用来检查在子查询中是否存在满足条件的行

如果在子查询中不存在满足条件的行:

条件返回 FALSE

继续在子查询中查找

如果在子查询中存在满足条件的行:

不在子查询中继续查找

条件返回 TRUE

NOT EXISTS关键字表示如果不存在某种条件,则返回TRUE,否则返回FALSE。

自连接、子查询 和 exist

题目:查询公司管理者的employee_id last_name job_id department_id信息

方式一(exist):

exist中的SQL一般都是select * from的,然后

方式二(自连接):

方式三(子连接):

挖个坑:join、in和exist的效率比较

相关更新:

相关删除: