目录
DQL作为经常使用的SQL语句,非常重要,是掌握数据库技术的基础,用于从一个或多个表中检索数据。下文根据SELECT基础语法,WHERE条件查询,条件中的各种操作符和关键字,LIMIT结果限制和ORDER BY排序,GROUP BY分组和HAVING分组条件筛选这些知识点,一一整理归纳和总结对应语句和关键字的语法、使用注意事项,以及工作中的使用技巧和面试问题。部分内容涉及MySQL8的新特性,以及与之前版本的比较。
环境:MySQL 8.0.32
下文语法中[]包括的内容表示可选项
一、查询 SELECT
1 查询常量、变量、表达式、函数
SELECT `常量`;
SELECT `变量`;
SELECT 表达式;
SELECT 函数;
说明:对于MySQL字符串、数值、日期时间等常量,用户、系统等变量,运算、逻辑表达式,以及指定参数值的函数(后续文章会作详解),可以通过SELECT语句直接查询
2 查询数据
查询指定列
SELECT `字段1`[, `字段2`, ...] FROM `表名`;
说明:指定字段可为一个或多个,返回字段对应单列或多列的所有行。显示的结果数据可能顺序不同,出现这种情况很正常,因为未作指定排序,所以返回的顺序是无特殊意义的。
查询所有列
SELECT * FROM `表名`;
说明:通配符(*)表示所有列,列的顺序一般是列在表定义中出现的顺序,但表模式的变化可能导致数据的变化。尽量避免使用!检索不需要的字段通常会降低检索和应用程序的性能。
通过DISTINCT关键字查询不同的行
SELECT DISTINCT `字段1`[, 字段2, ...] FROM `表名`;
说明:列前使用DISTINCT关键字去重,返回数据不同的行。如果有多个列,DISTINCT应用于检索的所有列而不是前置它的列,所以除非检索的所有列都不同,否则所有行都将被检索出来。
3 名字使用技巧
完全限定名
SELECT [`表名`.]`字段` FROM [`库名`.]`表名`;
说明:可通过完全限定名引用表或字段。如不同库、不同表关联查询时,需要使用完全限定名引用加以区分,否则容易混淆引发错误。
别名
SELECT `字段` [AS] `字段别名` FROM `表名` [AS] `表别名`
列别名:字段后使用AS指定列别名,查询结果显示列别名而不是原字段名
表别名:表后使用AS指定表别名,使用表别名后完全限定表名为表别名
注意:
- 如无必要不省略AS。
- 表的别名不能与数据库中的其他表同名。
- 字段的别名不能与该表的其它字段同名。在条件表达式中不能使用字段的别名,否则可能会出现错误。
- 别名中有空格,需要使用引号(单引号或双引号均可)。
- 某些情况下,如连接查询或子查询时,使用别名使查询更易维护、优化和管理,提高了查询结果的清晰度和准确性。
二、条件 WHERE
SELECT语句中,数据根据WHERE子句中指定搜索条件进行过滤,结果为表数据的子集。
1 比较操作符
SELECT `字段` FROM `表名` WHERE `字段` 操作符 值;
操作符 | 描述 |
= | 等于 |
<>或!= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
注意:
- 值与串类型的列进行比较,需用单引号或者双引号引用,默认不区分大小写。
- 尽量使用<>做不等判断
- 数值按大小比较;字符根据对应位置,按照ASCII码对应值,逐一进行比较
2 范围值检查
匹配范围中所有的值,包括指定的开始值和结束值。语法与其他操作符稍有不同,需要两个值。
SELECT `字段` FROM `表名` WHERE `字段` BETWEEN 值1 AND 值2;
注意:
- 值可为数值、文本或日期
- 闭区间,即大于等于左边的值,并且小于等于右边的值
3 空值检查
创建表时,可指定列是否可为NULL。SELECT语句通过两个特殊子句检查具有NULL值的列。
SELECT `字段` FROM `表名` WHERE `字段` IS [NOT] NULL;
IS NULL:查询指定列的值为NULL的记录
IS NOT NULL:查询指定列的值不为NULL的记录
注意:
- NULL与0、空字符串或包含空格不同。空字符串长度为0,不占用空间;NULL长度为NULL,占用空间,需要行中的额外空间来记录它们的值是否为NULL
- NULL与任何其他值(即使与NULL)比较结果都为NULL
- IS和IS NOT只检查NULL值,空字符串则可以使用比较操作符=、!=和<>检查
4 逻辑操作符
AND、OR操作符
SELECT `字段` FROM `表名` WHERE 条件1 [AND或OR 条件2 ...];
AND: 指示检索满足所有给定条件的行
OR:指示检索匹配任一条件的行
注意:
- WHERE可包含任意数量的AND和OR操作符
- 默认优先处理AND操作符
- 使用具有AND和OR操作符的WHERE子句,应使用圆括号分组操作符,明确计算优先级,消除歧义,不要过分依赖默认计算次序
IN操作符
IN用来指定条件范围,范围中的每个条件都可以进行匹配,满足任一合法值的都会返回。
SELECT `字段` FROM `表名` WHERE `字段` [NOT] IN (值1, 值2, ...);
注意:
- 合法项清单中的值的类型必须一致或兼容
- 不支持通配符
- IN操作符功能与OR操作符相当,NOT IN操作符功能与NOT...AND相当,但计算次序比OR和AND操作符更清晰,执行更快
- 可嵌套包含其他SELECT语句,使得能更动态地建立子句
- 可使用多字段与嵌套SELECT查询的字段一一对应进行查询(不建议使用)
NOT操作符
NOT否定它之后所跟的任何条件。MySQL支持使用NOT对IN、BETWEEN和EXSITS子句取反,如NOT IN返回与条件范围列表中都不匹配的记录。
5 LIKE操作符
LIKE是模糊查询关键字,可通过构造一个由字面值、通配符或两者组合构成的搜索条件,利用通配符进行匹配,检索出不为已知值的特定数据。
SELECT `字段` FROM `表名` WHERE `字段` LIKE pattern;
说明:pattern为搜索模式,可包含通配符。通配符可在模式中任意位置使用,并且可使用多个。
常用通配符
% 表示任何字符出现任意次数,次数可为0、1或多次
_ 表示任何字符只出现一次
注意:
- 首尾空格可能影响匹配,可使用函数去除,后续文章会详解
- 不能匹配NULL值
- 不要过度使用通配符。如果其他操作符能达到相同目的,应使用其他操作符。
- 除非必要,不要把通配符置于搜索模式的开始处,否则搜索速度最慢。
三、排序 ORDER BY
为明确排序通过SELECT语句检索出的数据,可使用ORDER BY子句,取一个或多个列进行输出降序或升序排序。
SELECT `字段` FROM `表名` ORDER BY `字段1` [ASC|DESC], `字段2` [ASC|DESC];
说明:
- ASC|DESC表示排序的规则,可省略,默认升序:ASC,降序:DESC
- 支持多个字段排序。仅限前一字段相同时,才进行后面字段的排序
- 当字段使用别名或函数时,可使用对应字段的别名或函数进行排序
四、限制 LIMIT
LIMIT强制SELECT语句返回检索出的数据的指定行数。
SELECT `字段` FROM `表名` LIMIT [offset,] rows;
说明:
- offset表示偏移量,默认为0;rows表示要返回的记录行数
- 参数不能为表达式,必须是一个整数常量
- MySQL 5开始,为了与 PostgreSQL 兼容,也支持句法: LIMIT rows OFFSET offset
- 行数不够时,返回所有可以返回的行数
获取前n行记录
SELECT `字段` FROM `表名` LIMIT n;
SELECT `字段` FROM `表名` LIMIT 0, n;
注意:初始记录行的偏移量是0(即检索出来的第一行为行0,而不是行1)
获取最大值或最小值记录:可以通过ORDER BY字段排序后,使用LIMIT获取第一行记录
获取第n到m行记录
SELECT `字段` FROM `表` LIMIT n-1, m-n+1;
注意:m越大,偏移量越大,性能越差。所以通过给出大致范围、WHERE语句添加条件过滤等方法,尽量缩小数据扫描的范围
分页查询
SELECT `字段` FROM`表名` LIMIT (page - 1) * pageSize,pageSize;
说明:page表示第几页,pageSize表示每页显示的记录数量
注意:分页排序时,不要有二义性,可能导致分页结果乱序,可以在后面追加一个主键排序
五、分组 GROUP BY、分组后过滤HAVING
分组查询
SELECT `字段`, group_function FROM `表名`
[WHERE 查询条件]
GROUP BY 分组表达式
[HAVING 分组过滤条件]
说明:
- group_fiunction:聚合函数
- group_by_expression:分组表达式,多个分组字段使用逗号间隔
- 分组后SELECT只能查询两种类型的字段:出现在GROUP BY后分组表达式中的、使用聚合函数max、min、count、sum、avg的(函数部分会在后续文章进行详解)。大部分数据库对此的规范相同
- WHERE子句对分组前数据进行筛选,HAVING对分组后数据进行筛选
- MySQL8后,GROUP BY不再默认排序
HAVING与WHERE的区别
- WHERE对分组前数据进行筛选,HAVING对分组后数据进行筛选
- HAVING单独使用(Oracle中不与GROUP BY使用会报错),大部分场合与WHERE相同。但一般与GROUP BY一起使用
- WHERE子句不能使用聚合函数和别名,而HAVING可以在条件中使用聚合函数和别名
- 可以使用WHERE时尽量不使用HAVING,WHERE是数据从磁盘读入内存时筛选,HAVING在内存中进行筛选
- WHERE对数据库文件过滤,HAVING对SELECT中查询的字段的数据进行过滤
- 多表关联查询时,WHERE先筛选再连接,HAVING先连接再筛选
六、关键字使用顺序
(以下知识点涉及的部分内容将在后续文章进行详解)
以上介绍的关键字使用写法顺序必须遵循以下规则:
SELECT DISTINCT `字段` FROM `表`
JOIN `表` ON 表关联条件
WHERE 查询条件
GROUP BY 分组表达式
HAVING 分组过滤条件
ORDER BY 排序条件
LIMIT [offset,] rows;
语句执行顺序必须遵循以下规则:
- FROM
- ON
- JOIN
- WHERE
- GROUP BY
- HAVING
- SELECT
- DISTINCT、函数
- ORDER BY
- LIMIT
注意:
- 在MySQL中SQL的逻辑查询是根据上述顺序执行,但MySQL可能并不完全会按照逻辑查询处理方式进行查询。MySQL在执行查询之前,都会选择一条自认为最优的查询方案去执行,获取查询结果。一般情况下都能计算出最优的查询方案,但在某些情况下,MySQL给出的查询方案并不是很好的查询方案
- SELECT在FROM和GROUP BY之后执行,所以导致无法在WHERE和GROUP BY中使用SELECT中设置的字段别名作为条件。
WHERE子句中,针对MySQL,其条件执行顺序是从左往右,自上而下;针对Orcale,其条件执行顺序是从右往左,自下而上。
WHERE子句在数据量小的时候不用考虑条件的先后顺序,但数据量多的时候应遵守一个原则:排除越多的条件放在第一个,提高查询效率
后续系列中会介绍MySQL更多知识。如有问题和建议,可私信或评论,非常感谢。