MySQL表的增删查改

文章详细介绍了MySQL中的数据操作,包括如何插入数据(单行、多行、更新和替换),查询数据(全列、指定列、表达式、where子句、排序和分页),更新数据(指定条件修改)和删除数据(全表或条件删除)。同时,文章还讨论了去重表数据的方法和聚合函数的使用,如COUNT、SUM、AVG、MAX和MIN。最后,文章通过案例展示了分组聚合统计(GROUPBY子句)的应用。
摘要由CSDN通过智能技术生成

MySQL基本查询

表的增删改查:CRUD

  • Create(创建)包含insert
  • Retrieve(读取)包含select
  • Update(更新)
  • Delete(删除)

一.增加数据Create

主要是insert。

创建一个学生表:

image-20230726194102971

1.单行数据

单行数据指定列插入:

insert into students (sn, name, qq) values (123, '张飞', '4567890');

image-20230726194404757

values左侧为表中属性,右侧为自定义插入的内容,左右两侧安装顺序是一一对应的,如果顺序不同就会导致类型不同而出错。

单行数据全列插入:

insert into students values (10, 124, '关于', '123456');

image-20230726194739161

将values左侧的属性列全部省略,那么所有属性对应的值都必须插入。

注:into可以省略。

2.多行数据

若想一次插入一批数据,则只需把上面的;替换成,然后继续(数据),最后再分号结束。

多行数据指定列插入:

image-20230726195633647

多行数据全列插入:

image-20230726195423697

3.插入是否更新

由于 主键 或者 唯一键 对应的值已经存在而导致插入失败。

若插入的数据发生主键或者唯一键冲突,就会插入失败,但是我们仍然希望将主键和唯一键之外的的值进行特定的修改,这就用到了 on duplicate key ,即不存在则插入,存在则修改:

insert into students values (13, 128, 'xuyou', 11111) on duplicate key update sn=132, name='xuyou', qq='11111';

image-20230726210059371

其中:

0 row affected: 表中有冲突数据,但冲突数据的值和 update 的值相等
1 row affected: 表中没有冲突数据,数据被插入
2 row affected: 表中有冲突数据,并且数据已经被更新

此外,通过 MySQL 函数获取受到影响的数据行数

image-20230726210943499

4.替换

还有一种方式能够替代上面的on duplicate key,用replace替换insert同样可以使数据:不存在则插入,存在则修改。

-- 主键 或者 唯一键 没有冲突,则直接插入;
-- 主键 或者 唯一键 如果冲突,则删除后再插入
REPLACE INTO students (sn, name) VALUES (20001, '曹阿瞒');
Query OK, 2 rows affected (0.00 sec)
-- 1 row affected: 表中没有冲突数据,数据被插入
-- 2 row affected: 表中有冲突数据,删除后重新插入

image-20230726211631265

二.查询数据Retrieve

主要是select。(使用频率最高的语句)

表中结构以及插入信息如下。

image-20230727154022109

下面用各种方式进行基本查询。

1.全列查询

全列查询需要 * 通配符完成。

-- 通常情况下不建议使用 * 进行全列查询
-- 1. 查询的列越多,意味着需要传输的数据量越大;
-- 2. 可能会影响到索引的使用。
select * from exam_result;

image-20230727154438005

2.指定列查询

指定列查询仍是将表中所有的列拿出来的,只不过我们所筛选的属性并不是全部的数据。

如下语句,都为筛选指定字段的数据:

select id from exam_result;
select id, math from exam_math;
select id, math, name from exam_math;

image-20230727154638284

3.查询字段为表达式

在MySQL数据类型的介绍中,select可以执行相应的各种表达式,函数。而在select的查询中,也可以将这些属性与查询字段拼凑一起。

比如,可以将常数10与特定查询的字段一起显示,这个10也可以换成表达式(1+1):

image-20230727155723715

既然可以查1+1,那么一定也可以查询math+chinese+english:

image-20230727155848239

这样,查询结果中的字段就存在math+chinese+english字段,当然可以用as将这个字段重命名成一个简短的名字total:

image-20230727160041616

甚至as可以省略,这样一来,在查询中,我们可以任意将表字段名在查询结果中重新显示成我们想要的字段名,即表中字段的别名:

image-20230727160304248

查询指定字段中,里面的数据避免不了出现相同的情况,若指向让相同的数据出现一次,则用distinct去重:

select distinct math from exam_result;

image-20230727160559368

需要注意的是null不参与查询。

4.where子句查询

之前的筛选是对表中数据的整体做筛选,即字段筛选,而同一个字段根据数值的不同,通过不同的筛选条件的不同,得到的结果自然也就不同,比如对于分数,想查询60分以上的,那就需要通过where子句的条件来进行筛选。

where筛选影响的是未来查询结果的行数。

where子句中的各种运算符

where条件的比较运算符:

运算符说明
>, >=, <, <=大于,大于等于,小于,小于等于
=等于,NULL 不安全,例如 NULL = NULL 的结果是 NULL
<=>等于,NULL 安全,例如 NULL <=> NULL 的结果是 TRUE(1)
!=, <>不等于
BETWEEN a0 AND a1范围匹配,[a0, a1],如果 a0 <= value <= a1,返回 TRUE(1)
IN (option, …)如果是 option 中的任意一个,返回 TRUE(1)
IS NULL是 NULL
IS NOT NULL不是 NULL
LIKE模糊匹配。% 表示任意多个(包括 0 个)任意字符;_ 表示任意一个字符

image-20230727162538796

通过select + 表达式的方式可以一一验证。

逻辑运算符:

运算符说明
AND多个条件必须都为 TRUE(1),结果才是 TRUE(1)
OR任意一个条件为 TRUE(1), 结果为 TRUE(1)
NOT条件为 TRUE(1),结果为 FALSE(0)

案例演示:按照条件进行筛选

案例1:英语不及格的同学及英语成绩 ( < 60 )

select name, english from exam_result where english<60;

image-20230727163317911

案例2:语文成绩在 [80, 90] 分的同学及语文成绩

方式一:and连接条件

select name, chinese from exam_result where chinese>=80 and chinese<=90;

image-20230727163557843

方式二:BETWEEN a0 AND a1

select name, chinese from exam_result where chinese between 80 and 90;

image-20230727163742981

案例3:数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩

方式一:使用 OR 进行条件连接

select name, math from exam_result where math=58 or math=59 or math=98 or math=99;

image-20230727170458479

方式二:使用 IN 条件

select name, math from exam_result where math in(58,59,98,99);

image-20230727170557093

案例4:姓孙的同学 及 孙某同学

类似于这种很模糊的条件,选择like比较运算符进行筛选。

like ‘孙%’:后面任意多个字符。

like ‘孙_’:后面一个字符。

select name from exam_result where name like '孙%';
select name from exam_result where name like '孙_';

image-20230727172759602

案例5: 语文成绩好于英语成绩的同学

select name, chinese, english from exam_result where chinese>english;

image-20230727172953859

案例6:总分在200分以下的同学

为了方便,这个时候就可以对列进行重命名了,但事实上没有执行成功:

image-20230727173704061

查找语句的执行顺序:

  1. 先执行from找到对应表
  2. 然后where,在表中拿着条件做筛选
  3. 最后select显示

所以,因为在执行where子句时还没有重命名,故也就找不到对应的字段。此外,也不能在筛选条件where中做重命名。故不能缩短语句,只能通过重命名的方式让数据在显示时将字段缩短。

select name, chinese+english+math total from exam_result where chinese+english+math < 200;

image-20230727174220630

案例7:语文成绩 > 80 并且不姓孙的同学

策略:and与not连用

select name, chinese from exam_result where chinese > 80 and name not like '孙%';

image-20230727174615844

案例8:孙某同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80

这是一个综合案例

分成两种情况:

  1. 孙某同学(名字任意字数)
  2. 总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80

这两种情况满足其一即可,因此用or连接这两个大条件,通过括号将后面的整体括起来。

select name, chinese, math, english, chinese+math+english total from exam_result 
where name like '孙%' or 
(chinese+math+english>200 and chinese<math and english > 80);

image-20230727175301722

案例9:NULL的查询

exam_result表没有空置,所以新建一个表:

image-20230727175839504

查找name=null的人:

select * from test where name is null;

image-20230727175946854

查找name不为空的人:

select * from test where name is not null;

image-20230727180231829

可见,空串不为null。

5.结果排序

查询得到的结果,都是按照一行一行排列的,这时候,就可以根据指定的字段将查询到的结果进行排序。

语法:

  • ASC 为升序(从小到大)默认为ASC
  • DESC 为降序(从大到小)
SELECT ... FROM table_name [WHERE ...]
ORDER BY column [ASC|DESC], [...];

image-20230727185943057

对此表进行结果排序

1. 同学及数学成绩,按数学成绩升序显示

select name, math from exam_result order by math asc;

image-20230727190251620

2. NULL视为比任何值都小,升序出现在最上面

image-20230727190800975

降序也就出现在最下面,不进行演示了。

3. 查询同学各门成绩,依次按 数学降序,英语升序,语文升序的方式显示

select name, math, english, chinese from exam_result order by math desc, english asc, chinese asc;

image-20230727192222717

其中,asc可以省略,因为默认是升序。

4. 查询同学及总分,由高到低

select name, chinese+math+english 总成绩 from exam_result order by chinese+math+english desc;

image-20230727192533869

不过,这里却可以使用别名,即如下方式也可以:

select name, chinese+math+english 总成绩 from exam_result order by 总成绩 desc;

image-20230727192717239

为什么这里又能使用别名了?

排序的原则是先要有合适的数据,在排序。因为这种策略可以最大化提高速度。所以排序的动作是在拿出数据之后的,因此我们select起别名之后就能使用别名了。所以能不能使用别名完全取决于子句的执行顺序。

5. 查询姓孙的同学或者姓曹的同学数学成绩,结果按数学成绩由高到低显示

根据上面的分析,执行顺序一定是先筛选满足条件的同学,然后再将结果排序。所以思想上自然的就将这两个需求肢解,先筛,再排。

select name, math from exam_result where name like '孙%' or name like '曹%' order by math desc;

image-20230727193451569

6.筛选分页结果

limit可以限制查询到的数据记录。有三种方式:

方式一:limit n 表示显示前n条数据。

select * from exam_result limit 3;

image-20230727195500537

方式二:也可以在一个指定的范围进行筛选:limit m,n表示显示从第m+1条开始显示,显示n条数据。

image-20230727195812761

方式三:limit n offset m;表示从第m条开始,筛选n条结果。m从0算起。

select * from exam_result limit 3 offset 0;

image-20230727204406320

只有数据准备好了,才能显示,limit本质的功能就是“显示”,因此limit执行顺序更靠后(比排序靠后)

对未知表进行查询时,limit能够避免因为表中数据过大,查询全表数据导致数据库卡死

三.更新数据update

一般update会和where子句配合使用,即筛选特定的数据将其进行修改,一旦不用where子句配合筛选,则整个表中的数据都将会被修改。

语法:

UPDATE table_name SET column = expr [, column = expr ...]
[WHERE ...] [ORDER BY ...] [LIMIT ...]

案例1:将孙悟空同学的数学成绩变更为 80 分

update exam_result set math=80 where name='孙悟空';

image-20230728142542095

案例2:将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分

和上一个相比,这个大原则不变,不过这次同时修改两个数据。

update exam_result set math=60, chinese=70 where name='曹孟德';

image-20230728142840125

案例3:将总成绩倒数前三的 3 位同学的数学成绩加上 30 分

与前两个不同的是,这个需要order by进行配合。也就是说,分成先查找满足条件的order by,通过limit筛选总成绩倒数前三名,再进行update。

不支持math += 30这种语法。

update exam_result set math=math+30 order by chinese+math+english asc limit 3;

image-20230728144503754

案例4:将所有同学的语文成绩更新为原来的 2 倍

这个更新的范围就是全表,没有筛选条件

不支持chinese *= 2这种语法。

update exam_result set chinese = 2*chinese;

image-20230728144818880

注意:更新全表的语句慎用

四.删除数据Delete

删除数据是以行为单位的删除,如果删除时不用where子句筛选特定行数据,那么表中的所有数据都会被删除。

语法:

DELETE FROM table_name [WHERE ...] [ORDER BY ...] [LIMIT ...]

案例1:删除孙悟空同学的考试成绩

delete from exam_result where name='孙悟空';

image-20230728161419473

案例2:删除总分倒数第一名同学的考试成绩

delete from exam_result order by chinese+math+english asc limit 1;

image-20230728162251195

案例3:删除整张表数据

表内容被删,结构不会被删,结构删用的是drop

方式一:delete from表名

delete from for_delete;

image-20230728162733088

需要注意的是,在这里如果有auto_increment字段,删除数据之后,通过语句:show create table for_delete\G;得到的结果中,其中的auto_increment仍为4,计数器值不会随着表中数据的清除而改变。当插入数据时,会随着此值继续自增。

image-20230728163552738

方式二:截断表

语法:

truncate [table] 表名;

table可省略。与方式一的区别:

  1. 只能对整表操作,不能像 DELETE 一样针对部分数据操作。
  2. 实际上 MySQL 不对数据操作,所以比 DELETE 更快,但是TRUNCATE在删除数据的时候,并不经过真正的事物,所以无法回滚。
  3. 会重置 AUTO_INCREMENT 项

image-20230728164353699

扩展内容

比如bin log, redo log, undo log,这些都是sql溯源之前操作所需要的日志,即为事务。平时的各种sql优化好的语句都会将记录保存在bin log(持久化,保存在文件中),redo log中;undo log是用来做事务回滚的,以及事务的隔离性。数据库一旦出现宕机,redo log可以将数据恢复过来。

持久化方式:

  1. 记录历史SQL语句
  2. 记录数据本身。bin log可以做到主从同步(两个数据库)

所以,truncate不把自己的操作包装成事务,它的操作也不会记录在这些日志里,只是单纯的清空,所以truncate操作一定更快。

五.去重表数据

案例:删除表中的的重复复记录,重复的数据只能有一份

distinct只是在select显示时的去重,不会影响原表中的数据。而现在要的是将表中数据的去重。

去重表数据分三步:

  • 首先,创建另一个表,此表与需要去重的表的结构完全一致。

  • 然后通过select distinct * from 表名,在显示时将重复的去重,再与insert组合,将显示的去重的数据插入到新表中。

  • 最后,将原表重命名进行备份或者直接删掉,新表命名为原表的名字,此新表中的数据就是原表去重后的数据。

实例演示:

原表数据如下:

image-20230728180628878

步骤1.创建新表,命名为no_duplicate_table,结构和duplicate_table相同。

CREATE TABLE no_duplicate_table LIKE duplicate_table;

image-20230728180854917

步骤2.select distinct与insert组合,将duplicate_table表中数据筛选去重并插入到no_duplicate_table中。

insert into no_duplicate_table select distinct *  from duplicate_table;

image-20230728181306496

步骤3.重命名原表与新表,原表备份,新表改名为原表同名

image-20230728181714874

这就完成了去重表数据。

为什么最后是通过rename方式进行的?

通过rename方式,就是单纯的想等一切都就绪了,然后一起放入、更新、生效的。

六.聚合函数

1.聚合函数汇总

函数说明
COUNT([DISTINCT] expr)返回查询到的数据的 数量
SUM([DISTINCT] expr)返回查询到的数据的总和,不是数字没有意义
AVG([DISTINCT] expr)返回查询到的数据的平均值,不是数字没有意义
MAX([DISTINCT] expr)返回查询到的数据的最大值,不是数字没有意义
MIN([DISTINCT] expr)返回查询到的数据的最小值,不是数字没有意义

2.案例演示

image-20230728185441636

案例1:统计班级共有多少同学

select count(*) from exam_result;

image-20230728185433465

可以将count函数起别名:

select count(*) 总数 from exam_result;

image-20230728185536350

count的括号中,为数字或者'字符'也可以,数字可以任意,甚至可以为小数。

image-20230728185649194

案例2:统计数学成绩有多少个

select count(math) from exam_result;

image-20230728190048709

当然,都是可以起别名的,这就不演示了。

若统计去重后的数据,采用如下:

select count(distinct math) from exam_result;

image-20230728190207253

由于聚合与去重要有先后之分,一旦先聚合再去重,先聚合的结果是5,5就一个数字无法去重,所以这里需要我们需要注意distinct要先执行,即写在括号里。

案例3:统计数学成绩的总分

select sum(math) from exam_result;

image-20230728190501645

案例4:统计数学成绩的平均分

select avg(math) from exam_result;

image-20230728190548416

案例5:统计英语成绩不及格的人数

select count(*) from exam_result where english<60;

image-20230728190732391

案例6:统计英语成绩不及格的同学的英语总分

select sum(english) from exam_result where english<60;

image-20230728190937197

案例7:统计英语成绩不及格的同学的平均分(两种方式)

select avg(english) from exam_result where english<60;
select sum(english)/count(*) from exam_result where english<60;

image-20230728191152497

案例8:统计英语成绩最高分

select max(english) from exam_result;

image-20230728191504415

需要注意的是,如下这样添加name字段,即name,max(english)是错误的:

image-20230728191657959

因为name没法做聚合,必须先分组再聚合,这就涉及到了group by。暂时若仍想这样做,用where子句筛选最高的成绩就可以了。

案例9:返回 > 70 分以上的数学最低分

select min(math) from exam_result where math>70;

image-20230728192003345

七.分组聚合统计group by子句

1.分组查询说明

分组的目的:进行分组之后的聚合统计

分组查询的SQL如下:

SELECT column1 [, column2], ... FROM table_name [WHERE ...] GROUP BY column [, ...] [order by ...] [LIMIT ...];

说明一下:

  • SQL中大写的表示关键字,[ ]中代表的是可选项。
  • 查询SQL中各语句的执行顺序为:where、group by、select、order by、limit。
  • group by后面的列名,表示按照指定列进行分组查询。

虽然是指定列名进行分组,实际上分组是用该列的不同的行数据来进行分组的。

2.分组查询测试表—雇员信息表

雇员信息表结构内容

雇员信息表中包含三张表,分别是员工表(emp)、部门表(dept)和工资等级表(salgrade)。

员工表(emp)中包含如下字段:

  • 雇员编号(empno)。
  • 雇员姓名(ename)。
  • 雇员职位(job)。
  • 雇员领导编号(mgr)。
  • 雇佣时间(hiredate)。
  • 工资月薪(sal)。
  • 奖金(comm)。
  • 部门编号(deptno)。

部门表(dept)中包含如下字段:

  • 部门编号(deptno)。
  • 部门名称(dname)。
  • 部门所在地点(loc)。

工资等级表(salgrade)中包含如下字段:

  • 等级(grade)。
  • 此等级最低工资(losal)。
  • 此等级最高工资(hisal)。

雇员信息表SQL

雇员信息表的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中创建了一个名为scott的数据库,在该数据库中分别创建了部门表(dept)、员工表(emp)和工资等级表(salgrade),并分别向三张表中插入了一些数据用于查询。

将上述SQL保存到文件中,然后在MySQL中使用source命令依次执行文件中的SQL。如下:

image-20230728195119296

执行完文件中的SQL后查看数据库,就能看到多了一个名为scott的数据库。如下:

image-20230728195443994

进入该数据库,在该数据库中就可以看到雇员信息表中的三张表。如下:

image-20230728195522322

其中部门表(dept)的表结构和表中的内容如下:

image-20230728195610867

员工表(emp)的表结构和表中的内容如下:

image-20230728195656660

工资等级表(salgrade)的表结构和表中的内容如下:

image-20230728195757139

显示每个部门的平均工资和最高工资

在group by子句中指明按照部门号进行分组,在select语句中使用avg函数和max函数,分别查询每个部门的平均工资和最高工资。如下:

select deptno, max(sal) 最高, avg(sal) 平均 from emp group by deptno;

image-20230728201815130

说明一下: 上述SQL会先将表中的数据按照部门号进行分组,然后各自在组内做聚合查询得到每个组的平均工资和最高工资。

显示每个部门的每种岗位的平均工资和最低工资

与上面不同的是,这个分组会分两次,即先在全表中,按照部门的不同进行分组,分组后的每个小组再根据岗位的不同进行分组。

select deptno, job, avg(sal) 平均工资, min(sal) 最低工资 from emp group by deptno, job;

image-20230728203640674

说明一下:

  • group by子句中可以指明按照多个字段进行分组,各个字段之间使用逗号隔开,分组优先级与书写顺序相同。
  • 比如上述SQL中,当两条记录的部门号相同时,将会继续按照岗位进行分组。

3.HAVING 条件

含有having子句的SQL如下:

SELECT ... FROM table_name [WHERE ...] [GROUP BY ...] [HAVING ...] [order by ...] [LIMIT ...];

说明一下:

  • SQL中大写的表示关键字,[ ]中代表的是可选项。
  • SQL中各语句的执行顺序为:where、group by、select、having、order by、limit。
  • having子句中可以指明一个或多个筛选条件。

having子句和where子句的区别

  • where子句放在表名后面,而having子句必须搭配group by子句使用,放在group by子句的后面。
  • where子句是对整表的数据进行筛选,having子句是对分组后的数据进行筛选。
  • where子句中不能使用聚合函数和别名,而having子句中可以使用聚合函数和别名。

SQL中各语句的执行顺序

  1. 根据where子句筛选出符合条件的记录。
  2. 根据group by子句对数据进行分组。
  3. 将分组后的数据依次执行select语句。
  4. 根据having子句对分组后的数据进行进一步的筛选。
  5. 根据order by子句对数据进行排序。
  6. 根据limit子句筛选若干条记录进行显示。

having案例

案例1:显示平均工资低于2000的部门和它的平均工资

  • 先统计每个部门的平均工资。
  • 然后通过having子句筛选出平均工资低于2000的部门。

统计每个部门的平均工资

在group by子句中指明按照部门号进行分组,在select语句中使用avg函数查询每个部门的平均工资。如下:

image-20230728210407623

显示平均工资低于2000的部门和它的平均工资

在上述SQL的基础上,在having子句中指明筛选条件为平均工资小于2000。如下:

select deptno, avg(sal) 平均工资 from emp group by deptno having 平均工资<2000;

image-20230728210629655

八.CURD总结

CURD无疑是在数据库中操作最多的,都是对表进行各种的增删查改,尤其对于查找,表与表之间因外键的联系等。但无疑,我们在上面的select 等语句,每一个过程都是对表进行操作,即筛选后的数据在逻辑上仍然是一张表,数据库本身也是一种表结构,所以,数据库下一切皆表

以下是一个简单的使用 MyBatis 框架进行增删改查的示例: 1. 配置文件 在 MyBatis 配置文件中,需要配置数据库连接信息、SQL 映射文件(Mapper)的位置等,例如: ``` <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="123456" /> </dataSource> </environment> </environments> <mappers> <mapper resource="com/example/mappers/UserMapper.xml" /> </mappers> </configuration> ``` 2. 实体类 定义一个实体类,例如: ``` public class User { private Integer id; private String name; private Integer age; // 省略 getter 和 setter 方法 } ``` 3. SQL 映射文件 在 SQL 映射文件中定义 SQL 语句和对应的参数映射,例如: ``` <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.mappers.UserMapper"> <resultMap id="userMap" type="com.example.entities.User"> <id column="id" property="id" /> <result column="name" property="name" /> <result column="age" property="age" /> </resultMap> <select id="getUserById" resultMap="userMap"> SELECT * FROM users WHERE id = #{id} </select> <insert id="addUser" parameterType="com.example.entities.User"> INSERT INTO users (name, age) VALUES (#{name}, #{age}) </insert> <update id="updateUser" parameterType="com.example.entities.User"> UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id} </update> <delete id="deleteUserById"> DELETE FROM users WHERE id = #{id} </delete> </mapper> ``` 4. Dao 接口 定义一个 Dao 接口,其中的方法名要与 SQL 映射文件中的 id 名称一致,例如: ``` public interface UserDao { User getUserById(Integer id); void addUser(User user); void updateUser(User user); void deleteUserById(Integer id); } ``` 5. Dao 实现类 实现 Dao 接口,使用 MyBatis 提供的 SqlSession 和 Mapper 接口来实现增删改查,例如: ``` public class UserDaoImpl implements UserDao { private SqlSessionFactory sqlSessionFactory; public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @Override public User getUserById(Integer id) { try (SqlSession session = sqlSessionFactory.openSession()) { return session.selectOne("com.example.mappers.UserMapper.getUserById", id); } } @Override public void addUser(User user) { try (SqlSession session = sqlSessionFactory.openSession()) { session.insert("com.example.mappers.UserMapper.addUser", user); session.commit(); } } @Override public void updateUser(User user) { try (SqlSession session = sqlSessionFactory.openSession()) { session.update("com.example.mappers.UserMapper.updateUser", user); session.commit(); } } @Override public void deleteUserById(Integer id) { try (SqlSession session = sqlSessionFactory.openSession()) { session.delete("com.example.mappers.UserMapper.deleteUserById", id); session.commit(); } } } ``` 6. 使用 Dao 接口 使用 Dao 接口中定义的方法来操作数据,例如: ``` public static void main(String[] args) { try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); UserDao userDao = new UserDaoImpl(sqlSessionFactory); // 查询用户 User user = userDao.getUserById(1); System.out.println(user.getName() + " " + user.getAge()); // 添加用户 User newUser = new User(); newUser.setName("Tom"); newUser.setAge(20); userDao.addUser(newUser); // 更新用户 User updateUser = new User(); updateUser.setId(1); updateUser.setName("Jerry"); updateUser.setAge(25); userDao.updateUser(updateUser); // 删除用户 userDao.deleteUserById(2); } catch (IOException e) { e.printStackTrace(); } } ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天都要进步呀~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值