DQL(数据查询)
数据库最重要功能,没有之一,并且应该根据需要对数据进行筛选以及确定以什么样的格式显示。
语法格式:
sql
[]:表示可选项
<>:表示必选项
select
[all/distinct]
<目标列表达式1> [别名],
<目标列表达式2> [别名],
...
from <表名/视图名> [别名],
<表名/视图名> [别名],
...
[where <条件表达式>]
[group by <列名>]
[having <条件表达式>]
[order by <列名> [asc/desc]]
[limit <数字/列表>]
数据准备:
sql
-- 创建数据库
create database if not exists mydb2;
use mydb2;
-- 创建商品表:
create table if not exists product(
pid int primary key auto_increment, -- 商品编号
pname varchar(20) not null, -- 商品名字
price double, -- 商品价格
category_id varchar(20) -- 商品所属分类
);
-- 向product表批量插入所有商品数据(包含家电、服饰、美妆、零食类)
insert into product(pid, pname, price, category_id)
values
-- 家电类 (c001)
(null, '海尔洗衣机', 5000, 'c001'),
(null, '美的冰箱', 3000, 'c001'),
(null, '格力空调', 5000, 'c001'),
(null, '九阳电饭煲', 5000, 'c001'),
-- 服饰类 (c002)
(null, '啄木鸟衬衣', 300, 'c002'),
(null, '恒源祥西裤', 800, 'c002'),
(null, '花花公子夹克', 440, 'c002'),
(null, '劲霸休闲裤', 266, 'c002'),
(null, '海澜之家卫衣', 180, 'c002'),
(null, '杰克琼斯运动裤', 430, 'c002'),
-- 美妆类 (c003)
(null, '兰蔻面霜', 300, 'c003'),
(null, '雅诗兰黛精华水', 200, 'c003'),
(null, '香奈儿香水', 350, 'c003'),
(null, 'SK-II神仙水', 350, 'c003'),
(null, '资生堂粉底液', 180, 'c003'),
-- 零食类 (c004)
(null, '老北京方便面', 56, 'c004'),
(null, '良品铺子海带丝', 17, 'c004'),
(null, '三只松鼠坚果', 88, null);
1. 运算符
通过运算符进行运算,可以获取到表结构以外的另一种数据,支持四种运算符,分别为:
1.1 算术运算符
| 算术运算符 | 说明 |
|---|---|
| + | 加法运算 |
| - | 减法运算 |
| * | 乘法运算 |
| / 或 DIV | 除法运算,返回商 |
| % 或 MOD | 求余运算,返回余数 |
1.2 比较运算符
| 比较运算符 | 说明 |
|---|---|
| = | 等于 |
| < 和 <= | 小于和小于等于 |
| > 和 >= | 大于和大于等于 |
| <=> | 安全的等于,两个操作数均为 NULL 时,其所得值为 1;而当一个操作数为 NULL 时,其所得值为 0 |
| <> 或!= | 不等于 |
| IS NULL 或 ISNULL | 判断一个值是否为 NULL |
| IS NOT NULL | 判断一个值是否不为 NULL |
| LEAST | 当有两个或多个参数时,返回最小值 |
| GREATEST | 当有两个或多个参数时,返回最大值 |
| BETWEEN AND | 判断一个值是否落在两个值之间 |
| IN | 判断一个值是 IN 列表中的任意一个值 |
| NOT IN | 判断一个值不是 IN 列表中的任意一个值 |
| LIKE | 通配符匹配 |
| REGEXP | 正则表达式匹配 |
1.3 逻辑运算符
| 逻辑运算符 | 说明 |
|---|---|
| NOT 或者! | 逻辑非 |
| AND 或者 && | 逻辑与 |
| OR 或者 || | 逻辑或 |
| XOR | 逻辑异或 |
1.4 位运算符(了解即可)
| 位运算符 | 说明 |
|---|---|
| | | 按位或 |
| & | 按位与 |
| ^ | 按位异或 |
| << | 按位左移 |
| >> | 按位右移 |
| ~ | 按位取反,反转所有比特 |
2. 正则表达式
字符串匹配的规则。
| 模式 | 描述 |
|---|---|
| ^ | 匹配输入字符串的开始位置。 |
| $ | 匹配输入字符串的结束位置。 |
| . | 匹配除 "\n" 之外的任何单个字符。 |
| [...] | 字符集合。匹配所包含的任意一个字符。例如,'[abc]' 可以匹配 "plain" 中的 'a'。 |
| [^...] | 负值字符集合。匹配未包含的任意字符。例如,'[^abc]' 可以匹配 "plain" 中的 'p'。 |
| p1|p2|p3 | 匹配 p1 或 p2 或 p3。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f) ood' 则匹配 "zood" 或 "food"。 |
| * | 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于 {0,}。 |
| + | 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。 |
| {n} | n 是一个非负整数。匹配确定的 n 次。例如,'o {2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。 |
| {n,m} | m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。 |
举例:
sql
-- 匹配成功返回1,否则返回0
-- ^ 在字符串开始处进行匹配
select 'abc' regexp '^a'; -- 字符串是否以a开头
select * from product where pname regexp '^海'; -- 商品是否以'海'开头
-- $ 在字符串末尾开始匹配
select 'abc' regexp 'a$'; -- 字符串是否以a结尾;
select * from product where pname regexp '水$'; -- 商品是否以'水'结尾
-- . 匹配任意字符,除换行符\n
select 'abc' regexp '.b';
select * from product where pname regexp '力.';
-- [...]匹配括号内的任意单个字符
select 'abc' regexp '[xyz]';
select 'abc' regexp '[xya]'; -- []中的任意字符串是否在前面字符串中出现
-- [^...]注意^符合只有在[]内才是取反的意思,在别的地方都是表示开始处匹配
select 'abc' regexp '[^xyz]';
select 'abc' regexp '[^xya]'; -- []中的任意字符串是否在前面字符串中出现
-- a* 匹配9个或多个a,包括空字符串。可以作为占位符使用.有没有指定字符都可以匹配到数据
select 'stab' regexp '.ta*b';
select 'stb' regexp '.ta*b';
select '' regexp 'a*'; -- 表示a可以出现0次或多次,换种方式讲:a可出现可不出现
-- a+ 匹配1个或者多个a,但是不包括空字符
select 'stab' regexp '.ta+b'; -- a要至少出现1次
select 'stb' regexp '.ta+b';
-- a? 匹配0个或者1个a
select 'stb' regexp '.ta?b';
select 'staab' regexp '.ta?b'; -- 表示a只能出现0次或者1次
-- a1|a2 匹配a1或者a2
select 'a' regexp 'a|b';
select 'a' regexp '^(a|b)';
select 'b' regexp '^(a|b)';
select 'c' regexp '^(a|b)';
-- a{m}匹配m个a
select 'staaab' regexp 'ta{3}';
select 'staaab' regexp 'a{4}';
-- a{m,}匹配m个或者更多个a
select 'staaab' regexp 'ta{3,}'; -- 表示至少出现3次,最多可以出现无限个a
-- a{m,n}匹配m到n个a,包含m和n
select 'staaab' regexp 'ta{1,4}'; -- 只需要出现1-4次直接都符合规则
select 'staaab' regexp 'ta{4,10}';
-- (abc) abc作为一个序列匹配,不用括号括起来都是用单个字符去匹配,如果要把多个字符作为一个整体去匹配就需要用到括号,所以括号适合上面的所有情况。
select 'xababy' regexp 'x(ab)*y'; -- ()表示一个整体
select 'xababy' regexp 'x(ab){3,4}y';
3. 条件查询
举例:
sql
-- 算术运算符
select 6 + 2;
select 6 - 2;
select 6 * 2;
select 6 / 2;
select 6 % 2;
-- 将所有商品价格 +10 元
select pname, price + 10 as new_price from product;
-- 将所有商品价格上调 10%
select pname, price * 1.1 as new_price from product;
-- 查询商品名称为"海尔洗衣机"的商品所有信息:
select * from product where pname='海尔洗衣机';
-- 查询价格为800商品
select pname from product where price = 800;
-- 查询价格不是800的所有商品
select pname from product where price != 800;
select pname from product where price <> 800;
select pname from product where not(price = 800);
-- 查询商品价格大于60元的所有商品信息
select * from product where price >= 60;
-- 查询商品价格在200到1000之间所有商品
select pname from product where price between 200 and 1000;
select pname from product where price >= 200 && price <= 1000;
select pname from product where price >= 200 and price <= 1000;
-- 查询商品价格是200或800的所有商品
select pname from product where price = 200 || price = 800;
select pname from product where price = 200 or price = 800;
select pname from product where price in(200,800);
-- 查询含有'鞋'字的所有商品
select pname from product where pname like '%鞋%'; -- %:用来匹配任意字符
-- 查询以'海'开头的所有商品
select * from product where pname like '海%';
-- 查询第二个字为'蔻'的所有商品
select * from product where pname like '_蔻%'; -- _:用来匹配单个字符
-- 查询category_id为null的商品
select pname from product where category_id is null;
-- 查询category_id不为null分类的商品
select pname from product where category_id is not null;
-- 使用 least 求最小值
select least(10,20);
select least(10,5,10) as small_number;
select least(10,5,null,40) as small_number; --如果有个值为null,则不会进行比较,直接输出null
-- 使用 greatest 求最大值
select greatest(10,20,40) as big_number;
select greatest(10,20,null,40) as big_number; --如果有个值为null,则不会进行比较,直接输出null
4. 排序查询
使用order by子句设定按照哪个字段哪种方式(asc/desc)进行排序。
语法格式:
sql
select
字段1,字段2,...
from
表名
order by
字段1 [asc/desc],字段2 [asc/desc],...;
特点:
- asc代表升序,desc代表降序,不写默认升序。
- order by 支持单个/多个字段、表达式、函数、别名。
举例:
sql
-- 1.使用价格排序(降序)
select * from product order by price desc;
-- 2.在价格排序(降序)的基础上,以分类排序(降序)
select * from product order by price desc,category_id desc;
-- 3.显示商品的价格(去重复),并排序(降序)
select distinct price from product order by price desc;
5. 聚合查询
聚合查询是对一列的值进行计算,然后返回一个单一的值。
| 聚合函数 | 作用 |
|---|---|
| count() | 统计指定列不为 NULL 的记录行数; |
| sum() | 计算指定列的数值和,如果指定列类型不是数值类型,那么计算结果为 0 |
| max() | 计算指定列的最大值,如果指定列是字符串类型,那么使用字符串排序运算; |
| min() | 计算指定列的最小值,如果指定列是字符串类型,那么使用字符串排序运算; |
| avg() | 计算指定列的平均值,如果指定列类型不是数值类型,那么计算结果为 0 |
⚠️:聚合函数对于NULL值的处理:
-
count(*)会统计表中所有的记录行数,包括值为NULL的行;而count(列名)只统计该列不为NULL的记录数。 -
sum()、avg()等函数在遇到非数值类型或NULL值时,通常会忽略这些值,而不是直接返回 0。
举例:
sql
-- 1 查询商品的总条数
select count(pname) from product;
-- 2 查询价格大于200商品的总条数
select count(pname) from product where price > 200;
-- 3 查询分类为'c001'的所有商品的价格总和
select sum(pname) from product where category_id = 'c001';
-- 4 查询商品的最大价格
select max(price) from product;
-- 5 查询商品的最小价格
select min(price) from product;
select max(price) as max_price,min(price) as min_price from product;
-- 6 查询分类为'c002'所有商品的平均价格
select avg(price) as avg_price from product order by category_id = 'c002';
6. 分组查询
使用group by对查询信息进行分组。
语法格式:
sql
select
字段1,字段2...
from
表名
group by
分组字段
having
分组条件;
⚠️:
- 使用group by,只能出现分组的字段和统计函数,其他的字段不能出现。
- 不分组筛选用where,分组筛选用having。
- group by子句用来分组where子句的输出。
- having子句用来分组的结果进行筛选行。
举例:
sql
-- 1 统计各个分类商品的个数
select category_id,count(pname) from product group by category_id;
-- 2.统计各个分类商品的个数,且只显示个数大于4的信息
select
category_id,count(pname) as cp_number
from
product
group by
category_id
having
cp_number > 4
order by
cp_numver;
7. 分页查询
由于数据量很大,因此需要对数据采取分页显示方式,使用limit,例如数据共30条,每页显示5条,则第一页为1-5条;第二页为6-10条。
语法格式:
sql
m:整数,表示从第几条索引开始
n:整数,表示查询多少条数据
select
字段1,字段2...
from
表名
limit
[n/m,n]
举例:
sql
-- 查询product表的前5条记录
select * from product limit 5;
-- 从第4条开始显示,显示5条
select * from product limit 3,5;
8. 练习
数据准备1:
sql
CREATE TABLE student(
id INT,
name VARCHAR(20),
gender VARCHAR(20),
chinese INT,
english INT,
math INT
);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(1, '张明', '男', 89, 78, 90);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(2, '李进', '男', 67, 53, 95);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(3, '王五', '女', 87, 78, 77);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(4, '李一', '女', 88, 98, 92);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(5, '李财', '男', 82, 84, 67);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(6, '张宝', '男', 55, 85, 45);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(7, '黄蓉', '女', 75, 65, 30);
INSERT INTO student(id, NAME, gender, chinese, english, math) VALUES(7, '黄蓉', '女', 75, 65, 30);
举例:
sql
-- 查询表中所有学生的信息。
select * from student;
-- 查询表中所有学生的姓名和对应的英语成绩。
select name,english from student;
-- 过滤表中重复数据。
select distinct * from student;
-- 统计每个学生的总分。
select name, sum(chinese + english + math) as total_score from student;
-- 在所有学生总分数上加10分特长分。
select name, sum(chinese + english + math) + 10 as new_sum_score from student;
-- 使用别名表示学生分数。
select name, (chinese + english + math) as score from student;
-- 查询英语成绩大于90分的同学
select * from student where english > 90;
-- 查询总分大于200分的所有同学
select name, sum(chinese + english + math) as sum_score from student where sum_score > 200;
-- 查询英语分数在 89-90之间的同学。
select * from student where english between 89 and 90;
-- 查询英语分数不在 80---90之间的同学。
select * from student where english not between 80 and 90;
-- 查询数学分数为89,90,91的同学。
select * from student where math=89 || math=90 || math=91;
-- 查询所有姓李的学生英语成绩。
select name,english from student where name like '李%';
-- 查询数学分80并且语文分80的同学。
select * from student where math=80 and chinese = 80;
-- 查询英语80或者总分200的同学
select * from student where english=80 || (chinese + english + math) =200;
-- 对数学成绩降序排序后输出。
select * from student order by math desc;
-- 对总分排序后输出,然后再按从高到低的顺序输出
select * from student order by (chinese + english + math) desc;
-- 对姓李的学生成绩排序输出
select * from student where name like '李%' order by chinese,english,math;
-- 查询男生和女生分别有多少人,并将人数降序排序输出
select gender,count(gender) as count_gender from student group by gender order by count_gender desc;
数据准备2:
sql
use mydb2;
CREATE TABLE emp(
empno INT, -- 员工编号
ename VARCHAR(50), -- 员工名字
job VARCHAR(50), -- 工作名字
mgr INT, -- 上级领导编号
hiredate DATE, -- 入职日期
sal INT, -- 薪资
comm INT, -- 奖金
deptno INT -- 部门编号
);
INSERT INTO emp VALUES(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
INSERT INTO emp VALUES(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
INSERT INTO emp VALUES(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
INSERT INTO emp VALUES(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
INSERT INTO emp VALUES(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
INSERT INTO emp VALUES(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
INSERT INTO emp VALUES(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);
INSERT INTO emp VALUES(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);
INSERT INTO emp VALUES(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
INSERT INTO emp VALUES(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
INSERT INTO emp VALUES(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);
INSERT INTO emp VALUES(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
INSERT INTO emp VALUES(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
INSERT INTO emp VALUES(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);
举例:
sql
-- 1、按员工编号升序排列不在19号部门工作的员工信息
select * from emp
where not in(deptno = 19)
order by empno;
-- 2、查询姓名第二个字母不是"A"且薪水大于1000元的员工信息,按年薪降序排列
select * from emp
where ename not like '_A%' && sal > 1000
order by (12 * sal + comm) desc;
-- 3、求每个部门的平均薪水
select deptno,avg(sal) as avg_sal from emp
group by deptno;
-- 4、求各个部门的最高薪水
select
deptno,max(sal) as max_sal
from
emp
group by
deptno;
-- 5、求每个部门每个岗位的最高薪水
select
deptno,job,max(sal) as max_sal
from
emp
group by
deptno,job
order by
deptno;
-- 6、求平均薪水大于2000的部门编号
select
deptno,avg(sal) as avg_sal
from
emp
group by
deptno
having
avg_sal >2000;
-- 7、将部门平均薪水大于1500的部门编号列出来,按部门平均薪水降序排列
select
deptno,avg(sal) as avg_sal
from
emp
group by
deptno
having
avg_sal > 1500
order by avg_sal desc;
-- 8、选择公司中有奖金的员工姓名,工资
select
ename,sal
from
emp
where
comm != NULL;
-- 9、查询员工最高工资和最低工资的差距
select max(sal) - min(sal) from emp;