学习数据库操作方面的内容:查询(DQL语句)。
查询操作我们分为两部分学习:
DQL语句-单表操作
DQL语句-多表操作
数据库中数据的查询操作,也就是DQL语句,DQL语句学习完毕之后,关于单表的操作就讲解完了。包括单表的设计,单表的增删改查。
多表的操作,包括多表的设计,多表的查询等。
DQL
查询操作是所有SQL语句当中最为常见,也是最为重要的操作。
1.2 基本语法
DQL查询语句,语法结构如下:
SELECT
字段列表
FROM
表名列表
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
ORDER BY
排序字段列表
LIMIT
分页参数
将上面的完整语法拆分为以下几个部分学习:
基本查询(不带任何条件)
条件查询(where)
分组查询(group by)
排序查询(order by)
分页查询(limit)
准备一些测试数据用于查询操作:
create database db02; -- 创建数据库
use db02; -- 切换数据库
-- 员工管理(带约束)
create table tb_emp (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) default '123456' comment '密码',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
image varchar(300) comment '图像',
job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管',
entrydate date comment '入职时间',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '员工表';
-- 准备测试数据
INSERT INTO tb_emp (id, username, password, name, gender, image, job, entrydate, create_time, update_time) VALUES
(1, 'jinyong', '123456', '金庸', 1, '1.jpg', 4, '2000-01-01', '2022-10-27 16:35:33', '2022-10-27 16:35:35'),
(2, 'zhangwuji', '123456', '张无忌', 1, '2.jpg', 2, '2015-01-01', '2022-10-27 16:35:33', '2022-10-27 16:35:37'),
(3, 'yangxiao', '123456', '杨逍', 1, '3.jpg', 2, '2008-05-01', '2022-10-27 16:35:33', '2022-10-27 16:35:39'),
(4, 'weiyixiao', '123456', '韦一笑', 1, '4.jpg', 2, '2007-01-01', '2022-10-27 16:35:33', '2022-10-27 16:35:41'),
(5, 'changyuchun', '123456', '常遇春', 1, '5.jpg', 2, '2012-12-05', '2022-10-27 16:35:33', '2022-10-27 16:35:43'),
(6, 'xiaozhao', '123456', '小昭', 2, '6.jpg', 3, '2013-09-05', '2022-10-27 16:35:33', '2022-10-27 16:35:45'),
(7, 'jixiaofu', '123456', '纪晓芙', 2, '7.jpg', 1, '2005-08-01', '2022-10-27 16:35:33', '2022-10-27 16:35:47'),
(8, 'zhouzhiruo', '123456', '周芷若', 2, '8.jpg', 1, '2014-11-09', '2022-10-27 16:35:33', '2022-10-27 16:35:49'),
(9, 'dingminjun', '123456', '丁敏君', 2, '9.jpg', 1, '2011-03-11', '2022-10-27 16:35:33', '2022-10-27 16:35:51'),
(10, 'zhaomin', '123456', '赵敏', 2, '10.jpg', 1, '2013-09-05', '2022-10-27 16:35:33', '2022-10-27 16:35:53'),
(11, 'luzhangke', '123456', '鹿杖客', 1, '11.jpg', 2, '2007-02-01', '2022-10-27 16:35:33', '2022-10-27 16:35:55'),
(12, 'hebiweng', '123456', '鹤笔翁', 1, '12.jpg', 2, '2008-08-18', '2022-10-27 16:35:33', '2022-10-27 16:35:57'),
(13, 'fangdongbai', '123456', '方东白', 1, '13.jpg', 1, '2012-11-01', '2022-10-27 16:35:33', '2022-10-27 16:35:59'),
(14, 'zhangsanfeng', '123456', '张三丰', 1, '14.jpg', 2, '2002-08-01', '2022-10-27 16:35:33', '2022-10-27 16:36:01'),
(15, 'yulianzhou', '123456', '俞莲舟', 1, '15.jpg', 2, '2011-05-01', '2022-10-27 16:35:33', '2022-10-27 16:36:03'),
(16, 'songyuanqiao', '123456', '宋远桥', 1, '16.jpg', 2, '2010-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:05'),
(17, 'chenyouliang', '12345678', '陈友谅', 1, '17.jpg', null, '2015-03-21', '2022-10-27 16:35:33', '2022-10-27 16:36:07'),
(18, 'zhang1', '123456', '张一', 1, '2.jpg', 2, '2015-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:09'),
(19, 'zhang2', '123456', '张二', 1, '2.jpg', 2, '2012-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:11'),
(20, 'zhang3', '123456', '张三', 1, '2.jpg', 2, '2018-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:13'),
(21, 'zhang4', '123456', '张四', 1, '2.jpg', 2, '2015-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:15'),
(22, 'zhang5', '123456', '张五', 1, '2.jpg', 2, '2016-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:17'),
(23, 'zhang6', '123456', '张六', 1, '2.jpg', 2, '2012-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:19'),
(24, 'zhang7', '123456', '张七', 1, '2.jpg', 2, '2006-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:21'),
(25, 'zhang8', '123456', '张八', 1, '2.jpg', 2, '2002-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:23'),
(26, 'zhang9', '123456', '张九', 1, '2.jpg', 2, '2011-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:25'),
(27, 'zhang10', '123456', '张十', 1, '2.jpg', 2, '2004-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:27'),
(28, 'zhang11', '123456', '张十一', 1, '2.jpg', 2, '2007-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:29'),
(29, 'zhang12', '123456', '张十二', 1, '2.jpg', 2, '2020-01-01', '2022-10-27 16:35:33', '2022-10-27 16:36:31');
基本查询我们只需要指定从哪一张表当中来查询返回哪些字段就可以。
在基本查询的DQL语句中,不带任何的查询条件,语法如下:
select 字段1, 字段2, 字段3 from 表名;
select * from 表名;
select 字段1 [ as 别名1 ] , 字段2 [ as 别名2 ] from 表名;
select distinct 字段列表 from 表名;
-- =============================DQL:基本查询=============================
-- 1.查询指定字段 name,entrydate 并返回
select name,entrydate from tb_emp;
-- 2.查询返回所有字段
-- 推荐
select id, username, password, name, gender, image, job, entrydate, create_time, update_time from tb_emp;
-- 不推荐(原因:可读性不好不直观,性能低影响效率)
select * from tb_emp;
-- 3.查询所有员工的 name,entrydate,并起别名(姓名、入职日期)
select name as 姓名,entrydate as 入职日期 from tb_emp;
-- as关键字是可以省略的
select name 姓名,entrydate 入职日期 from tb_emp;
-- 如果别名当中有特殊符号或者说有空格,需要给这个别名加上一个引号(单引号或者双引号都可以)
select name '姓 名',entrydate '入 职 日 期' from tb_emp;
-- 4.查询已有的员工关联了哪几种职位(不要重复)
-- distinct关键字对查询去重
select distinct job from tb_emp;
案例1:查询指定字段 name,entrydate并返回
select name,entrydate from tb_emp;
案例2:查询返回所有字段
-- 2.查询返回所有字段
-- 推荐
select id, username, password, name, gender, image, job, entrydate, create_time, update_time from tb_emp;
-- 不推荐(原因:可读性不好不直观,性能低影响效率)
select * from tb_emp;
*
号代表查询所有字段,在实际开发中尽量少用(可读性不好不直观、性能低影响效率)案例3:查询所有员工的 name,entrydate,并起别名(姓名、入职日期)
-- 3.查询所有员工的 name,entrydate,并起别名(姓名、入职日期)
select name as 姓名,entrydate as 入职日期 from tb_emp;
-- as关键字是可以省略的
select name 姓名,entrydate 入职日期 from tb_emp;
-- 如果别名当中有特殊符号或者说有空格,需要给这个别名加上一个引号(单引号或者双引号都可以)
select name '姓 名',entrydate '入 职 日 期' from tb_emp;
案例4:查询已有的员工关联了哪几种职位(不要重复)
-- 4.查询已有的员工关联了哪几种职位(不要重复)
-- distinct关键字对查询去重
select distinct job from tb_emp;
语法:
select 字段列表 from 表名 where 条件列表 ; -- 条件列表:意味着可以有多个条件
常用的比较运算符如下:
比较运算符 | 功能 |
---|---|
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
= | 等于 |
<> 或 != | 不等于 |
between ..最小值. and .最大值.. | 在某个范围之内(包含最小、最大值) |
in(...) | 在in之后的列表中的值,多选一 |
like 占位符 | 模糊查询,模糊匹配(_匹配单个字符, %匹配任意个字符) |
is null | 是null , 还有is not null,不是null |
常用的逻辑运算符如下:
逻辑运算符 | 功能 |
---|---|
and 或 && | 并且 (条件列表当中的多个条件同时成立) |
or 或 || | 或者 (条件列表当中的多个条件有任意一个成立) |
not 或 ! | 非 , 不是(指的就是取反的含义) |
-- ==================DQL:条件查询==================
-- 1.查询 姓名 为 杨逍 的员工
-- 字符串要使用''或者""包含
select * from tb_emp where name = '杨逍';
-- 2.查询 id小于等于5 的员工信息
select * from tb_emp where id <= 5;
-- 3.查询 没有分配职位 的员工信息
-- 注意:查询为NULL的数据时,不能使用 = null,否则查询结果没有数据
select * from tb_emp where job is null;
-- 4.查询 有职位 的员工信息
select * from tb_emp where job is not null;
-- 5.查询 密码不等于'123456' 的员工信息
select * from tb_emp where password != '123456';
select * from tb_emp where password <> '123456';
-- 6.查询 入职日期 在'2000-01-01'(包含) 到'2010-01-01'(包含) 之家的员工信息
select * from tb_emp where entrydate >= '2000-01-01' and entrydate <= '2010-01-01';
select * from tb_emp where entrydate between '2000-01-01' and '2010-01-01';
-- 7.查询 入职时间 在'2000-01-01'(包含) 到'2010-01-01'(包含) 之间 且 性别为女 的员工信息
select * from tb_emp where entrydate between '2000-01-01' and '2010-01-01' and gender = 2;
-- 也可以用双与&&,但是在SQL语句当中一般用的都是and关键字
select * from tb_emp where entrydate between '2000-01-01' and '2010-01-01' && gender = 2;
-- 8.查询 职位是 2(讲师),3(学工主管),4(教研主管)的员工信息
-- 方式一:使用or关键字连接多个条件
select * from tb_emp where job = 2 or job = 3 or job = 4;
-- 方式二:in关键字
select * from tb_emp where job in(2,3,4);
-- 9.查询 姓名 为两个字的员工信息
-- like模糊查询,一个下划线_就代表一个字符
-- 模糊查询中,通配符/占位符"_"标识一个任意字符
select * from tb_emp where name like '__';
-- 10.查询 姓'张' 的员工信息
-- 百分号%代表的是任意个字符
-- 通配符"%"代表任意个字符(0个~多个)
-- 这就代表要查询的是姓名当中第一个字符是张,而后面是什么无所谓
select * from tb_emp where name like '张%';
分组查询就是在条件查询where之后加上一个关键字group by来指定分组的字段,如果在分组之后还要进行条件的过滤、条件的筛选,就可以在group by之后再加上一个关键字having,然后通过having这个关键字来指定分组后的过滤条件。而分组这个操作一般会伴随着聚合统计进行,比如我们要根据员工的性别来进行分组,然后分别计算男性员工多少人,女性员工多少人,那此时既需要根据gender这个字段进行分组,还要进行分组之后的聚合统计操作。
这里会涉及到数据库当中的聚合函数,所以在学习分组查询的具体语法之前我们先来学习一下MySQL当中的聚合函数。
所谓聚合函数指的就是将表当中的某一列数据作为一个整体,进行一个纵向的计算。
之前我们做的查询都是横向查询,就是根据条件一行一行的进行判断,而使用聚合函数查询就是纵向查询,它是对一列的值进行计算,然后返回一个结果值。(将一列数据作为一个整体,进行纵向计算)
我们在调用聚合函数的时候,直接写上聚合函数的名字,然后在小括号当中来指定这个聚合函数要对哪一个字段进行纵向的运算。
count :按照列去统计有多少行数据。
sum :计算指定列的数值和,如果不是数值类型,那么计算结果为0
max :计算指定列的最大值
min :计算指定列的最小值
avg :计算指定列的平均值
语法:
select 聚合函数(字段列表) from 表名 ;
注意 : 聚合函数会忽略空值,对NULL值不作为统计。
-- ======================DQL:分组查询========================
-- 聚合函数:聚合函数它是不对null值进行运算的
-- 1.统计该企业员工数量
-- 要统计总数量,用到的聚合函数就是count
-- 而count统计总数据量它总共有三种方式
-- 使用count这个聚合函数求取总数据量的三种使用方式:
-- A.count(字段)
select count(id) from tb_emp;
-- 提问:我们count这个字段,到底要count哪个字段,是任何一个字段都可以吗?
-- 由于聚合函数它是不对null值进行运算的,因此如果我们要使用count字段这种形式来统计总数据量,一定要count一个非空字段
-- B.count(常量)
-- 只要count的是一个不为null的常量都可以,因为null值不参与聚合函数的运算
select count(0) from tb_emp;
select count('A') from tb_emp;
-- C.count(*) -- 推荐使用
-- *就是通配的意思,通过count(*)就可以求取这张表的总数据量
-- 在项目开发的时候,推荐使用count(*),因为MySQL数据库底层对此专门做了优化处理
select count(*) from tb_emp;
-- 2.统计该企业最早入职的员工 -- min
-- 最早入职就是入职时间的最小值
select min(entrydate) from tb_emp;
-- 3.统计该企业最迟入职的员工 -- max
-- 最迟入职就是应该计算入职时间的最大值
select max(entrydate) from tb_emp;
-- 4.统计该企业员工 ID 的平均值 -- avg
select avg(id) from tb_emp;
-- 5.统计该企业员工的 ID 之和 -- sum
select sum(id) from tb_emp;
分组查询就是在条件查询where之后加上一个关键字group by来指定分组的字段,如果在分组之后还要进行条件的过滤、条件的筛选,不要写在where之后,要写在having之后,就可以在group by之后再加上一个关键字having,然后通过having这个关键字来指定分组后的过滤条件。而分组这个操作一般会伴随着聚合统计进行,比如我们要根据员工的性别来进行分组,然后分别计算男性员工多少人,女性员工多少人,那此时既需要根据gender这个字段进行分组,还要进行分组之后的聚合统计操作。
分组: 按照某一列或者某几列,把相同的数据进行合并输出。
分组其实就是按列进行分类(指定列下相同的数据归为一类),然后可以对分类完的数据进行合并计算。
分组查询通常会使用聚合函数进行计算。
语法:
select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件];
-- 分组
-- 1.根据性别分组,统计男性和女性员工的数量 -- count(*)
-- 没有查询条件,但是有分组字段
-- 这代表我们要将员工根据性别这个字段进行一个分组
-- 按照gender字段进行分组(gender字段下相同的数据归为一组)
-- 在分组查询当中,select之后能够返回的字段列表主要包含两类:一类是分组字段,另一类是聚合函数
-- select * from tb_emp group by gender; -- 运行报错
select gender,count(*) from tb_emp group by gender;
-- 2.要查询入职时间在'2015-01-01'(包含)以前的员工,并对结果根据职位分组,获取员工数量大于等于2的职位
-- 报错,原因:在where之后是不能使用count聚合函数的,where不能对聚合函数进行判断
-- "获取员工数量大于等于2的职位",这个条件是属于分组之后的过滤,如果是分组之后的过滤,不要写在where之后
-- 要在后面加上一个关键字having,在having之后再来写分组之后的过滤条件
-- select job,count(*) from tb_emp where entrydate <= '2015-01-01' and count(*) >= 2 group by job;
select job,count(*) from tb_emp where entrydate <= '2015-01-01' group by job having count(*) >= 2;
-- where entrydate <= '2015-01-01' -- 分组前条件
-- group by job -- 按照job字段分组
-- having count(*) >= 2; -- 分组后条件
面试题:SQL语句当中where和having之后跟上的都是查询条件,它们之间有什么样的区别?
where与having区别(面试题)
执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分 组之后对结果进行过滤。
判断条件不同:where之后的条件不能对聚合函数进行判断,而having可以。
分组查询的两点注意事项:
排序在日常开发中是非常常见的一个操作,有升序排序,也有降序排序。
排序查询对应的关键字是order by,我们只需要在分组查询group by之后跟上一个关键字order by,在order by之后指定排序的字段列表就可以了。
排序查询只需要在基本查询,条件查询,分组查询之后加上一个关键字order by,在order by之后指定排序的字段以及这个字段的排序方式。如果排序字段有多个,中间使用逗号分隔,后面再指定第二个排序字段以及第二个字段的排序方式。
语法:
select 字段列表
from 表名
[where 条件列表]
[group by 分组字段 ]
order by 字段1 排序方式1 , 字段2 排序方式2 … ;
而这个排序方式就只有两种,一种是升序,一种是降序,默认是升序。
排序方式:
ASC :升序(默认值)
DESC:降序
-- =========================排序查询=========================
-- 1.根据入职时间,对员工进行升序排序 -- asc
-- 按照entrydate字段下的数据进行升序排序
select * from tb_emp order by entrydate asc;
-- 注意事项:如果是升序,可以不指定排序方式ASC
select * from tb_emp order by entrydate; -- 默认就是ASC(升序)
-- 2.根据入职时间,对员工进行降序排序 -- desc
-- 按照entrydate字段下的数据进行降序排序
select * from tb_emp order by entrydate desc;
-- 3.根据 入职时间 对公司的员工进行 升序排序 , 如果入职时间相同, 再按照 更新时间 进行降序排序
-- 如果是多个排序字段,每个排序字段之间使用逗号分隔,后面再指定第二个排序字段
-- 在SQL语句当中,只有当第一个字段值相同的时候,第二个排序字段才会生效
-- 反之,如果是三个字段的排序,那就是前两个字段值都相同时,才会根据第三个字段排序
-- 注意事项:如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序
select * from tb_emp order by entrydate, update_time desc;
分页操作在业务系统开发时,也是非常常见的一个功能,日常我们在网站中看到的各种各样的分页条,后台也都需要借助于数据库的分页操作。数据分页展示之后,用户在查看数据的时候只需要点击对应的页码,就可以加载出这一页的数据。
如果我们一次性查询出来的数据比较多,就会进行分页展示,从而提高用户的体验。
在DQL语句当中分页查询的语法:
分页查询只需要在查询语句的后面加上一个关键字limit,然后在limit之后来指定分页参数就可以了。而limit之后所指定的分页参数有两个,一个是起始索引,一个是查询记录数,两个参数之间使用逗号分隔。
起始索引指的是我们要从哪一条记录往后进行查询,起始索引默认是从0开始的。
而查询记录数指的就是这一页我需要展示多少条数据。
分页查询语法:
select 字段列表 from 表名 limit 起始索引, 查询记录数 ;
-- ============================分页查询========================
-- 1.从 起始索引0 开始查询员工数据,每页展示5条记录
-- 从索引0开始,向后取5条记录
select * from tb_emp limit 0,5;
-- 2.查询 第1页 员工数据,每页展示5条记录
select * from tb_emp limit 0,5;
-- 如果查询的是第一页数据,起始索引可以忽略,直接简写为:limit 条数
select * from tb_emp limit 5;
-- 3.查询 第2页 员工数据,每页展示5条记录
-- 2是页码不是起始索引
-- 从索引5开始,向后取5条记录
select * from tb_emp limit 5,5;
-- 4.查询 第3页 员工数据,每页展示5条记录
-- 从索引10开始,向后取5条记录
select * from tb_emp limit 10,5;
-- 起始索引的计算公式
-- 起始索引 = (页码 - 1) * 每页展示记录数
注意事项:
1. 在分页查询时,将来前端并不会把起始索引传递过来,它传递的都是一个页码,比如我要查询第一页,第二页,第三页,第四页的数据,传递过来一个页码之后,我们在执行SQL语句的时候,需要将页码换算成对应的起始索引:起始索引 = (查询页码 - 1) * 每页展示记录数。
2. 分页查询它其实是属于数据库的方言,所谓数据库的方言指的就是各个关系型数据库之间它们不是通用的,是有不同实现的,不同数据库它们的分页实现方式也不一样,在MySQL当中用的是limit
3. 如果查询的是第一页数据,起始索引可以忽略,直接简写为:limit 条数。