从单表查询到多表查询
1 sql/db/dbms 他们都是什么,它们之间的关系又是怎么样?
DB: 数据库,以一个文件的形式存储到硬盘里
DBMS: 数据库管理系统 (...)
SQL: 结构化查询语言,是一门标准通用的语言。
sql属于高级语言,sql的编译是看不见的,sql语句是由DBMS完成编译的。
DBMS -》 (执行者)
SQL -》 (操作者)
DB
2 什么是表?
表: table
表:table时数据库的基本组成单元,所有的数据都以表格的形式组织,目的:可读性强。
一个表包含: 行和列:
行: 数据(data)
列: 字段(cloumn)
学号 姓名 年龄
·····················
110 20 20
120 30 30
bigint - 对应java中的long类型
每一个字段应该包括哪一些属性?
字段名,数据类型,相关的约束(比如说为空/不为空的限制条件)。
3.sql语句怎么分类呢?
DQL(数据查询语言) :查询语句 select
DML(数据操作语言): insert delete update ,对表里面的数据进行增删改查
DDL(数据定义语言): create drop alter ,针对的时表结构,不是数据
TCL(事务控制语言): commit (程序员两大难,一线程,二事务)
DCL(数据控制语言): grant授权
4.导入数据(后期大家练习使用)
第一步: mysql登录 mysql -uroot -p444
第二步: mysql查看有哪些数据库
show databases; (不是sql语句,这是mysql的命令)
第三步: 创建自己的数据库,iot
create database iot; (不是sql语句,这是mysql的命令)
第四步: 选择自己想使用的数据库:
use iot;
第五步: 看一看当前我们创建的这个iot数据库里面的表
show tables;
第六步: 导入我们自己的练习数据:
source iot.sql (不加分号)
5 iot.sql 这个文件以sql结尾,这样的被称为“sql脚本文件”
当一个文件的扩展名是.sql,并且该文件中编写了sql语句,这样的文件就是sql脚本。
source : 直接用这个命令,即可导入sql脚本文件
错误: ERROR 1046 (3D000): No database selected
6. drop database iot;
7. 数据导入完成,我们来查看一下表结构:
desc user;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| uid | int(6) | NO | PRI | NULL | auto_increment |
| uName | varchar(100) | YES | | NULL | |
| uSex | varchar(100) | YES | | NULL | |
| uAddress | varchar(255) | YES | | NULL | |
| uPhone | varchar(255) | YES | | NULL | |
| uHeight | double(20,2) | YES | | NULL | |
| cName | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
主键: 关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键
假如,有一个学生表,(学号,性别,班级,年龄),其中每个学生的学号是唯一的,不能重复,学号==主键。
主键是确定一条记录的唯一标识,比如:身份证,学号....等等
8. 常用的命令:
select database(); 查看当前在操作哪一个数据库
\c : 退出命令,命令不执行。
show create table user;
9. 表数据查询
DQL : 简单查询
语法格式: select 字段名1,字段名2,.... from 表名;
提示:
1.任何一条sql语句必须;结尾
2. sql语句不分大小写的
3.关键字不能乱写
4.如果第一次安装好查询出现中文繁体乱码,要一条语句进行修改
show variables like '%char%'; (查看当前的编码方式)
set character_set_results=gb2312; (如果乱码请改这个)
5. 查出来横向显示(尾部+\G即可,\G实际上已经带了分号)
参与数学运算的查询:
select uName,uHeight*100 from user\G
增加字段别名:
select uName as 'name' , uHeight*100 as 'height' from user\G
10. 条件查询:
语法格式: select 字段名1,字段名2,.... from 表名 where 条件;
比如说:我们要查询指定身高的同学数据:
select * from user where uHeight = '178.91';
+-----+--------+------+--------------+-------------+---------+--------------+
| uid | uName | uSex | uAddress | uPhone | uHeight | cName |
+-----+--------+------+--------------+-------------+---------+--------------+
| 3 | 姜沸沸 | 男 | 湖南省长沙市 | 18855474458 | 178.91 | 物联网1902班 |
+-----+--------+------+--------------+-------------+---------+--------------+
1 row in set (0.00 sec)
比如说:我们想知道身高在175以上的同学是哪些?
select * from user where uHeight > '175.00';
比如说:我们想知道身高在160-180的同学是哪些?
select * from user where uHeight > '160.00' and uHeight < '180.00';
and和or 是兄弟 , and就是两个都要满足 ,or 就是只用满足一个
select * from user where uHeight > '160.00' and uHeight < '180.00'; (既要大于160,也要小于180)
select * from user where uHeight > '160.00' or uHeight < '180.00'; (大于160可以,或者小于180也可以)
in 包含 (相当于多个or)
比如说:我们想知道身高是176.40,178.91,174.60这几个之中的的同学是哪些?
select * from user where uHeight not in ('176.40','178.91','174.60');
表达式的优先级别:
查询身高大于170的并且班级是物联网1901或者1902的同学;
select * from user where uHeight > '170.00' and (cName = '1901' or cName = '1902');
navicat: 数据库系统管理软件
https://www.navicat.com.cn/manual/online_manual/cn/navicat/win_manual/index.html#/installation
1. between ... and ... (在数值方面的比较上,是闭区间,15-20,如果它用在字符的比较上,是左闭右开)
select uName,uSex,uAddress,uHeight,cName from user where uHeight > '160.00' and uHeight < '180.00';
select uName,uSex,uAddress,uHeight,cName from user where uHeight BETWEEN '160.00' and '180.00';
2. <> != not in :
例如我要查询班级不为1901的学生数据:
select uName,uSex,uAddress,uHeight,cName from user where cName <> '1901';
select uName,uSex,uAddress,uHeight,cName from user where cName != '1901';
select uName,uSex,uAddress,uHeight,cName from user where cName not in ('1901');
3. null (null它不是一个值,为空,但是不是空值,它不能用等号来衡量,用is null来进行区别,把null跟''空字符串区分开)
找出没有填写性别的同学数据:
select uName,uSex,uAddress,uHeight,cName from user where uSex is null; (只是把null的数据查了出来)
select uName,uSex,uAddress,uHeight,cName from user where uSex is null or uSex =''; (正确)
select uName,uSex,uAddress,uHeight,cName from user where uSex in('','null'); (错误的)
1. 查询班级为1901班的男生数据
select uName,uSex,uAddress,uHeight,cName from user where cName = '1901' and uSex = '男';
2. 查询身高在170-180之间的男生数据
select uName,uSex from user where uHeight between '170.00' and '180.00';
3. 查询性别为空的学生数据(性别未填写的)
select uName,uSex,uHeight from user where uSex in null or uSex = '';
4. 查询班级为1901的或者性别为男的学生数据
select uName,uSex,uHeight,cName from user where cName = '1901' or uSex = '男';
5. 查询身在湖南省永州市或者是湖南省长沙市的学生数据(用两种方法)
select uName,uSex,uAddress,uHeight,cName from user where uAddress = '湖南省永州市' or uAddress = '湖南省长沙市';
in:
select uName,uSex,uAddress,uHeight,cName from user where uAddress in('湖南省永州市','湖南省长沙市');
11. like(模糊查询):
在模糊查询中,掌握两个关键点即可:一个是 % , 一个是 _
%: 任意个 字符 , _ : 一个字符
找出衡阳市的学生数据:
select uName,uSex,uAddress,uHeight,cName from user where uAddress = '湖南省衡阳市'
select uName,uSex,uAddress,uHeight,cName from user where uAddress like '%衡阳%';
找出名字中第二个字是'飞'的同学数据:
select uName,uSex,uAddress,uHeight,cName from user where uName like '_飞%';
找出名字中带'桑'字的同学数据:
select uName,uSex,uAddress,uHeight,cName from user where uName like '%桑%';
找出名字中带有 '_' 的同学数据:
select uName,uSex,uAddress,uHeight,cName from user where uName like '%\_%';
转义: \_
找出名字中最后一个字是'桑'的:
select uName,uSex from user where uName like '%桑';
1). IFNULL : 这个函数用于判断第一个表达式是否为NULL,如果为NULL,则返回第二个参数的值。
函数格式:
IFNULL(expression,value);
select IFNULL(NULL,'hello'); --> hello
select IFNULL('cash','hello'); --> cash
2). CONCAT() : 将多个字符串连接成一个字符串 , 是最重要的函数之一
函数格式:
CONCAT(str1,str2,...)
但是,用这个函数要注意,如果中间有任意一个字符串为NULL,那么返回值直接是NULL
select uName,uSex,uAddress,uHeight,cName from user where CONCAT(IFNULL(uName,''),IFNULL(uSex,''),IFNULL(uAddress,''),IFNULL(uPhone,''),IFNULL(cName,'')) like '%1903%';
12.综合运用:
定义变量:
1. set @变量名 = 值
2. select @变量名 := 值
假设:我想新增一个变量
set @strTest1 = '长沙';
select @strTest1 := '长沙';
select @strTest1;
1). uid在1-6之间并且身在长沙市的同学数据:
select uName,uSex,uAddress,uHeight,cName from user where uid between 1 and 6 and uAddress like '%长沙%';
set @strTest1 = '长沙';
select uName,uSex,uAddress,uHeight,cName from user where uid between 1 and 6 and uAddress like CONCAT('%',@strTest1,'%');
2). 查询1901班或者1902班并且身高在170以上的男同学:
select @strTest1 := '1901'; select @strTest2 := '1902'; select @strTest3 := '170'; select @strTest4 := '男';
select uName,uSex,uAddress,uHeight,cName from user where cName in (@strTest1,@strTest2) and uHeight > @strTest3 and uSex = @strTest4;
3). 查询1903班中姓王的女生:
select uName,uSex,uAddress,uHeight,cName from user where cName = '1903' and uSex = '女' and uName like '王%';
4).查询1903班或者1902班中,姓王并且身高在160-180之间的女生:
select uName,uSex,uAddress,uHeight,cName from user where
uName like '王%' and (cName ='1903' or cName = '1902' ) and uHeight between '160.00' and '180.00' and uSex = '女';
5).模拟搜索有效用户数据(输入性别或者名字或者班级都可以,同学信息中为NULL 的过滤掉,不显示)
select uName,uSex,uAddress,uHeight,cName from user where CONCAT(uSex,uName,cName) like '%女%';
13.数据排序:
升序/降序 : order by
1. 按照身高升序来查询学生数据: (order by 默认升序,在后面指定DESC 降序 ASC升序)
select uName,uSex,uAddress,uHeight,cName from user order by uHeight; # (默认升序-从小到大)
2. 按照身高升序来查询学生数据,当身高相同的时候,再按照班级来升序排列
select uName,uSex,uAddress,uHeight,cName from user order by uHeight ASC , cName ASC;
原理:越靠前的字段起到的主导作用就越大,后面的压根可能会用不上。
3. 找出1901班的同学,并且要求按照身高降序排列:
select uName,uSex,uAddress,uHeight,cName from user where cName = '1901' order by uHeight DESC;
执行原理: order by 是最后执行的,先查,再排序输出。
14. 分组函数: (也叫聚合函数 ,统计函数,组函数,多行处理函数)
count : 计数的
sum : 求和
avg : 平均值
max : 最大值
min : 最小值
特点:1. 所有的分组函数都是对“某一组”数据进行操作的。
2. sum() , avg() 一般用于处理值
3. max() , min() , count() 可以处理任何类型
4. 分组函数都会自动过滤NULL
1. 找出身高的总和:(sum)
select sum(uHeight) 总身高 from user;
2. 找出最高的身高: (max)
select max(uHeight) 总身高 from user;
3. 找出最低身高: (min)
select min(uHeight) 总身高 from user;
4.找出平均身高: (avg)
select avg(uHeight) 平均身高 from user;
5.查询用户表的总人数: count
select count(uName) 平均身高 from user;
select count(*) 平均身高 from user;
select count(uHeight) 平均身高 from user;
2. count(*) 和 count(字段名) 有什么区别?
count(*):不是统计某个字段中的数据,而是统计总记录数。(和某个字段是无关的)
count(字段名): 表示统计字段中不为NULL的数据总数量。
分组函数也能合起来用:
select count(uSex),avg(uHeight) from user;
单行处理函数:
输入一行,输出一行:跟计算有关的查询
3. 找出身高 高于 平均身高 的同学:
select avg(uHeight) from user;
select * from user where uHeight > avg(uHeight);
[Err] 1111 - Invalid use of group function 无效的使用了分组函数
原因:SQL语句的规则: 分组函数不可直接使用在where子句当中,why?
1). 找出身高高于平均身高的同学数据:
找出平均身高:
select avg(uHeight) from user;
找出高于平均身高的:
select * from user where uHeight > 173.65;
select * from user where uHeight > avg(uHeight);
select * from user where uHeight > (select avg(uHeight) from user); # 正确的
----
因为 group by 是在where 之后才会执行:
select 5
...
from 1
...
where 2
...
group by 3
...
having 4
...
order by 6
...
----
4. group by 和 having
group by : 按照某个字段或者某些字段进行分组
having: 对分组之后的数据再次过滤
1). 找出每个班的最高身高
先查有多少班级?
select cName from user group by cName;
这样写事先分组,然后再求每个组的身高的最大值,分组函数是在group by 结束以后再执行
select max(uHeight),cName from user group by cName;
注意: 分组函数一般都会和group by 联合使用,这也是他为什么被称为分组函数的原因
并且任何一个分组函数(count sum max min ) 都是在group by 语句执行结束以后才会执行。
当一条sql语句没有用group by 的时候,整张表的数据会自成一组。
select max(uHeight),cName from user group by cName;
select uName,max(uHeight),cName from user group by cName;
以上在mysql中可以查出来,但是没有意义,在oracle中会报错,直接语法错误。
所以,记住一个规则:当一条语句中有group by 的时候,select后面只能跟分组函数和参与分组的字段。
2). 查询每个班的平均身高
select avg(uHeight),cName from user group by cName;
多个字段能不能联合起来一块分组? 可以用
案例: 找出 每个班 不同性别 的 最高身高:
select
cName,uSex,max(uHeight)
from
user
group by
cName,uSex;
3). having
找出 每个班 的最高身高,要求显示身高大于175的数据:
第一步:找出每个班的最高身高:
select max(uHeight),cName from user group by cName;
第二步:过滤:>175
where: 效率高,能使用它尽量用它,因为where先执行,所以参加分组的数据就会减少
select max(uHeight),cName from user where uHeight > '175' group by cName ;
having: 效率就低了
select max(uHeight),cName from user group by cName having max(uHeight) > '175';
找出每个班的平均身高,要求显示身高大于171的数据
第一步:找平均身高
select avg(uHeight),cName from user group by cName;
第二步:过滤 >171
select avg(uHeight),cName from user where avg(uHeight) > '171 group by cName;
select avg(uHeight),cName from user group by cName having avg(uHeight) > '171'; (正确的)
注意:有的情况没有办法,只能用having进行过滤
简单的单表查询,到此结束,接下来就到了mysql的重点:多表查询
5. distinct : 查询结果集去除重复值。
select cName from user;
案例:
select distinct cName from user;
select uName,uSex, distinct cName from user; (错误的)
记住: distinct 只能出现在所有字段的最前面
select distinct cName,uSex from user;
案例:
统计班级的数量:
select count(distinct cName) from user;
15. 连接查询:
15.1 什么是连接查询?
在实际的开发过程中,大部分的情况下都不是从一张表中查询数据,一般都是多张表联合查询,取出最终的结果。
所有字段都设计在一张表中,会造成数据冗余,浪费硬盘空间
15.2 连接查询的分类:
根据语法出现的年代进行划分:
SQL92(一些老的DBA啊,还在用这类语法)
SQL99(比较新的语法)
根据表的连接方式来划分:
内连接:
等值连接
非等值连接
自连接
外连接:
左外连接
右外连接
全连接: (很少用)
15.3 在表的连接查询方面有一种现象:笛卡尔积现象(笛卡尔乘积现象)
案例: 找出每一个学生所在班级的总人数,要求显示学生姓名和班级总人数。
select uName,cNum from user,class; why? 为什么是24条数据,因为没加条件
笛卡尔积现象: 当两张表在连接查询的时候,没有任何条件限制,最终的查询结果是两张表记录条数的乘积
关于表的别名:
select u.uName,c.cNum from user u,class c;
表的别名有什么好处?
1. 执行效率高。
2. 可读性强。
15.4 怎么来避免笛卡尔乘积现象?
当然是增加条件过滤。
案例:找出每一个学生所在班级的总人数,要求显示学生姓名和班级总人数。
思考:我们多表查询要加条件,其实就是看多表中哪些字段相对应
select u.uName,c.cNum from user u,class c where u.cName = c.cName;
(SQL92 语法,目前不赞同这种做法)
15.5 内连接之等值连接 : 最大的特点就是: 条件是等量关系
案例:找出每一个学生所在班级的总人数,要求显示学生姓名和班级总人数。
select u.uName,c.cNum from user u,class c where u.cName = c.cName;
(SQL92 语法,目前不赞同这种做法)
select b.id ,count(y.*) from bing join yuyue on b.id = y.pid
SQL99 语法:
select u.uName,c.cNum from user u inner join class c on u.cName = c.cName;
SQL99明显更复杂,但是我们为什么要用它?
1. 官方推荐,没有办法你得用它。
2. 因为sql99语法解结构更清晰,表的连接条件和后来的where条件分离了。
语法总结:
....
A
join
B
on
连接条件
where
...
15.6 内连接之非等值连接: 最大的一个特点就是: 连接条件中的关系是非等量关系。
案例: 找出每个同学的身高所属区间号,要求显示学生姓名,身高,身高所属的区间号
第一步:
select uName,uHeight from user;
select hid from height;
第二步:
select u.uName,u.uHeight,h.hid from user u join height h on u.uHeight BETWEEN h.hMin and h.hMax;
15.7 自连接: 最大的特点: 一张表当两张表来看,自己连接自己
案例: 找出每个同学的同桌,要求显示学生姓名和对应的同桌姓名:
第一步:
1. 学生表:select uid,uName,uLeaderId from user;
2. 同桌表:select uid,uName from user;
同学的同桌编号uLeaderId = 这个同桌的uid
select a.uName '学生姓名' ,b.uName '同桌姓名' from user a inner join user b on a.uLeaderId = b.uid;
15.8 外连接:
什么是外连接?跟内连接有什么区别?
内连接: 假设A和B两张表进行连接,使用内连接的话,凡是A表和B表能匹配上的记录查出来,这就是内连接。
AB两张表之间没有主副之分,两张表是平等的。
外连接: 假设AB两张表进行外连接,AB两张表中有一张是主表,一张是副表,主要查询主表中的数据,
当副表中的数据没有和主表中的数据匹配上,副表会自动模拟出NULL与之匹配。
外连接的分类:
左外连接(左连接):表示左边的这张表是主表。
右外连接(右链接):表示右边的这张表是主表。
左连接有右连接的写法,右连接也会有对应左连接的写法。
案例: 找出每个同学的同桌,要求显示学生姓名和对应的同桌姓名(所有同学的数据都要有):
1. 内连接:
select a.uName as '学生姓名' , b.uName as '同桌姓名' from user a inner join user b
on a.uLeaderId = b.uid;
2. 外连接:因为有主次之分,主表激素按匹配不上次表,数据也会查出来
左外连接:outer不写也没关系
select a.uName as '学生姓名' , b.uName as '同桌姓名' from user a left join user b
on a.uLeaderId = b.uid;
右外连接:
select a.uName as '学生姓名' , b.uName as '同桌姓名' from user b right join user a
on a.uLeaderId = b.uid;
外连接:最重要的特点是:主表的数据无条件的全部查询出来。
案例: 找出哪个班级没有学生。
select c.*,u.* from user u right join class c on u.cName = c.cName where u.uid is null;
16. 三张表怎么连接查询?
案例: 找出每一个同学的班级总人数 以及 身高所属区间号
格式:
...
A
join B
on
join C
on ...
--------------
select u.uName , c.cNum , h.hid
from user u
join class c
on u.cName = c.cName
join height h
on u.uHeight BETWEEN h.hMin and h.hMax;
案例:找出每一个同学的班级总人数以及身高所属区间号以及同桌姓名
select u.uName '学生姓名', c.cNum , h.hid , u1.uName '同桌姓名'
from user u
join class c
on u.cName = c.cName
join height h
on u.uHeight BETWEEN h.hMin and h.hMax
left join user u1
on u.uLeaderId = u1.uid;
17. 子查询:
什么是子查询?它可以出现在哪里?
select语句当中嵌套select语句,被嵌套的select语句就是子查询。
语法格式:
select ...(select)...
from ...(select)...
where ...(select)...
17.1 where 子句中的子查询
案例: 找出高于平均身高的学生信息。
select * from user where uHeight > avg(uHeight); // 错误的
第一步:找出平均身高
select avg(uHeight) from user;
第二步:
where 过滤:
select * from user where uHeight > 173.65;
第三步:
select * from user where uHeight > (select avg(uHeight) from user);
17.2 from 后面嵌套子查询:
案例:找出每一个班级平均身高 的 所属区间号:
第一步:
找出每个班的平均身高(按照班级分组,求uHeight的平均值)
select cName , avg(uHeight) as avgHeight from user group by cName;
第二步:
将以上的查询结果当作临时表t,让t表和height表相连接
select t.* , h.hid
from (select cName , avg(uHeight) as avgHeight from user group by cName) t
join height h
on t.avgHeight between h.hMin and h.hMax;
案例: 找出每个班级平均的 身高所属区间号。
第一步:
找出每个同学的身高所属区间号:
select u.uName,u.uHeight,u.cName,h.hid from user u
join height h on u.uHeight BETWEEN h.hMin and h.hMax;
第二步:
基于以上结果,继续按照班级分组,求uHeight的平均值
select
u.cName,avg(h.hid)
from user u
join height h
on u.uHeight BETWEEN h.hMin and h.hMax
group by
u.cName;
17.3 select 后面嵌套子查询:
案例: 找出每个学生所在班级的总人数,要求显示学生姓名和班级总人数:
连接查询:
select u.uName,c.cNum from user u join class c on u.cName=c.cName;
子查询:
select u.uName,(select c.cNum from class c where u.cName=c.cName) from user u;
18. union (可以将查询结果集相加)
案例:找出班级是1901或1902的学生姓名
第一种: or
select uName,cName from user where cName='1901' or cName='1902';
第二种: in
select uName,cName from user where cName in('1901','1902');
第三种: union
select uName from user where cName = '1901'
union
select uName from user where cName = '1902';
select uName from user
union
select cNum from class;
1列对1列,不然报错!
19. limit (重点,以后用于分页查询)
19.1 limit 是mysql特有的,其他数据库中没有,不通用的 (oracle 中有一个相同的机制,叫做rownum)
19.2 limit取结果集中的部分数据,这就是它的作用
19.3 语法规则:
limit startIndex length
startIndex表示起始位置,从0开始,0表示第一条数据,可以不写,不写的时候默认为0
length表示取几个
案例: 取出身高前3名的学生数据(降序排序,再取3个)
第一步:
select uName,uHeight from user order by uHeight desc;
第二步:取前三条数据
select uName,uHeight from user order by uHeight desc limit 0,3;
select uName,uHeight from user order by uHeight desc limit 3;
19.4 limit是sql语句最后执行的一个环节
select ... 5
from ... 1
where ... 2
group by ... 3
having ... 4
order by ... 6
limit ... 7
案例: 取出身高排名在第3到第6的学生数据(降序再取)
select uName,uHeight from user order by uHeight desc limit 2,4;
通用的标准分页sql?
每页显示8条记录:(limit 0,3)
第 1 页: 0,8
第 2 页: 8,8
第 3 页: 16,8
第 4 页: 24,8
......
每页显示 pageSize 条记录:
第 pageNo 页 : ?,pageSize
(pageNo -1)*pageSize , pageSize
java代码:
{
int pageNo = 2; //页码是2
int pageSize = 10; //每页显示10条记录
limit (pageNo -1)*pageSize , pageSize // 10,10
}