MySQL数据库原理与实践(四):基本查询

本篇目标

  1. 掌握 create,select 语句的基本使用方法。
  2. 掌握查询指定列、查询所有列以及为查询结果设置别名的方法。
  3. 掌握使用 distinct 对查询结果进行去重。
  4. 掌握在查询中使用算术表达式,对字段数据进行计算。
  5. 掌握使用 where 子句对数据进行条件筛选。
  6. 掌握比较运算符、逻辑运算符、范围查询和模糊查询的使用。
  7. 掌握使用 order by 对查询结果进行升序或降序排列。
  8. 掌握使用 limit 限制查询结果的数量,实现数据分页。
  9. 能够根据实际需求组合使用各种查询条件,完成基本的数据检索操作。

一.表的增删改查

1.创建操作

1.1.create-创建表

基本语法

cpp 复制代码
create table [if not exists] 表名
(
    字段名 数据类型 字段约束,
    字段名 数据类型 字段约束
);

例如:

cpp 复制代码
CREATE TABLE students (
    id int unsigned primary key auto_increment,
    sn int not null unique comment '学号',
    name varchar(20) not null,
    qq varchar(20)
);

1.2.insert-插入

<1>.单行数据 + 全列插入

插入两条记录,value_list 数量必须和定义表的列的数量及顺序一致

注意:这里在插入的时候,也可以不用指定id (当然,指定的时候就需要明确插入数据到那些列),那么mysql会使用默认的值进行自增。

操作:

cpp 复制代码
insert into students values (100, 10000, '唐三藏', null);

insert into students values (101, 10001, '哥伦比娅', '11111');

mysql> insert into students values (102, 10002, '桑多涅', '11111');

使用select来查看插入结果,如图:

<2>.多行数据 + 指定列插入

插入两条记录,value_list 数量必须和指定列数量及顺序一致。

cpp 复制代码
insert into students (id, sn, name) values (103, 20001, '曹孟德'),(104, 20002, '孙仲谋');

使用select来查看插入结果,如图:

<3>.插入后检查是否更新

由于 主键 /唯一键 对应的值已经存在而导致插入失败, 此时我们可以选择性的进行同步更新操作语法。

例如:

cpp 复制代码
insert into students (id, sn, name) values (100, 10010, '唐大师');

insert into students (sn, name) values (20001, '曹阿瞒');

如图:

一个是主键冲突 ,另一个是唯一键冲突。

此时需要用到更新这个操作**,语法:**

cpp 复制代码
insert into 表名 [(字段列表)] values (值列表) on duplicate key update 
字段名1 = 新值1,字段名2 = 新值2,...;

例如:

cpp 复制代码
insert into students (id,sn,name) values (100,10010,'唐大师')
on duplicate key update sn=10010,name='唐大师';

如图:

这时候就会有人好奇:这个2 rows affected是什么意思?

0 row affected:表中有冲突数据,但冲突数据的值和 update 的值相等。

1 row affected:表中没有冲突数据,数据被插入。

2 row affected:表中有冲突数据,并且数据已经被更新。

通过 MySQL 函数获取受到影响的数据行数:

cpp 复制代码
select count();

如图:

1.3.replace-替换

语法:

cpp 复制代码
replace [into] 表名 [(字段名列表)] values (值列表) [, (值列表)] ...;

示例:

cpp 复制代码
replace into students (id, sn, name) values (100, 10010, '唐大师');

如图:

执行逻辑

  • 如果不存在主键或唯一键冲突,则直接插入数据。
  • 如果存在主键或唯一键冲突,则先删除原来的记录,再插入新的记录。

解释一下这个:

1 row affected:没有发生冲突,直接插入一条新记录。

2 rows affected:与一条旧记录发生冲突,先删除一条,再插入一条。

3 rows affected:新数据同时与两条不同的旧记录发生唯一键冲突,删除两条,再插入一条。

计算方式:

cpp 复制代码
受影响行数 = 删除的旧记录数量 + 插入的新记录数量

例如这个是之前的students,如图:

可是如果我们故意插入一个id为102的数据呢?

cpp 复制代码
 replace into students (id, sn, name) values (102, 10010, '唐大师');

插入后的students,如图:

可以看出之前id为102的数据是已经被删除了的,并且在原来的位置上插入新的数据。

2.查询操作

创建表结构,操作:

cpp 复制代码
create table exam_result (
    id int unsigned primary key auto_increment,
    name varchar(20) not null comment '同学姓名',
    chinese float default 0.0 comment '语文成绩',
    math float default 0.0 comment '数学成绩',
    english float default 0.0 comment '英语成绩'
);

插入测试数据,操作

cpp 复制代码
insert into exam_result (name, chinese, math, english) values
    ('唐三藏', 67, 98, 56),
    ('孙悟空', 87, 78, 77),
    ('猪悟能', 88, 98, 90),
    ('曹孟德', 82, 84, 67),
    ('刘玄德', 55, 85, 45),
    ('孙权', 70, 73, 78),
    ('宋公明', 75, 65, 30);

2.1.select-查询

<1>.全列查询

通常情况下不建议使用 * 进行全列查询, 原因**:**

  1. 查询的列越多 ,意味着需要传输的数据量越大

  2. 可能会影响到索引的使用

操作:

cpp 复制代码
select * from exam_result;

如图:

<2>.指定列查询

我们在查询的时候,指定列的顺序不需要按定义表的顺序来

操作:

cpp 复制代码
select id, name, english FROM exam_result;

如图:

<3>.查询字段为表达式

表达式不包含字段时

表达式包含多个字段

<4>.为查询结果指定别名

语法:

cpp 复制代码
SELECT column [AS] alias_name(别名) [...] FROM table_name;

操作:

cpp 复制代码
select id, name, chinese + math + english 总分 from exam_result;

如图:

<5>.结果去重

distinct用于去除查询结果中完全重复的记录

基本语法:

cpp 复制代码
select distinct 字段列表 from 表名;

操作:

cpp 复制代码
select distinct math from exam_result;

2.2.where条件

2.2.1.运算符

<1>.比较运算符

|-------------------|---------------------------------------------------|
| 运算符 | 说明 |
| >, >=, <, <= | 大于,大于等于,小于,小于等于 |
| = | 等于,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 个)任意字符;_ 表示任意一个字符 |

<2>.逻辑运算符:

|-----|-------------------------------|
| 运算符 | 说明 |
| and | 多个条件必须都为 TRUE(1),结果才是 TRUE(1) |
| or | 任意一个条件为 TRUE(1), 结果为 TRUE(1) |
| not | 条件为 TRUE(1),结果为 FALSE(0) |

2.2.2.案例演示

<1>.英语不及格的同学及英语成绩 ( < 60 ),操作

cpp 复制代码
SELECT name, english FROM exam_result WHERE english < 60; 

如图:

<2>.语文成绩在 80, 90 分的同学及语文成绩,此时要用and做连接,操作:

cpp 复制代码
select name,chinese from exam_result where chinese>=80 and chinese<=90;

如图:

当然也可以用**between...and...**了,操作:

cpp 复制代码
select name,chinese from exam_result where chinese between 80 and 90;

<3>.数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩

可以使用使用or进行条件连接,操作:

cpp 复制代码
select name,math from exam_result where math=58 or math=59 or math=98 or math=99;

如图:

也可以使用使用 in条件,操作:

cpp 复制代码
select name, math from exam_result where math in (58, 59, 98, 99);

如图:

<4>.姓孙的同学及孙某同学

% 匹配任意多个(包括 0 个)任意字符,操作:

cpp 复制代码
 select name from exam_result where name like '孙%';

如图:

_ 匹配严格的一个任意字符,可以添加多个_,操作:

cpp 复制代码
select name from exam_result where name like '孙_';

select name from exam_result where name like '孙__';

select name from exam_result where name like '孙___';

如图:

<5>.语文成绩好于英语成绩的同学

where 条件中比较运算符两侧都是字段,操作:

cpp 复制代码
select name, chinese, english from exam_result where chinese > english;

如图:

<6>.总分在 200 分以下的同学

可以在WHERE 条件中使用表达式,操作:

cpp 复制代码
select name, chinese + math + english 总分 from exam_result where 总分 <200;

报错原因是:总分通过 select设置的别名,而 where的执行早于 select。执行 where时,总分这个别名还没有产生,所以MySQL认为它是一个不存在的字段。

修改后的:

cpp 复制代码
select name, chinese + math + english 总分 from exam_result where chinese+math+english <200;

<7>.语文成绩 > 80 并且不姓孙的同学

and 与 not 的使用,操作:

cpp 复制代码
select name, chinese from exam_result where chinese > 80 and name not like '孙%'; 

如图:

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

操作:

cpp 复制代码
select name, chinese, math, english, chinese + math + english 总分 from exam_result where name like '孙_' or ( chinese + math + english > 200 and chinese < math and english > 80 ); 

2.2.3.NULL 的查询

查询 students 表,操作如图:

查询 qq 号已知的同学姓名,操作:

cpp 复制代码
select name,qq from students where qq is not null;

如图:

<1>.NULL 和 NULL 的比较,= 和<=> 的区别

在MySQL中,null表示"未知值",不是普通的数据。因此,不能使用普通的 =判断两个 null是否相等。

1. 使用=

cpp 复制代码
select null = null;

结果:

因为两个未知值无法判断是否相等,所以结果既不是 true(1),也不是 false(0),而是 null

下面的结果也都是 null

cpp 复制代码
select null = 10;
select null != 10;
select null <> 10;

2.使用<=>

<=>是MySQL提供的 null安全等于运算符。

cpp 复制代码
select null <=> null;

select null <=> 10;

结果:

2.3.结果排序

MySQL使用 order by对查询结果进行排序

基本语法:

cpp 复制代码
select 字段列表 from 表名 [where 筛选条件] order by 排序字段 [asc | desc];

注意:asc 为升序(从小到大),desc 为降序(从大到小) ,默认为 asc。

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

操作:

cpp 复制代码
select math from exam_result order by math;

select math from exam_result order by math desc;

如图:

<2>.同学及 qq 号,按 qq 号排序显示

操作:

cpp 复制代码
select name, qq from students order by qq;

如图:

注意:NULL 视为比任何值都小,升序出现在最上面

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

操作:

cpp 复制代码
select name, math, english, chinese from exam_result order by math desc, english, chinese;

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

order by 中可以使用表达式:

操作:

cpp 复制代码
select name, chinese + english + math from exam_result order by chinese + english + math desc; 

order by 子句中可以使用列别名:

操作:

cpp 复制代码
select name, chinese + english + math 总分 from exam_result order by 总分 desc;

如图:

可以使用别名的原因:

执行顺序

cpp 复制代码
from exam_result
        ↓
select name, chinese + english + math 总分
        ↓
order by 总分 desc
        ↓
返回排序后的结果

也就是因为执行 order by时,select已经产生了 总分别名,所以这里可以直接使用 总分

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

操作:

cpp 复制代码
select name,math from exam_result where name like '孙%' or name like '曹%' order by math desc;

如图:

2.4.筛选

语法:

cpp 复制代码
起始下标为 0 -- 从 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; 

操作:

cpp 复制代码
select * from exam_result limit 3;
cpp 复制代码
select * from exam_result limit 3,6;

select * from exam_result limit 3,8;
cpp 复制代码
select * from exam_result limit 3 offset 5;

3.更新操作

3.1.update

update用于修改表中已经存在的数据。

基本语法:

cpp 复制代码
update 表名 set 字段名1 = 新值1,字段名2 = 新值2,... [where 筛选条件] [order by 排序字段] [limit 更新数量];

3.2.实例

<1>.将孙悟空同学的数学成绩变更为 90 分

查看原数据 ,操作:

cpp 复制代码
select name, math from exam_result where name = '孙悟空';

进行更新操作:

cpp 复制代码
update exam_result set math=90 where name='孙悟空';

<2>.将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分

cpp 复制代码
 update exam_result set math=60,chinese=70 where name='曹孟德';

select name, math,chinese from exam_result where name = '曹孟德';

如图:

<3>.将总成绩倒数前三的 3 位同学的数学成绩加上 30 分

先查询表数据,操作:

cpp 复制代码
select name, math, chinese + math + english 总分 from exam_result order by 总分 limit 3; 
cpp 复制代码
update exam_result set math=math+30 order by math+chinese+english limit 3;

注意:mysql不支持 math += 30 这种语法

<4>.将所有同学的语文成绩更新为原来的 2 倍

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

查看原数据,如图:

操作:

cpp 复制代码
update exam_result set chinese=chinese*2;

4.删除操作

注意:删除表数据的操作要慎用

4.1.delete

基本语法

cpp 复制代码
delete from 表名 [where 筛选条件] [order by 排序字段] [limit 删除数量];

<1>.例如删除孙悟空同学的考试成绩:

cpp 复制代码
select name from exam_result where name='孙悟空';
cpp 复制代码
delete from exam_result where name = '孙悟空';

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

<2>.删除整张表数据

准备测试表:

cpp 复制代码
create table for_delete(id int primary key auto_increment, name varchar(20));

插入测试数据:

cpp 复制代码
insert into for_delete (name) values ('A'), ('B'), ('C');

查看测试数据:

删除整表数据:

cpp 复制代码
delete from for_delete;

查看删除结果时,可以看出表中的数据都被删除了,但是表没有被删除

再插入一条数据,自增 id 在原值上增长,

cpp 复制代码
insert into for_delete (name) values ('D');

查看表结构,会有auto_increment=n 项:

4.2.截断表

语法:

cpp 复制代码
truncate [table] table_name;

准备测试表+插入测试数据+查看测试数据,操作:

cpp 复制代码
create table for_truncate(id int primary key auto_increment,name varchar(20));

insert into for_truncate (name) values ('A'),('B'),('C');

select * from for_truncate;

截断整表数据,操作:

cpp 复制代码
truncate for_truncate;

查看删除结果:

cpp 复制代码
select * from for_truncate;

再插入一条数据,

cpp 复制代码
insert into for_truncate (name) values ('D');

查表,

cpp 复制代码
select * from for_truncate;

查看表结构,

cpp 复制代码
show create table for_truncate\G;

特点

  1. 只能清空整张表 ,**不能通过 where**删除部分数据。
  2. 保留表结构,只清空数据,不会删除表的字段、约束和索引等结构。
  3. 执行速度较快truncate通常不会像 delete一样逐行删除数据,因此清空大表时速度更快。
  4. 重置自增长值 ,执行后,auto_increment计数器通常会重新开始

5.聚合函数

5.1.函数

|-------------|------------------------|
| 函数 | 说明 |
| count(expr) | 返回查询到的数据的 数量 |
| sum(expr) | 返回查询到的数据的 总和,不是数字没有意义 |
| avg(expr) | 返回查询到的数据的 平均值,不是数字没有意义 |
| max(expr) | 返回查询到的数据的 最大值,不是数字没有意义 |
| min(expr) | 返回查询到的数据的 最小值,不是数字没有意义 |

5.2.案例

<1>.统计班级共有多少同学

我们可以**使用 * 做统计,不受 NULL 影响,**操作:

cpp 复制代码
select count(*) from exam_result;

<2>.统计班级收集的 qq 号有多少

注意:null 不会计入结果

cpp 复制代码
select count(qq) from students;

<3>.统计本次考试的数学成绩分数个数

使用count统计全部成绩,操作:

cpp 复制代码
select count(math) from exam_result;

count(distinct math) 统计的是去重成绩数量,操作:

cpp 复制代码
select count(distinct math) from exam_result;

<4>.统计数学成绩总分

操作:

cpp 复制代码
select sum(math) from exam_result;

加上一个分数 < 60 的条件,操作:

cpp 复制代码
select sum(math) from exam_result where math<60;

<5>.统计平均总分

cpp 复制代码
select avg(math+chinese+english) from exam_result;

<6>.返回英语最高分

cpp 复制代码
select max(english) from exam_result;

<7>.返回 > 70 分以上的数学最低分

cpp 复制代码
select min(math) from exam_result where math>70;

5.3.group by

语法:

cpp 复制代码
select column1, column2, .. from table group by column;

案例:

先创建表

cpp 复制代码
-- 创建dept部门表
create table dep (
    deptno int primary key,
    dname varchar(20),
    loc varchar(20)
);

-- 创建salgrade工资等级表
create table salgrade(
    grade int primary key,
    losal decimal(7, 2),
    hisal decimal(7, 2)
);

-- 创建emp员工表
create table emp(
    empno int primary key,
    ename varchar(20),
    job varchar(20),
    mgr int,
    hiredate date,
    sal decimal(7, 2),
    comm decimal(7, 2),
    deptno int,
    constraint fk_emp_dept
    foreign key (deptno) references dept(deptno)
);

然后再插入数据

cpp 复制代码
//向dept部门表插入数据
insert into dept (deptno, dname, loc) values
(10, 'accounting', 'new york'),
(20, 'research', 'dallas'),
(30, 'sales', 'chicago'),
(40, 'operations', 'boston');

//向salgrade工资等级表插入数据
insert into salgrade (grade, losal, hisal) values
(1, 700, 1200),
(2, 1201, 1400),
(3, 1401, 2000),
(4, 2001, 3000),
(5, 3001, 9999);

//向emp员工表插入数据
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) values
(7369, 'smith', 'clerk', 7902, '1980-12-17', 800, null, 20),
(7499, 'allen', 'salesman', 7698, '1981-02-20', 1600, 300, 30),
(7521, 'ward', 'salesman', 7698, '1981-02-22', 1250, 500, 30),
(7566, 'jones', 'manager', 7839, '1981-04-02', 2975, null, 20),
(7654, 'martin', 'salesman', 7698, '1981-09-28', 1250, 1400, 30),
(7698, 'blake', 'manager', 7839, '1981-05-01', 2850, null, 30),
(7782, 'clark', 'manager', 7839, '1981-06-09', 2450, null, 10),
(7788, 'scott', 'analyst', 7566, '1987-04-19', 3000, null, 20),
(7839, 'king', 'president', null, '1981-11-17', 5000, null, 10),
(7844, 'turner', 'salesman', 7698, '1981-09-08', 1500, 0, 30),
(7876, 'adams', 'clerk', 7788, '1987-05-23', 1100, null, 20),
(7900, 'james', 'clerk', 7698, '1981-12-03', 950, null, 30),
(7902, 'ford', 'analyst', 7566, '1981-12-03', 3000, null, 20),
(7934, 'miller', 'clerk', 7782, '1982-01-23', 1300, null, 10);

<1>.显示每个部门的平均工资和最高工资

cpp 复制代码
select deptno,avg(sal) as 平均工资,max(sal) as 最高工资 from emp group by deptno;

执行结果

这里按照 deptno分组,然后分别计算每个部门的平均工资和最高工资。

<2>. 显示每个部门中每种岗位的平均工资和最低工资,

cpp 复制代码
select deptno,job,avg(sal) as 平均工资,min(sal) as 最低工资 from emp group by deptno, job;

这里按照两个字段进行分组:

cpp 复制代码
group by deptno, job

只有部门编号和岗位都相同的员工,才会被划分到同一组

执行结果:

<3>. 统计各个部门的平均工资

cpp 复制代码
select deptno,avg(sal) as 平均工资 from emp group by deptno;

此时只进行分组统计,还没有对分组结果进行筛选。

<4>. 显示平均工资低于2000的部门及其平均工资

需要使用 havinggroup by产生的分组结果进行过滤

cpp 复制代码
select deptno,avg(sal) as 平均工资 from emp group by deptno having 平均工资 < 2000;

也可以不使用别名:

cpp 复制代码
select deptno,avg(sal) as 平均工资 from emp group by deptno having avg(sal) < 2000;

执行结果:

那如果我们使用where呢?操作:

cpp 复制代码
select deptno,avg(sal) as 平均工资 from emp group by deptno where avg(sal) < 2000;

报错原因:

where必须写在 group by之前 ,SQL一般书写顺序是:

cpp 复制代码
select → from → where → group by → having → order by

那如果这样写呢?

cpp 复制代码
select deptno,avg(sal) as 平均工资 from emp where avg(sal)<2000 group by deptno;

报错原因:where不能筛选聚合结果,avg(sal)是分组后才计算出来的平均工资,而 where在分组前执行,所以 where不能使用 avg()等聚合函数

having的执行顺序:

cpp 复制代码
from emp
    ↓
group by deptno
    ↓
计算avg(sal),产生平均工资
    ↓
having 平均工资 < 2000
    ↓
select返回结果

where的执行顺序:

cpp 复制代码
from emp
    ↓
where sal < 2000
筛选出工资低于2000的员工
    ↓
group by deptno
按照部门编号进行分组
    ↓
计算每个部门的avg(sal)
    ↓
select deptno, avg(sal) as 平均工资
    ↓
返回查询结果

总结:

本篇介绍了 MySQL 中数据的增删改查操作,并学习了 whereorder bylimit等常用查询子句。通过聚合函数、group byhaving,可以进一步完成数据统计、分组以及分组结果筛选。掌握这些基础语法和执行顺序后,就能够根据实际需求组合出较为完整的数据查询语句。

相关推荐
秋田君1 小时前
Qt_常用控件使用学习
数据库·qt·学习
A15362551 小时前
五金批发电商业财一体化 ERP 推荐:打通订单、库存、财务对账
大数据·运维·人工智能·零售
爱喝水的鱼丶1 小时前
SAP-ABAP:SELECT大数据量查询性能调优——避免嵌套循环、减少数据库交互的核心方案
开发语言·数据库·sql·性能优化·sap·abap·erp
持力行2 小时前
AI编程CRUD类项目实操
大数据·人工智能·ai编程
@Demi2 小时前
前端开发 Git 分支与 Tag 管理规范
大数据·git·elasticsearch
于壮士hoho2 小时前
IT|如何搭建企业级指标体系
大数据·指标体系
三8442 小时前
get方法/post方法/SQL注入文字型/数字型
数据库·sql·mysql
事变天下2 小时前
冲刺港三所,如何选则香港申请机构?香港留学机构对比及避坑指南
大数据·人工智能
ACP广源盛139246256733 小时前
YLB3118 存储桥接芯片完整机会点@ACP#联动曙光 8000
大数据·人工智能·分布式·单片机·嵌入式硬件