- 表的增删查改简称CRUD:Create(新增),Retrieve(查找),Update(修改),Delete(删除)
- CRUD的操作对象是对表当中的数据,是典型的数据操作语言 (DML)
一. 表的增删查改
1. Create
INSERT [INTO] table_name [(column1 [, column2] ...)] VALUES (value_list1) [, (value_list2)] ...;
- SQL 中大写的表示关键字,[ ]中代表的是可选项。
- SQL 中的每个 value_list 都表示插入的一条记录,每个 value_list 都由若干待插入的列值组成。
- SQL 中的 column 列表,用于指定每个 value_list 中的各个列值应该插入到表中的哪一列。
create table student(
id int unsigned primary key auto_increment comment '主键',
name varchar(20) not null comment '姓名',
sn int unsigned unique key comment '学号',
qq varchar(32) unique key comment 'QQ'
);
desc student;
1. 单行数据 + 全列插入
如果插入数据时不指定column列表,表示按照表中默认的列顺序进行全列插入,因此插入的每条记录中的列值需要按表列顺序依次列出,如下:
insert into student values (1, '橙留香', 11, '111');
insert into student values (2, '菠萝吹雪', 22, '222');
insert into student values (3, '陆小果', 33, '333');
select * from student;
2. 多行数据 + 指定列插入
如果一次向表中插入多条记录,插入的多条记录之间使用逗号隔开,并且插入记录时可以只指定某些列进行插入,如下:
insert into student (name, sn, qq) values ('上官子怡', 44, '444'), ('梨花诗', 55, '555'), ('花如意', 66, '666');
select * from student;
3. 插入相同数据 + 同步更新
由于 主键 / 唯一键 对应的值已经存在而导致插入失败。
# 主键冲突: 插入失败
insert into student (id, name, sn, qq) values (6, '小果叮', 77, '777');
# 唯一键冲突: 插入失败
insert into student (id, name, sn, qq) values (7, '小果叮', 66, '777');
可以选择性的进行同步更新操作:
- 如果表中没有冲突数据,则直接插入数据。
- 如果表中有冲突数据,则将表中的数据进行更新。
INSERT ... ON DUPLICATE KEY UPDATE column1=value1 [, column2=value2] ...;
- SQL中大写的表示关键字,[ ]中代表的是可选项。
- SQL中INSERT之后语法与之前的INSERT语句相同。
- UPDATE后面的column=value,表示当插入记录出现冲突时需要更新的列值。
insert into student (id, name, sn, qq) values (7, '小果叮', 77, '777');
select * from student;
主键冲突且唯一键不冲突且插入数据与该数据不相同时:插入操作变为修改操作,插入成功!
insert into student values (7, '贼眉鼠眼', 77, '777')
on duplicate key update name='贼眉鼠眼', sn=77, qq='777';
select * from student;
主键冲突时:将插入操作变成更新操作,但发现更新过程中与唯一键发生冲突,插入失败!
insert into student values (7, '认贼作父', 66, '777')
on duplicate key update name='认贼作父', sn=66, qq='777';
主键发生冲突且插入的数据与该数据相同时:不受影响
insert into student values (7, '贼眉鼠眼', 77, '777')
on duplicate key update name='贼眉鼠眼', sn=77, qq='777';
执行插入更新的 SQL 后,可以通过受影响的数据行数来判断本次数据的插入情况:
- 0 rows affected:表中有冲突数据,但冲突数据的值和指定更新的值相同。
- 1 row affected:表中没有冲突数据,数据直接被插入。
- 2 rows affected:表中有冲突数据,并且数据已经被更新。
3. 替换数据
- 主键 或者 唯一键 没有冲突,则直接插入。
- 主键 或者 唯一键 如果冲突,则删除后再插入。
要达到上述效果,只需要在插入数据时将 SQL 语句中的 INSERT 改为 REPLACE 即可,比如:
唯一键没有冲突:直接插入
replace into student values (8, '认贼作父', 88, '888');
select * from student;
唯一键冲突:删除后再插入
replace into student values (8, '乱臣贼子', 88, '888');
select * from student;
- replace 较于 update 操作更为简单,推荐。
2. Retrieve
SELECT
[DISTINCT] {* | {column [, column] ...}}
[FROM table_name]
[WHERE ...]
[ORDER BY column [ASC | DESC], ...]
[LIMIT ...]
- SQL 中大写的表示关键字,[ ] 中代表的是可选项。
- { } 中的 | 代表可以选择左侧的语句或右侧的语句。
1. select + from:从表中选择列
create table exam(
id int unsigned primary key auto_increment comment '主键',
name varchar(20) not null comment '学生姓名',
Chinese float default 0.0 comment '语文成绩',
math float default 0.0 comment '数学成绩',
English float default 0.0 comment '英语成绩'
);
insert into exam (name, Chinese, math, English) values
('喜羊羊', 90, 99, 94),
('美羊羊', 95, 86, 98),
('懒羊羊', 35, 24, 48),
('沸羊羊', 60, 50, 64),
('暖羊羊', 95, 86, 90);
desc exam;
全列查询
select * from exam;
通常情况下不建议使用 * 进行全列查询的原因:
- 查询的列越多,意味着需要传输的数据量越大,且不方便查看数据。
- 可能会影响到索引的使用。
指定列查询
select name, math from exam;
查询字段是表达式
select name, Chinese + math + English from exam;
为查询字段取别名
select name, Chinese + math + English 总成绩 from exam;
select name 姓名, math 数学成绩, Chinese + math + English 总成绩 from exam;
2. distinct:行数据去重
查询结果去重
select Chinese from exam;
select distinct Chinese from exam;
3. where:行数据选择
比较运算符 | 说明 |
---|---|
>、>=、<、<= | 大于、大于等于、小于、小于等于 |
= | 等于,NULL 不安全。NULL = NULL 的结果是 NULL |
<=> | 等于,NULL 安全。NULL = NULL 的结果是 true(1) |
!=、<> | 不等于,NULL 不安全 |
between x and y | 范围匹配:[x,y]。如果 value 在 [x,y] 区间内,返回 true(1) |
in(option1,option2…) | 如果是 option 中的任意一个,返回 true(1) |
is NULL | 是 NULL |
is not NULL | 不是 NULL |
like | 模糊匹配。% 表示任意多个(包括 0 个)任意字符。_ 表示任意一个字符。 |
逻辑运算符 | 说明 |
---|---|
and | 多个条件必须都为 TRUE(1),结果才是 TRUE(1) |
or | 任意一个条件为 TRUE(1),结果为 TRUE(1) |
not | 条件为 TRUE(1),结果为 FALSE(0) |
查询:英语不及格的同学及英语成绩。
先用 select 查询列,再用 where 查询行。
select name, English from exam where English < 60;
查询:语文成绩在 [80, 90] 分的同学及语文成绩。
# 方法一
select name, Chinese from exam where Chinese >= 80 and Chinese <= 90;
# 方法二
select name, Chinese from exam where Chinses between 80 and 90;
查询:数学成绩是 [50,59] 或者 [90,99] 区间内的同学及数学成绩。
select name, math from exam where (math between 50 and 59) or (math between 90 and 99);
查询:数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩。
# 方法一
select name, math from exam where math = 58 or math = 59 or math = 98 or math = 99;
# 方法二
select name, math from exam where math in(58, 29, 98, 99);
查询:姓喜的同学 和 喜某同学。
insert into exam (name, Chinese, math, English) values ('喜羊', 90, 99, 94);
insert into exam (name, Chinese, math, English) values ('喜', 90, 99, 94);
select * from exam;
select name from exam where name like '喜%';
select name from exam where name like '喜_';
- %来匹配任意个字符 (包括0个)
- _来匹配单个字符。
查询:语文成绩高于英语成绩的同学。
select name, Chinese, English from exam where Chinese > English;
查询:总成绩在 200 分以下的同学。
select name, (Chinese + math + English) 总成绩 from exam where (Chinese + math + English) < 200;
select name, (Chinese + math + English) 总成绩 from exam where 总成绩 < 200;
select name, 总成绩 from exam where (Chinese + math + English) 总成绩 < 200;
结论:where 子句不能出现别名。
查询:语文成绩 > 80 并且 不姓喜的同学。
select name, Chinese from exam where Chinese > 80 and name not like '喜%';
查询:姓懒同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80。
select name, Chinese, math, English, (Chinese + math + English) 总成绩 from exam
where name like '懒%' or (Chinese + math + English > 200 and Chinese < math and English > 80);
NULL 的查询
create table testnull(
name varchar(20),
id int
);
insert into testnull values ('曹操', 1);
insert into testnull values ('刘备', null);
insert into testnull values ('孙权', 2);
insert into testnull values (null, null);
insert into testnull values ('', 3);
select * from testnull;
select * from testnull where name is not null;
select * from testnull where name is null;
select * from testnull where name = '';
NULL 的比较
select null = null, null <=> null, null != null, null <> null;
select null = 0, null = 1;
select null is null, null is not null;
- 使用 =、!=、<> 比较 null 是不安全的,结果是 null,可以使用安全的 <==>
- 也可以使用 is null、is not null 比较 null
4. order by:行数据排序
SELECT ... FROM table_name [WHERE ...] ORDER BY column [ASC | DESC] [, ...];
- SQL中大写的表示关键字,[ ]中代表的是可选项。
- ASC和DESC分别代表的是排升序和排降序,默认为ASC。
注意: 如果查询 SQL 中没有 order by 子句,那么返回的顺序是未定义的。
delete from exam where name='喜';
delete from exam where name='喜羊';
select * from exam;
查询:同学及数学成绩,按数学成绩升序 / 降序显示。
select name, math from exam order by math desc; # 降序
select name, math from exam order by math asc; # 升序
select name, math from exam order by math; # 默认升序
NULL 参与排序,且比任何值都小。
select name from testnull order by name asc;
查询:同学各门成绩,依次按 数学降序,英语升序(数学相等时),语文升序(英语相等时)的方式显示。
select name, math, English, Chinese from exam order by math desc, English desc, Chinese asc;
查询:同学及总分,由高到低。
select name, Chinese + math + English 总成绩 from exam order by 总成绩 asc;
SQL 语句的执行顺序
总结:SQL 语句执行的先后顺序,决定是否能识别别名。
查询:姓喜的同学或者姓美的同学数学成绩,结果按数学成绩由高到低显示。
select name, math from exam where name like '喜%' or name like '美%' order by math desc;
SQL 语句的执行顺序
5. limit:行数据筛选
# 从第0条记录开始,向后筛选出n条记录
SELECT ... FROM table_name [WHERE ..] [ORDER BY ...] LIMIT n;
# 从第s条记录开始,向后筛选出n条记录
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT s, n;
# 从第s条记录开始,向后筛选出n条记录
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT n OFFSET s;
- SQL 中大写的表示关键字,[ ]中代表的是可选项。
- 查询 SQL 中各语句的执行顺序为:from、where、select、order by、limit。
- limit 子句在筛选记录时,记录的下标从0开始。
从表中筛选出前两行数据
select * from exam limit 2;
select * from exam limit 0, 2;
select * from exam limit 2 offset 0;
注意:第一行数据的下标为0。
按 id 进行分页,每页 3 条记录,分别显示 第 1、2 页
select * from exam limit 0, 3;
select * from exam limit 3, 3;
# 或者
select * from exam limit 3 offset 0;
select * from exam limit 3 offset 3;
建议:对未知表进行查询时,最好加一条 limit 3,避免因为表中数据过大,查询全表数据导致数据库卡死。按 id 进行分页,每页 3 条记录,分别显示 第 1、2、页。
SQL 语句的执行顺序
select name, Chinese + math + English as 总成绩 from exam
where Chinese + math + English < 200 order by 总成绩 desc limit 2;
只有数据准备好了,才能显示结果,limit 的本质的功能是 “显示”。
3. Update
1. update:行数据更新
UPDATE table_name SET column1=expr1 [, column2=expr2] ... [WHERE ...] [ORDER BY ...] [LIMIT ...];
- SQL中大写的表示关键字,[ ]中代表的是可选项。
- SQL中的column=expr,表示将记录中列名为column的值修改为expr
- 在修改数据之前需要先找到待修改的记录,update语句中的where、order by和limit就是用来定位数据的。
将懒羊羊同学的数学成绩变更为 36 分
select name, math from exam where name = '懒羊羊'; #查看原数据
update exam set math = 36 where name = '懒羊羊'; #更新懒羊羊的数学成绩
select name, math from exam where name = '懒羊羊'; #查看更新后的数据
注意:update通常需要搭配where使用,否则会将所有的数据进行修改,此行为非常危险,不亚于delete
将沸羊羊同学的数学成绩变更为 60 分,语文成绩变更为 70 分
select name, math, Chinese from exam where name = '沸羊羊';
update exam set math = 60, Chinese = 70 where name = '沸羊羊';
select name, math, Chinese from exam where name = '沸羊羊';
将总成绩倒数前 2 的两位同学的数学成绩加上 30 分
# 先查看总成绩倒数前 2 的两位同学的数学成绩
select name, math, Chinese + math + English 总成绩 from exam order by 总成绩 asc limit 0, 2;
# 将总成绩倒数前 2 的两位同学的数学成绩加上 30 分
update exam set math = math + 30 order by Chinese + math + English asc limit 2;
# 再查看总成绩倒数前 2 的两位同学的数学成绩
select name, math, Chinese + math + English 总成绩 from exam order by 总成绩 asc limit 0, 2;
# 数据恢复
update exam set math = math - 30 order by Chinese + math + English asc limit 2;
将所有同学的语文成绩更新为原来的 2 倍
select name, Chinese from exam;
update exam set Chinese = Chinese * 2;
select name, Chinese from exam;
# 数据恢复
update exam set Chinese = Chinese / 2;
4. Delete
1. delete:行数据删除
DELETE FROM table_name [WHERE ...] [ORDER BY ...] [LIMIT ...];
- SQL中大写的表示关键字,[ ]中代表的是可选项。
- 在删除数据之前需要先找到待删除的记录,delete语句中的where、order by和limit就是用来定位数据的。
删除喜羊羊同学的考试成绩:
select * from exam where name = '喜羊羊';
delete from exam where name = '喜羊羊';
select * from exam where name = '喜羊羊';
# 恢复数据
insert into exam (name, Chinese, math, English) values ('喜羊羊', 90, 99, 94);
删除总成绩倒数第一的同学
# 先查看总成绩倒数第一的同学
select name, Chinese + math + English 总成绩 from exam order by 总成绩 asc limit 0, 1;
# 删除总成绩倒数第一的同学
delete from exam order by Chinese + math + English asc limit 1;
# 再查看总成绩倒数第一的同学
select name, Chinese + math + English 总成绩 from exam order by 总成绩 asc limit 0, 1;
# 恢复数据
insert into exam (name, Chinese, math, English) values ('懒羊羊', 35, 36, 48);
删除整张表中的数据
# 准备测试表
create table test_delete(
id int primary key auto_increment,
name varchar(20)
);
# 插入测试数据
insert into test_delete (name) values ('青龙'), ('白虎'), ('朱雀'), ('玄武');
# 查看测试数据:青龙、白虎、朱雀、玄武的id依次为:1、2、3、4
select * from test_delete;
# 查看表结构,其中 AUTO_INCREMENT 等于5
show create table test_delete \G
# 删除表中所有的数据
delete from test_delete;
# 查看表结构,其中 AUTO_INCREMENT 依旧等于5
show create table test_delete\G
# 插入测试数据
insert into test_delete (name) values ('青龙'), ('白虎'), ('朱雀'), ('玄武');
# 查看测试数据:青龙、白虎、朱雀、玄武的id依次为:5、6、7、8
select * from test_delete;
# 查看表结构,其中 AUTO_INCREMENT 等于9
show create table test_delete \G
注意:删除整表操作要慎用!
2. truncate:截断表
TRUNCATE [TABLE] table_name;
注意:这个操作慎用!
- SQL 中大写的表示关键字,[ ]中代表的是可选项。
- truncate 只能对整表操作,不能像 delete 一样针对部分数据操作。
- truncate 实际上不对数据操作,所以比 delete 更快。
- truncate 在删除数据时不经过真正的事务,所以无法回滚,delete 需要经过事务。
- truncate 会重置 AUTO_INCREMENT=n 字段,重新从1开始。
MySQL 中的日志
- bin log:用于记录数据库的所有数据变更操作,包括数据的插入、更新和删除等。
- redo log:数据库意外停止(如停电、系统崩溃等)后,通过 redo log 可以将未完成的事务重新执行一遍,确保数据的一致性。
- indo log:当事务执行失败或用户主动回滚时,根据 undo log 中的反向操作记录,可以撤销事务对数据库所做的修改,将数据库恢复到事务开始前的状态。
创建一张测试表,表中包含一个自增长的主键 id 和姓名
create table test_truncate(
id int primary key auto_increment,
name varchar(20)
);
insert into test_truncate (name) values ('青龙'), ('白虎'), ('朱雀'), ('玄武');
select * from test_truncate;
show create table test_truncate \G
truncate test_truncate;
insert into test_truncate (name) values ('青龙'), ('白虎'), ('朱雀'), ('玄武');
select * from test_truncate;
show create table test_truncate \G
注意: 截断表操作要慎用!
5. 插入查询结果
INSERT [INTO] table_name [(column1 [, column2] ...)] SELECT ... [WHERE ...] [ORDER BY ...] [LIMIT ...];
- SQL中大写的表示关键字,[ ]中代表的是可选项。
- SQL的作用是将筛选出来的记录插入到指定的表当中。
- SQL中的column,表示将筛选出的记录的各个列插入到表中的哪一列。
删除表中的的重复复记录,重复的数据只能有一份
create table duplicate_table(
id int,
name varchar(20)
);
insert into duplicate_table values
(100, 'aaa'),
(100, 'aaa'),
(200, 'bbb'),
(200, 'bbb'),
(200, 'bbb'),
(300, 'ccc');
# 查询表中非重复的数据:正是删除后的结果
select distinct * from duplicate_table;
# 1.创建相同结构的表duplicate_table_tmp
create table duplicate_table_tmp like duplicate_table;
# 2.将duplicate_table中非重复的数据插入到表duplicate_table_tmp中
insert into duplicate_table_tmp select distinct * from duplicate_table;
# 3.修改表的名称
rename table duplicate_table to old_duplicate_table;
rename table duplicate_table_tmp to duplicate_table;
# 4.查看最终结果
select * from duplicate_table;
二. 聚合函数
函数 | 说明 |
---|---|
cout([distinct] expr) | 返回查询到的数据的 数量 |
sum([distinct] expr) | 返回查询到的数据的 总和,不是数字没有意义 |
avg([distinct] expr) | 返回查询到的数据的 平均值,不是数字没有意义 |
max([distinct] expr) | 返回查询到的数据的 最大值,不是数字没有意义 |
min([distinct] expr) | 返回查询到的数据的 最小值,不是数字没有意义 |
聚合函数可以在 select 语句中使用,此时select每处理一条记录时都会将对应的参数传递给这些聚合函数。
select * from exam;
统计班级共有多少同学
select count(*) from exam;
select count(1) from exam;
select count(*) 总人数 from exam;
select count(math) 数学成绩人数 from exam;
统计语文成绩分数的个数
select count(distinct Chinese) 不同语文成绩的个数 from exam;
统计数学成绩的总分
select sum(math) 数学成绩总分 from exam;
统计数学成绩的平均分
select sum(math) / count(*) 数学成绩的平均分 from exam;
select avg(math) 数学成绩的平均分 from exam;
统计数学不及格的人数
select count(*) 数学不及格人数 from exam where math < 60;
统计三门成绩的平均分
select avg(Chinese + math + English) 三门成绩的平均分 from exam;
返回英文成绩最高分:
select max(English) 英文最高分 from exam;
返回数学成绩70分以上的最低分:
select min(math) res from exam where math > 70;
三. 分组查询
创建一个雇员信息表:emp员工表、dept部门表、salgrade工资等级表 (来自 oracle 9i 的经典测试表)
# scott_data.sql
DROP database IF EXISTS `scott`;
CREATE database IF NOT EXISTS `scott` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `scott`;
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`deptno` int(2) unsigned zerofill NOT NULL COMMENT '部门编号',
`dname` varchar(14) DEFAULT NULL COMMENT '部门名称',
`loc` varchar(13) DEFAULT NULL COMMENT '部门所在地点'
);
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
`empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',
`ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',
`job` varchar(9) DEFAULT NULL COMMENT '雇员职位',
`mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',
`hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',
`sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',
`comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',
`deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);
DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (
`grade` int(11) DEFAULT NULL COMMENT '等级',
`losal` int(11) DEFAULT NULL COMMENT '此等级最低工资',
`hisal` int(11) DEFAULT NULL COMMENT '此等级最高工资'
);
insert into dept (deptno, dname, loc)
values (10, 'ACCOUNTING', 'NEW YORK');
insert into dept (deptno, dname, loc)
values (20, 'RESEARCH', 'DALLAS');
insert into dept (deptno, dname, loc)
values (30, 'SALES', 'CHICAGO');
insert into dept (deptno, dname, loc)
values (40, 'OPERATIONS', 'BOSTON');
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7844, 'TURNER', 'SALESMAN', 7698,'1981-09-08', 1500, 0, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);
insert into salgrade (grade, losal, hisal) values (1, 700, 1200);
insert into salgrade (grade, losal, hisal) values (2, 1201, 1400);
insert into salgrade (grade, losal, hisal) values (3, 1401, 2000);
insert into salgrade (grade, losal, hisal) values (4, 2001, 3000);
insert into salgrade (grade, losal, hisal) values (5, 3001, 9999);
# 将 sql 文件导入 MySQL 数据库中
source /home/xzy/scott_data.sql;
# 使用该数据库
use scott;
# 查看3张表
desc dept;
select * from dept;
desc emp;
select * from emp;
desc salgrade;
select * from salgrade;
1. group by:分组
SELECT column1 [, column2], ... FROM table_name [WHERE ...] GROUP BY column [, ...] [order by ...] [LIMIT ...];
- SQL中大写的表示关键字,[ ]中代表的是可选项。
- 查询SQL中各语句的执行顺序为:where、group by、select、order by、limit
- group by后面的列名,表示按照指定列进行分组查询。
显示每个部门员工的最高工资和平均工资
# 员工的最高工资和平均工资
select max(sal) 最高工资, avg(sal) 平均工资 from emp;
# 通过deptno部门编号的不同,将员工划分为不同的部门,获取每个部门员工的最高工资和平均工资
select deptno, max(sal) 最高工资, avg(sal) 平均工资 from emp group by deptno;
- 指定列名进行分组,实际是用该列不同的行数据来进行分组的,分组的列名 deptno,同一个组内的 deptno 值一定是相同的,可以被聚合统计。
- 分组就是把一组按照条件拆成了多个组,进行各自组内的统计。分组类似分表,将一张表按照条件在逻辑上拆成了多个子表,然后分别对各自的子表进行聚合统计。
显示每个部门的每种岗位的平均工资和最低工资
select deptno, job, avg(sal) 平均工资, min(sal) 最低工资 from emp group by deptno, job;
- 一般来说,只有在 group by 后面出现的列名称,才能在 select 后面出现,否则语法错误。
- 但是聚合函数可以出现,原因是分组就是为了聚合统计的。
select ename, deptno, job, avg(sal) 平均工资, min(sal) 最低工资 from emp group by ename, deptno, job;
2. having:条件筛选
SELECT ... FROM table_name [WHERE ...] [GROUP BY ...] [HAVING ...] [order by ...] [LIMIT ...];
- SQL 中大写的表示关键字,[ ]中代表的是可选项。
- SQL 中各语句的执行顺序为:where、group by、select、having、order by、limit。
- having 子句中可以指明一个或多个筛选条件。
显示平均工资低于2000的部门和它的平均工资
先统计出每一个部门的平均工资,再筛选出平均工资低于2000的部门。
select deptno, avg(sal) 平均工资 from emp group by deptno;
select deptno, avg(sal) 平均工资 from emp group by deptno having 平均工资 < 2000;
显示部门编号为 10 的平均工资
select deptno, avg(sal) 平均工资 from emp group by deptno;
select deptno, avg(sal) 平均工资 from emp group by deptno having deptno = 10;
注意:
- having 子句可以同时包含聚合函数和非聚合列。
- 但是 having 子句中的非聚合列必须出现在 group by 子句中。
3. having 和 where 的区别
- where 子句放在表名后面,而 having 子句必须搭配 group by 子句使用,放在 group by 子句的后面。
- where 子句是对整表的数据进行筛选,having 子句是对分组后的数据进行筛选。
- where 子句中不能使用聚合函数和别名,而 having 子句中可以使用聚合函数和别名。
显示平均工资低于2000的部门和它的平均工资,其中SMITH员工不参与统计
select deptno, job, avg(sal) 平均工资 from emp where ename != 'SMITH' group by deptno, job having 平均工资 < 2000;
注意:这里 having 可以引用 select 中的别名,但是 where 不能引用 select 中的别名。
- 不要单纯的认为,只有磁盘上表结构导入到 MySQL,真实存在的表,才叫表。
- 中间筛选出来的,包括最终结果,全部都是逻辑上的表,MySQL 一切皆表。
- 只有处理好单表的 “增删查改”,所有的 SQL 场景,全部都能用统一的方式进行。
select * from emp where ename = 'SMITH';
select * from emp having ename = 'SMITH';
having 将其看作聚合统计之后的表,进行条件筛选,可以替换为 where,但是不建议。
SQL 查询的逻辑执行顺序是:
- from:确定数据来源表。
- where:对表中的行进行过滤。
- group by:将数据按指定列分组。
- having:对分组后的结果进行过滤。
- select:选择需要返回的列或表达式。
- order by:对结果进行排序。
- limit:限制返回的行数或跳过指定数量的行。
四. SQL 实战
- 牛客:批量插入数据
- 牛客:找出所有员工当前薪水salary情况
- 牛客:查找最晚入职员工的所有信息
- 牛客:查找入职员工时间排名倒数第三的员工所有信息
- 牛客:查找薪水记录超过15条的员工号emp_no以及其对应的记录次数t
- leetcode:查找重复的电子邮箱
- leetcode:大的国家
- leetcode:第二高的薪水
面试题 SQL 查询中各个关键字的执行先后顺序:
from > on > join > where > group by > with > having > select > distinct > order by > limit