DBA MySQL单表查询
前戏工作
数据准备
单表查询相关数据:
CREATE TABLE userInfo(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT "记录编号",
name CHAR(32) NOT NULL COMMENT "用户姓名",
gender ENUM("MALE", "FEMALE") NOT NULL DEFAULT "MALE" COMMENT "用户性别",
age TINYINT(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT "用户年龄",
role ENUM("USER", "ADMIN", "BOSS") NOT NULL DEFAULT "USER" COMMENT "用户角色",
fans_quantinty INT NOT NULL DEFAULT 0 COMMENT "粉丝数量",
create_time DATETIME NOT NULL DEFAULT NOW() COMMENT "创建时间",
delete_status ENUM("0", "1") NOT NULL DEFAULT "0" COMMENT "是否注销"
) ENGINE innodb CHARSET utf8mb4 COLLATE utf8mb4_general_ci;
INSERT INTO
userInfo(name, gender, age, role, fans_quantinty, create_time, delete_status)
VALUES
("YunYa", "MALE", 18, "BOSS", 0, "2002-01-28 19:18:22", "0"),
("Jack", "MALE", 21, "ADMIN", 2000, "2006-03-05 08:30:00", "0"),
("David", "MALE", 18, "ADMIN", 3689, "2004-02-21 14:30:00", "0"),
("Dairis", "FEMALE", 19, "ADMIN", 5232, "2008-09-21 08:30:00", "0"),
("Kyle", "FEMALE", 21, "USER", 123021, "2002-08-22 12:02:31", "1"),
("Alice", "FEMALE", 24, "USER", 293042, "2010-03-07 23:02:21", "0"),
("Ken", "MALE", 23, "USER", 231, "2020-11-13 09:06:32", "0"),
("Jason", "MALE", 22, "USER", 32132134, "2002-02-01 12:32:45", "0"),
("Tom", "MALE", 28, "USER", 32, "2021-01-01 02:30:21", "1"),
("Fiona", "FEMALE", 18, "USER", 321234, "2012-05-28 19:21:32", "0");
查询语法
查询语法如下:
SELECT
DISTINCT(字段名1, 字段名2...)
FROM
表名
WHERE
过滤条件
GROUP BY
分组字段名
HAVING
筛选条件
ORDER BY
排序字段名 ASC / DESC
LIMIT
限制条数;
执行顺序
内部执行顺序如下所示:
1.通过FROM找到将要查询的表
2.WHERE规定查询条件,在表记录中逐行进行查询并过滤出符合规则的记录
3.将过滤到的记录信息按照字段进行GROUP BY分组,如果没有进行分组,则默认按照主键分为1组
4.将分组得到的结果进行HAVING筛选,此时可使用聚合函数。WHERE时不可使用聚合函数
5.执行SELECT准备打印
6.执行DISTINCT()函数对打印结果进行去重
7.执行ORDERY BY对结果进行排序,升序ASC,降序DESC
8.执行LIMIT对打印结果进行条数限制
SELECT
功能概述
SELECT
主要负责打印相关的工作。
除了查表外,也可以单独使用,如以下命令:
SELECT DATABASE();
全部查询
全部查询的语句格式如下:
SELECT * FROM 表名;
示例演示:
M > SELECT * FROM userinfo;
+----+--------+--------+-----+-------+----------------+---------------------+---------------+
| id | name | gender | age | role | fans_quantinty | create_time | delete_status |
+----+--------+--------+-----+-------+----------------+---------------------+---------------+
| 1 | YunYa | MALE | 18 | BOSS | 0 | 2002-01-28 19:18:22 | 0 |
| 2 | Jack | MALE | 21 | ADMIN | 2000 | 2006-03-05 08:30:00 | 0 |
| 3 | David | MALE | 18 | ADMIN | 3689 | 2004-02-21 14:30:00 | 0 |
| 4 | Dairis | FEMALE | 19 | ADMIN | 5232 | 2008-09-21 08:30:00 | 0 |
| 5 | Kyle | FEMALE | 21 | USER | 123021 | 2002-08-22 12:02:31 | 1 |
| 6 | Alice | FEMALE | 24 | USER | 293042 | 2010-03-07 23:02:21 | 0 |
| 7 | Ken | MALE | 23 | USER | 231 | 2020-11-13 09:06:32 | 0 |
| 8 | Jason | MALE | 22 | USER | 32132134 | 2002-02-01 12:32:45 | 0 |
| 9 | Tom | MALE | 28 | USER | 32 | 2021-01-01 02:30:21 | 1 |
| 10 | Fiona | FEMALE | 18 | USER | 321234 | 2012-05-28 19:21:32 | 0 |
+----+--------+--------+-----+-------+----------------+---------------------+---------------+
字段查询
使用以下语句格式进行查询,可拿到特定的字段及其下所对应的数据。
SELECT 字段名1, 字段名2 FROM 表名;
示例演示,查询每个用户的角色:
M > SELECT name,role FROM userinfo;
+--------+-------+
| name | role |
+--------+-------+
| YunYa | BOSS |
| Jack | ADMIN |
| David | ADMIN |
| Dairis | ADMIN |
| Kyle | USER |
| Alice | USER |
| Ken | USER |
| Jason | USER |
| Tom | USER |
| Fiona | USER |
+--------+-------+
别名使用
使用AS
进行别名定制,除了在SELECT
语句中使用,多表查询时也经常使用到。
使用格式如下:
SELECT 字段名1 AS 别名1, 字段名2 AS 别名2 FROM 表名;
示例演示,查询每个用户的姓名,年龄,角色,注意观察首行字段的名字:
M > SELECT
name AS "姓名",
age AS "年龄",
gender AS "性别",
role AS "用户角色"
FROM
userInfo;
+--------+--------+--------+--------------+
| 姓名 | 年龄 | 性别 | 用户角色 |
+--------+--------+--------+--------------+
| YunYa | 18 | MALE | BOSS |
| Jack | 21 | MALE | ADMIN |
| David | 18 | MALE | ADMIN |
| Dairis | 19 | FEMALE | ADMIN |
| Kyle | 21 | FEMALE | USER |
| Alice | 24 | FEMALE | USER |
| Ken | 23 | MALE | USER |
| Jason | 22 | MALE | USER |
| Tom | 28 | MALE | USER |
| Fiona | 18 | FEMALE | USER |
+--------+--------+--------+--------------+
DISTINCT()
使用DISTINCT()
函数可进行去重,如下示例将展示该表中拥有多少个用户角色:
M > SELECT
DISTINCT(role) AS "用户角色"
FROM
userInfo;
+--------------+
| 用户角色 |
+--------------+
| BOSS |
| ADMIN |
| USER |
+--------------+
四则运算
查询结果可进行四则运算,以下示例将展示该表中所有人的粉丝数+1000是多少:
M > SELECT
name AS "用户名",
fans_quantinty + 1000 AS "增加1000粉丝后的结果"
FROM
userInfo;
+-----------+------------------------------+
| 用户名 | 增加1000粉丝后的结果 |
+-----------+------------------------------+
| YunYa | 1000 |
| Jack | 3000 |
| David | 4689 |
| Dairis | 6232 |
| Kyle | 124021 |
| Alice | 294042 |
| Ken | 1231 |
| Jason | 32133134 |
| Tom | 1032 |
| Fiona | 322234 |
+-----------+------------------------------+
显示格式
这里有两个关于显示格式的函数,其中第一个函数尤为常用:
函数 | 描述 |
---|---|
CONCAT() | 可将查询结果与任意字符串进行拼接 |
CONCAT_WS() | 可指定连接符进行拼接,第1个参数是连接符 |
以下示例将展示CONCAT()
函数如何使用:
# 打印任意格式的数据
M > SELECT
CONCAT("用户名:", name, "-------", "粉丝数:", fans_quantinty)
FROM
userInfo;
+---------------------------------------------------------------------+
| CONCAT("用户名:", name, "-------", "粉丝数:", fans_quantinty) |
+---------------------------------------------------------------------+
| 用户名:YunYa-------粉丝数:0 |
| 用户名:Jack-------粉丝数:2000 |
| 用户名:David-------粉丝数:3689 |
| 用户名:Dairis-------粉丝数:5232 |
| 用户名:Kyle-------粉丝数:123021 |
| 用户名:Alice-------粉丝数:293042 |
| 用户名:Ken-------粉丝数:231 |
| 用户名:Jason-------粉丝数:32132134 |
| 用户名:Tom-------粉丝数:32 |
| 用户名:Fiona-------粉丝数:321234 |
+---------------------------------------------------------------------+
以下示例将展示CONCAT_WS()
函数如何使用:
# 使用*****为字段进行分割
M > SELECT
CONCAT_ws("*****", name, fans_quantinty)
FROM
userInfo;
+------------------------------------------+
| CONCAT_ws("*****", name, fans_quantinty) |
+------------------------------------------+
| YunYa*****0 |
| Jack*****2000 |
| David*****3689 |
| Dairis*****5232 |
| Kyle*****123021 |
| Alice*****293042 |
| Ken*****231 |
| Jason*****32132134 |
| Tom*****32 |
| Fiona*****321234 |
+------------------------------------------+
WHERE
功能概述
WHERE
能够有效过滤出我们想要的任意数据,但是要注意语句优化的使用。
比如在比较运算中,尽量避免使用!=
,少使用范围查询> <
等操作。
比较运算
WHERE
中支持的比较运算符> < = >= <= !=
,以下示例将展示过滤出粉丝大于10000的用户。
M > SELECT name FROM userInfo WHERE fans_quantinty > 10000;
+-------+
| name |
+-------+
| Kyle |
| Alice |
| Jason |
| Fiona |
+-------+
逻辑运算
使用AND OR NOT
可进行逻辑运算与多条件查询。
以下示例中将展示找到年龄大于18并且年龄小于20的用户:
M > SELECT name, age FROM userInfo WHERE age > 18 and age < 20;
+--------+-----+
| name | age |
+--------+-----+
| Dairis | 19 |
+--------+-----+
成员运算
IN
可以在特定的值中进行获取,如IN(80,90,100)
则代表只取80或者90或者100的这几条记录。
以下示例中将展示找到role
为BOOS
和ADMIN
的用户:
M > SELECT name FROM userInfo WHERE role in ("ADMIN","BOSS");
+--------+
| name |
+--------+
| YunYa |
| Jack |
| David |
| Dairis |
+--------+
BETWEEN&AND
BETWEEN AND
是取区间的意思。
与>=
并且<
的意思是相同的。
以下示例中将展示找到年龄大于等于20,且小于25的用户:
M > SELECT name, age FROM userInfo WHERE age BETWEEN 20 AND 25;
+-------+-----+
| name | age |
+-------+-----+
| Jack | 21 |
| Kyle | 21 |
| Alice | 24 |
| Ken | 23 |
| Jason | 22 |
+-------+-----+
LIKE查询
LIKE
是模糊查询。
其中%
代表任意多个字符(类似于贪婪匹配的通配符.+
)。
_
代表任意一个字符(类似于非贪婪匹配的通配符.+?
)。
不要出现类似于%xx%这种前后都有百分号的语句
因为不走索引,所以性能极差
以下示例将展示使用LIKE
匹配出姓名以k
开头的所有用户的名字:
M > SELECT name FROM userInfo WHERE name LIKE "K%";
+------+
| name |
+------+
| Kyle |
| Ken |
+------+
REGEXP
REGEXP
可用作正则匹配。
mysql8版本以下不允许出现?的取消贪婪符号
以下示例将展示使用REGEXP
匹配出姓名中带有k
字符的所有用户的名字:
M > SELECT name FROM userInfo WHERE name REGEXP "K+";
+------+
| name |
+------+
| Kyle |
| Ken |
+------+
GROUP BY
功能概述
分组行为发生在WHERE
条件之后,我们可以将查询到的记录按照某个相同字段进行归类,一般分组都会配合聚合函数进行使用。
需要注意的是SELECT
语句是排在GROUP BY
条件之后的,因此聚合函数也能在SELECT
语句中使用。
如果不使用GROUP BY
语句进行分组,则默认会将所有记录分成一组!详情参见分组模式。
基本使用
以下示例将展示对role
字段进行分组。
我们按照role字段进行分组时,受分组模式影响,SELECT后打印的字段只能是role字段或者是被聚合函数包裹住的其余字段。
如果想要直接获取组内其它的字段信息,需要借助函数GROUNP_CONCAT()或修改分组模式来完成。
正常的情况下,role
是分组条件,SELECT
后只能出现role
:
M > SELECT
role
FROM
userInfo
GROUP BY
role;
+-------+
| role |
+-------+
| USER |
| ADMIN |
| BOSS |
+-------+
如果还想直接打印其他字段信息而不包裹聚合函数的话,如直接打印name
的操作,就会抛出模式错误的异常:
M > SELECT
role,
name
FROM
userInfo
GROUP BY
role;
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db1.userInfo.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
显示函数
受到分组模式的影响,我们只能在SELECT
语句后直接打印出GROUNP BY
指定的字段,这很不方便。
使用分组显示函数GROUP_CONCAT()
可改变这种状况,配合其他显示函数食用效果更佳,如下所示,展示role
分组与其中的成员信息:
M > SELECT
role AS "等级",
GROUP_CONCAT(CONCAT("", name, "|", age, "")) AS "成员信息(name,age)"
FROM
userInfo
GROUP BY
role;
+--------+--------------------------------------------------------------+
| 等级 | 成员信息(name,age) |
+--------+--------------------------------------------------------------+
| USER | Kyle|21,Alice|24,Ken|23,Jason|22,Tom|28,Fiona|18 |
| ADMIN | Jack|21,David|18,Dairis|19 |
| BOSS | YunYa|18 |
+--------+--------------------------------------------------------------+
分组模式
ONLY_FULL_GROUP_BY
要求SELECT
中出现的字段必须是在GROUP BY
中使用的字段,或者是被聚合函数包裹,否则抛出异常。
解决办法如下所示:
- 使用函数GROUP_CONCAT()将位于SELECT语句后且不在GROUP BY分组条件中的字段进行包裹
- 使用函数ANY_VALUE()将位于SELECT语句后且不在GROUP BY分组条件中的字段进行包裹
- 使用聚合函数将位于SELECT语句后且不在GROUP BY分组条件中的字段进行包裹
- 通过更改分组模式sql_mode,允许位于SELECT语句后且不在GROUP BY分组条件中的字段进行显示
- 如果GROUP BY后的字段是主键或拥有NOT NULL UNIQUE约束时可以在SELECT后列出其他字段而不必使用上述手段进行处理
# 查看mysql5.7中默认的sql_mode:
M > SELECT @@GLOBAL.SQL_MODE;
# 修改sql_mode,去掉ONLY_FULL_GROUP_BY模式,本次mysqld.service服务运行中生效,重启服务失效
SET GLOBAL sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
聚合函数
聚合函数一般放在SELECT
语句后和HAVING
语句后使用。
聚合函数一般是同分组进行配套使用,以下是常用的聚合函数。
注意:不使用分组,则默认为一组,所以也可以在SELECT语句后面使用聚合函数
函数名 | 作用 |
---|---|
COUNT() | 对组内成员某一字段求个数 |
MAX() | 对组内成员某一字段求最大值 |
MIN() | 对组内成员某一字段求最小值 |
AVG() | 对组内成员某一字段求平均值 |
SUM() | 对组内成员某一字段求和 |
示例演示,以role
进行分组,查看每个组中年纪最大的人:
M > SELECT
role AS "等级",
MAX(age) AS "最大年纪"
FROM
userInfo
GROUP BY
role;
+--------+--------------+
| 等级 | 最大年纪 |
+--------+--------------+
| USER | 28 |
| ADMIN | 21 |
| BOSS | 18 |
+--------+--------------+
示例演示,以age
分组,查看不同年龄段中有多少人:
M > SELECT
age AS "年龄",
COUNT(id) AS "人数"
FROM
userInfo
GROUP BY
age;
+--------+--------+
| 年龄 | 人数 |
+--------+--------+
| 18 | 3 |
| 19 | 1 |
| 21 | 2 |
| 22 | 1 |
| 23 | 1 |
| 24 | 1 |
| 28 | 1 |
+--------+--------+
HAVING
使用介绍
HAVING
可以对分组后的数据进行筛选,和WHERE
最大的区别就是能够使用聚合函数进行筛选。
执行优先级从高到低:WHERE
> GROUNP BY
> HAVING
,先分组,后HAVING
过滤。
WHERE
发生在分组GROUNP BY
之前,因而WHERE
中可以有任意字段,但是绝对不能使用聚合函数。
2.HAVING
发生在分组GROUNP BY
之后,因而HAVING
中可以使用分组的字段,无法直接取到其他字段,可以使用聚合函数
3. 如果不使用分组GROUNP BY
而单纯使用HAVING
并且同时使用聚合函数作为筛选的话,SELECT
将会受到分组模式的影响,可查看上面章节进行解决
过滤出role
是ADMIN
的用户,且归到一个组中:
M > SELECT
GROUP_CONCAT(name)
FROM
userInfo
GROUP BY
role
HAVING
role = "ADMIN";
+--------------------+
| GROUP_CONCAT(name) |
+--------------------+
| Jack,David,Dairis |
+--------------------+
ORDER BY
排序规则
默认排序是按照id
进行排序,也就是主键。
它的比对规则是对比ASCII
码来产生的。
排序可以有多重规则。
ASC
升序排序,也是默认的排序规则,以下示例中将展示按照粉丝数量进行升序排序:
M > SELECT
name,
fans_quantinty
FROM
userInfo
ORDER BY
fans_quantinty; -- 默认的排序规则,所以不用指定ASC
+--------+----------------+
| name | fans_quantinty |
+--------+----------------+
| YunYa | 0 |
| Tom | 32 |
| Ken | 231 |
| Jack | 2000 |
| David | 3689 |
| Dairis | 5232 |
| Kyle | 123021 |
| Alice | 293042 |
| Fiona | 321234 |
| Jason | 32132134 |
+--------+----------------+
DESC
降序排序,以下示例中将展示用户年龄进行降序排序,如年龄相同则按照粉丝数量进行比对:
M > SELECT
name,
age,
fans_quantinty
FROM
userInfo
ORDER BY
age DESC,
fans_quantinty DESC;
+--------+-----+----------------+
| name | age | fans_quantinty |
+--------+-----+----------------+
| Tom | 28 | 32 |
| Alice | 24 | 293042 |
| Ken | 23 | 231 |
| Jason | 22 | 32132134 |
| Kyle | 21 | 123021 |
| Jack | 21 | 2000 |
| Dairis | 19 | 5232 |
| Fiona | 18 | 321234 |
| David | 18 | 3689 |
| YunYa | 18 | 0 |
+--------+-----+----------------+
LIMIT
基本演示
LIMIT
用于控制SELECT
打印的条目数量。
一般来说我们会对查询都会做上LIMIT
来提升性能。
以下示例中,将展示控制显示条目为1条,以此来获得表中年龄最小,粉丝最少的一个人:
M > SELECT
name,
age,
fans_quantinty
FROM
userInfo
ORDER BY
age,
fans_quantinty
LIMIT
1;
+-------+-----+----------------+
| name | age | fans_quantinty |
+-------+-----+----------------+
| YunYa | 18 | 0 |
+-------+-----+----------------+
以下示例中,将展示全表中从第5条开始(不包含5)向后取3条的数据:
M > SELECT
*
FROM
userInfo
LIMIT
5, 3;
+----+-------+--------+-----+-------+----------------+---------------------+---------------+
| id | name | gender | age | role | fans_quantinty | create_time | delete_status |
+----+-------+--------+-----+-------+----------------+---------------------+---------------+
| 6 | Alice | FEMALE | 24 | USER | 293042 | 2010-03-07 23:02:21 | 0 |
| 7 | Ken | MALE | 23 | USER | 231 | 2020-11-13 09:06:32 | 0 |
| 8 | Jason | MALE | 22 | USER | 32132134 | 2002-02-01 12:32:45 | 0 |
+----+-------+--------+-----+-------+----------------+---------------------+---------------+