vlambda博客
学习文章列表

SQL学习笔记(八) ----Mysql数据库的条件查询

Mysql 数据库的条件查询

本次主要学习:

  • 聚合函数
  • 分组查询
  • 内连接查询
  • 左连接查询
  • 右连接查询
  • 自连接查询
  • 子查询
  • 数据库设计之三范式
  • E-R模型
  • 外键SQL语句的编写

1.聚合函数的介绍

聚合函数又叫组函数,通常是对表中的数据进行统计和计算,一般结合分组(group by)来使用,用于统计和计算分组数据。

常用的聚合函数;

  1. count(col):表示求指定列的总行数
  2. max(col):表示求指定列的最大值
  3. min(col):表示求指定列的最小值
  4. sum(col):表示求指定列的和
  5. avg(col):表示求指定列的平均值
  • 例1 查询学生的个数
select count(id) from students;

输出结果为:

+-----------+
| count(id) |
+-----------+
|         9 |
+-----------+
1 row in set (0.00 sec)

注意:

  1. 聚合函数不对空值进行统计
  2. 一般如果要是指定列名,那么就是主键字段

通用的写法

select count(*) from students;
  • 例2 查询女生的编号最大值
select max(id) from students where gender = '女';

输出结果为:

+---------+
| max(id) |
+---------+
| 5 |
+---------+
  • 例3 查询未删除的学生最小编号
select min(id) from students where is_del = 0;

输出结果为:

+---------+
| min(id) |
+---------+
| 1 |
+---------+
1 row in set (0.00 sec)
  • 例4 查询男生的总身高
select sum(height) from students where gender = '男';

输出结果为:

+-------------+
| sum(height) |
+-------------+
| 7.10 |
+-------------+
1 row in set (0.00 sec)
  • 例5 求男生的平均身高
select avg(height) from students where gender = '男';

输出结果为"

+-------------+
| avg(height) |
+-------------+
| 1.775000 |
+-------------+
1 row in set (0.00 sec)

在实际的情况中会出现空值,如果要将空值看做0来处理,命令如下:

select avg(height) from students where gender = '男';
select avg(ifnull(height,0)) from students where gender = '男';
select sum(height) / count(*) from students where gender = '男';

输出结果为:

+-----------------------+
| avg(ifnull(height,0)) |
+-----------------------+
| 1.014286 |
+-----------------------+
1 row in set (0.04 sec)

注意点:

  1. 聚合函数不会对空值进行统计
  2. ifnull 函数判断指定的是否是空值,如果是空值,使用默认值0.

2.分组查询

学习目标

  • 能够写出分组查询的SQL语句
  1. 分组查询介绍

分组查询就是将查询结果按照指定字段进行分组,字段中数据相等的分为一组。

分组查询基本的语法格式如下:

GROUP BY 列名 [HAVING 条件表达式] [WITH ROLLUP]

说明:

  • 列名:是指按照指定字段的值进行分组
  • HAVING 条件表达式: 用来过滤分组后的数据
  • WITH ROLLUP:在所有记录的最后加上一条记录,显示select查询时聚合函数的统计和计算结果
  1. group by 的使用

group by 可用于单个字段分组,也可以用于多个字段分组

  • 例1 查询性别的种类
#写法一
select distinct gender from students;
#写法二
select gender from students group by gender;

输出结果为:

+--------+
| gender |
+--------+
| 男 |
| 女 |
+--------+
2 rows in set (0.00 sec)
  • 例2 按照多个条件进行分组检验
select gender,name from students group by gender, name;

输出结果为:

+--------+--------+
| gender | name |
+--------+--------+
| 男 | 张三 |
| 男 | 曹操 |
| 男 | 李二狗 |
| 男 | 杨过 |
| 男 | 王铁蛋 |
| 男 | 郭靖 |
| 男 | 黄老邪 |
| 女 | 欧阳锋 |
| 女 | 黄蓉 |
+--------+--------+
9 rows in set (0.00 sec)
  1. group by + group_concat()的使用

group_concat (字段名):统计每个分组指定字段的信息集合,每个信息之间使用逗号进行分割 按照上述的写法,我们需要统计男性有谁,女性有谁,可以使用该种统计方法

select gender, group_concat(name) from students group by gender;

group_concat:统计每个分组指定字段的信息集合,信息之间使用逗号进行分割 输出结果为:

+--------+------------------------------------------+
| gender | group_concat(name) |
+--------+------------------------------------------+
| 男 | 张三,曹操,郭靖,黄老邪,杨过,王铁蛋,李二狗 |
| 女 | 欧阳锋,黄蓉 |
+--------+------------------------------------------+
2 rows in set (0.07 sec)
  • 例2 统计不同性别的平均年龄
select gender, avg(age) from students group by gender;

输出结果为:

+--------+----------+
| gender | avg(age) |
+--------+----------+
| 男 | 34.2857 |
| 女 | 23.0000 |
+--------+----------+
2 rows in set (0.00 sec)
  • 例3 统计不同性别的人的个数
select gender count(*) from students group by gender;

输出结果为:

+--------+----------+
| gender | count(*) |
+--------+----------+
| 男 | 7 |
| 女 | 2 |
+--------+----------+
2 rows in set (0.00 sec)
  • 例4 根据 gender 字段进行分组。统计分组条数大于2的
select gender, count(*) from students group by gender having count(*) > 2;

对分组数据进行过滤,使用的是having输出结果为:

+--------+----------+
| gender | count(*) |
+--------+----------+
| 男 | 7 |
+--------+----------+
1 row in set (0.00 sec)
  • 例5 根据gender字段进行分组,汇总所有人的年龄
select gender, sum(age) from students group by gender;

输出结果为:

+--------+----------+
| gender | sum(age) |
+--------+----------+
| 男 | 240 |
| 女 | 46 |
+--------+----------+
2 rows in set (0.00 sec)
  • 例6 根据gender字段进行分组,汇总总人数
select gender, count(*) from students group by gender with rollup;

输出结果为:

+--------+----------+
| gender | count(*) |
+--------+----------+
| 男 | 7 |
| 女 | 2 |
| NULL | 9 |
+--------+----------+
3 rows in set (0.00 sec)
  • 例7 根据gender字段进行分组,汇总所有人的年龄
+--------+----------------------------+
| gender | group_concat(age) |
+--------+----------------------------+
| 男 | 18,65,30,50,20,45,12 |
| 女 | 18,28 |
| NULL | 18,65,30,50,20,45,12,18,28 |
+--------+----------------------------+
3 rows in set (0.00 sec)

3.连接查询--内连接

  1. 连接查询的介绍

连接查询可以实现多个表的查询,当查询的字段数据来自不同的表就可以使用连接查询来完成。连接查询可以分为:

  1. 内连接查询

  2. 左连接查询

  3. 右连接查询

  4. 自连接查询

  5. 内连接查询

查询两个表中符合条件的共有记录

select 字段 from 表1 inner join 表2 on 表1.字段1 = 表2.字段2

说明:

  • inner join就是内连接查询关键字
  • on 就是连接查询条件:表1中的字段等于表二的字段

例1:使用内连接查询学生表与班级表

操作步骤:

  • 步骤1:创建一个 classes表,并且添加 py40 py41两个字段
create table classes(id int unsigned not null primary key auto_increment, name varchar(20) not null);
desc classes;

输出结果为:

+-------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
  • 步骤2 添加 py40 py41两个班级
insert into classes(name) values('py40'),('py41');
select * from classes;

输出结果为:

+----+------+
| id | name |
+----+------+
| 1 | py40 |
| 2 | py41 |
+----+------+
2 rows in set (0.00 sec)
  • 步骤3 修改students表
alter table students add c_id int unsigned;
update students set c_id = 2 where id in (1,3,5,7)
update students set c_id = 1 where id in (2,4)
select * from students;

输出结果为:

+----+--------+------+--------+--------+--------+------+
| id | name | age | gender | is_del | height | c_id |
+----+--------+------+--------+--------+--------+------+
| 1 | 张三 | 18 | 男 | 1 | 1.65 | 2 |
| 2 | 曹操 | 65 | 男 | 0 | NULL | 1 |
| 3 | 欧阳锋 | 18 | 女 | 0 | 1.85 | 2 |
| 4 | 郭靖 | 30 | 男 | 0 | NULL | 1 |
| 5 | 黄蓉 | 28 | 女 | 0 | 1.85 | NULL |
| 6 | 黄老邪 | 50 | 男 | 0 | NULL | 2 |
| 7 | 杨过 | 20 | 男 | 0 | 1.85 | 2 |
| 9 | 王铁蛋 | 45 | 男 | 0 | 1.75 | NULL |
| 10 | 李二狗 | 12 | 男 | 0 | 1.85 | NULL |
+----+--------+------+--------+--------+--------+------+
9 rows in set (0.00 sec)
  • 步骤4 两张表进行连接
select * from students inner join classes on students.c_id = classes.id;

我们就能够将相关的同学分为不同的班级中,查看结果如下:

+----+--------+------+--------+--------+--------+------+----+------+
| id | name | age | gender | is_del | height | c_id | id | name |
+----+--------+------+--------+--------+--------+------+----+------+
| 1 | 张三 | 18 | 男 | 1 | 1.65 | 2 | 2 | py41 |
| 2 | 曹操 | 65 | 男 | 0 | NULL | 1 | 1 | py40 |
| 3 | 欧阳锋 | 18 | 女 | 0 | 1.85 | 2 | 2 | py41 |
| 4 | 郭靖 | 30 | 男 | 0 | NULL | 1 | 1 | py40 |
| 6 | 黄老邪 | 50 | 男 | 0 | NULL | 2 | 2 | py41 |
| 7 | 杨过 | 20 | 男 | 0 | 1.85 | 2 | 2 | py41 |
+----+--------+------+--------+--------+--------+------+----+------+
6 rows in set (0.02 sec)
  • 步骤5 使用简化的方法将其连接在一起
select s.name, c.name from students s inner join classes c on s.c_id = c.id;

输出结果为:

+--------+------+
| name | name |
+--------+------+
| 张三 | py41 |
| 曹操 | py40 |
| 欧阳锋 | py41 |
| 郭靖 | py40 |
| 黄老邪 | py41 |
| 杨过 | py41 |
+--------+------+
6 rows in set (0.04 sec)

students表简写成s, classes表简写成c,然后其他的字段进行描述,同时添加。

总结:

  • 内连接使用 inner join...on...,on 表示两个表的连接查询条件
  • 内连接根据连接查询条件去除两个表的"交集"

4.左连接

以左表为主根据条件查询右表数据,如果根据条件查询右表数据不存在使用null值填充

左连接查询语法格式:

select 字段 from 表1 left join 表2 on 表1.字段1 = 表2.字段2

我们分别观察左表和右表的相关数据:

然后将左边合并右表:

select * from students s left join classes c on s.c_id = c.id;

输出结果为:

+----+--------+------+--------+--------+--------+------+------+------+
| id | name | age | gender | is_del | height | c_id | id | name |
+----+--------+------+--------+--------+--------+------+------+------+
| 2 | 曹操 | 65 | 男 | 0 | NULL | 1 | 1 | py40 |
| 4 | 郭靖 | 30 | 男 | 0 | NULL | 1 | 1 | py40 |
| 1 | 张三 | 18 | 男 | 1 | 1.65 | 2 | 2 | py41 |
| 3 | 欧阳锋 | 18 | 女 | 0 | 1.85 | 2 | 2 | py41 |
| 6 | 黄老邪 | 50 | 男 | 0 | NULL | 2 | 2 | py41 |
| 7 | 杨过 | 20 | 男 | 0 | 1.85 | 2 | 2 | py41 |
| 5 | 黄蓉 | 28 | 女 | 0 | 1.85 | NULL | NULL | NULL |
| 9 | 王铁蛋 | 45 | 男 | 0 | 1.75 | NULL | NULL | NULL |
| 10 | 李二狗 | 12 | 男 | 0 | 1.85 | NULL | NULL | NULL |
+----+--------+------+--------+--------+--------+------+------+------+
9 rows in set (0.00 sec)

左连接查询,根据左表查询右表数据,如果右表数据不存在,使用null填充。left 左边是左表,left右边是右表。

5.右连接查询

以右表为主根据条件查询左表数据,如果根据条件查询左表数据不存在使用null值填充

语法格式:select 字段 from 表1 right join 表2 on 表1.字段 = 表2.字段说明:

  • right on 就是右连接查询关键字
  • on就是连接查询条件
  • 表1是左表
  • 表2是右表

例1 :使用右连接查询学生表与班级表:mysqlselect * from students s right join classes c on s.c_id = c.id;以右表classes为主,满足classes表中所有的信息,如果没有匹配到的,以空值填充, 具体结果如下:

+------+--------+------+--------+--------+--------+------+----+----------+
| id | name | age | gender | is_del | height | c_id | id | name |
+------+--------+------+--------+--------+--------+------+----+----------+
| 1 | 张三 | 18 | 男 | 1 | 1.65 | 2 | 2 | py41 |
| 2 | 曹操 | 65 | 男 | 0 | NULL | 1 | 1 | py40 |
| 3 | 欧阳锋 | 18 | 女 | 0 | 1.85 | 2 | 2 | py41 |
| 4 | 郭靖 | 30 | 男 | 0 | NULL | 1 | 1 | py40 |
| 6 | 黄老邪 | 50 | 男 | 0 | NULL | 2 | 2 | py41 |
| 7 | 杨过 | 20 | 男 | 0 | 1.85 | 2 | 2 | py41 |
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | 3 | python42 |
+------+--------+------+--------+--------+--------+------+----+----------+
7 rows in set (0.00 sec)

6.自连接查询

左表和右表是同一个表,根据连接查询条件查询两个表中的数据

7.子查询

1.子查询的介绍

在一个select语句中,嵌入了另外一个select语句,那么被嵌入的select语句称为子查询语句,外部那个select语句则称为主查询

主查询和子查询的关系

  1. 子查询嵌入到主查询中
  2. 子查询是辅助主查询的,要么充当条件,要么充当数据源
  3. 子查询是可以独立存在的语句,是一条完整的select语句

2.子查询的使用

例1. 查询大于平均年龄的学生;

select * from students where age > (select avg(age) from students)

输出结果为:

+----+--------+------+--------+--------+--------+------+
| id | name | age | gender | is_del | height | c_id |
+----+--------+------+--------+--------+--------+------+
| 2 | 曹操 | 65 | 男 | 0 | NULL | 1 |
| 6 | 黄老邪 | 50 | 男 | 0 | NULL | 2 |
| 9 | 王铁蛋 | 45 | 男 | 0 | 1.75 | NULL |
+----+--------+------+--------+--------+--------+------+
3 rows in set (0.05 sec)

其中select avg(age) from students 表示从表中提取平均值

例2. 查找学生在班的所有班级名字

select * from classes where id in (select c_id from students where c_id is not null);

例3. 查询年龄最大,身高最高的学生

#写法1
select * from students where age = (select max(age) from students) and height = (select max(height) from students);

#写法2
select * from students where (age, height) = (select max(age), max(height) from students);

输出结果为:

+----+------+------+--------+--------+--------+------+
| id | name | age | gender | is_del | height | c_id |
+----+------+------+--------+--------+--------+------+
| 2 | 曹操 | 65 | 男 | 0 | 2.00 | 1 |
+----+------+------+--------+--------+--------+------+
1 row in set (0.00 sec)

总结:

子查询就是一个完整的SQL语句,子查询被嵌套到一对小括号里面,子查的执行顺序,先执行子查询然后主查询根据子查询的结果再执行

8.数据库设计之三范式

能够知道一个表结构的设计是否符合三范式的要求 范式:设计数据库的一些规范,目前需要遵循有3个规范

  • 范式1:强调列的原子性,列不能够再分成其他几列。

  • 范式2:满足范式1,另外包含两部分内容,一个是必须有一个主键;二是非主键字段必须完全依赖主键,而不能只依赖于主键的一部分。

  • 范式3:满足范式2,另外非主键列必须直接依赖主键,不能存在传递依赖。不能存在:非主键A依赖于非主键B,非主键B依赖于主键的情况。

9.E-R模型

E-R模型即实体-关系模型,E-R模型就是描述数据库存储数据的结构模型。

10.外键SQL语句的编写

  1. 外键约束作用 外键约束:对外键字段的值进行更新和插入时会和引用表中字段的数据进行验证,数据如果不合法则更新和插入会失败,保证数据的有效性。

  2. 对于已经存在的字段添加外键约束 举一个具体的例子:分别创建学校表和老师表并且添加信息

#创建学校表
create table school(
id int unsigned not null primary key auto_increment,
name varchar(30) not null)
);

#创建教师表
create table teacher(id int unsigned not null,
s_id int unsigned,
foreign key(s_id) references school(id)
);

#插入信息
insert into school(name) values('传智');
insert into teacher(name,s_id) values('李四',1);

# 查询信息
select * from school;
select * from teacher;

输出结果为:

+----+------+
| id | name |
+----+------+
| 1 | 传智 |
+----+------+
1 row in set (0.00 sec)

+----+------+------+
| id | name | s_id |
+----+------+------+
| 1 | 李四 | 1 |
+----+------+------+
1 row in set (0.00 sec)
  1. 删除外键约束 需要先获取外键约束名称,该名称系统会自动生成,可以通过查看表创建语句获取名称
show create table teacher;

输出结果为:

+---------+-----------------------------------------------------------------------
----------------------------------+
| Table | Create Table
|
+---------+-----------------------------------------------------------------------
----------------------------------+
| teacher | CREATE TABLE `teacher` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`s_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `s_id` (`s_id`),
CONSTRAINT `teacher_ibfk_1` FOREIGN KEY (`s_id`) REFERENCES `school` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
+---------+-----------------------------------------------------------------------
----------------------------------+
1 row in set (0.00 sec)

获取名称之后就可以根据名称来删除外键约束

alter table teacher drop foreign key 外键名(CONSTRAINT);

alter table teacher drop foreign key teacher_ibfk_1;
show create table teacher;

输出结果为:

+---------+----------------------------------------------
| Table | Create Table
+---------+----------------------------------------------
| teacher | CREATE TABLE `teacher` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`s_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `s_id` (`s_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
+---------+----------------------------------------------
  1. 小结:
  • 添加外键约束:alter table 从表add foreign key(外键字段) references主表(主键字段);
  • 删除外键约束: alter table 表名 drop foreign key 外键名;