多表查询分类
需求:在设计数据表时,那么将现实中的一个实体设计为一个表,但在查询数据时,很多时候会将具有关系的不同实体表中的数据一同取出来。
语法:
select * from 表A,表B 【where 子句】
说明:
从多个表中获取where子句的匹配条件进行获取数据
如果省略where子句,那么得到的结果是一个笛卡尔积
笛卡尔积:
得到的结果集的记录数是:两个表的记录数的乘积
字段数:两个表的字段数的和
union联合查询
注意:
对同一个表的不同的部分进行不同的操作
一般用于对比较大的表进行分表存储,联合查询
1 | mysql> (select * from stu where sex=1 order by age limit 999)union(select * from stu where sex=0 order by age desc limit 999); |
内连接【inner join】
语法一:select 列名 from 表1 inner join 表2 on 表1.公共字段=表2.公共字段
inner是可以省略的
1 | mysql> select * from stuinfo inner join stuMarks on stuinfo.stuNo=stuMarks.stuNo; |
语法二:select 列名 from 表1,表2 where 表1.公共字段=表2.公共字段
1 | mysql> select * from stuinfo,stuMarks where stuinfo.stuNo=stuMarks.stuNo; |
select from 表1 inner join 表2 on 表1.公共字段=表2.公共字段和select from 表2 inner join 表1 on 表1.公共字段=表2.公共字段是否一样?
答:一样的,因为内连接显示的是两个表的公共记录。
三个表的内连接如何实现?
答:select 列名 from 表1 inner join 表2 on 表1.公共字段=表2.公共字段 inner join 表3 on 表2.公共字段=表3.公共字段
左外连接【left join】
以左边的表为标准,如果右边的表没有对应的记录,用NULL填充。
语法:select 列名 from 表1 left join 表2 on 表1.公共字段=表2.公共字段
1 | mysql> select * from stuinfo left join stuMarks on stuinfo.stuNo=stuMarks.stuNo; |
select from 表1 left join 表2 on 表1.公共字段=表2.公共字段和select from 表2 left join 表1 on 表1.公共字段=表2.公共字段是否一样?
答:不一样,第一个SQL语句以表1为准,第二个SQL语句以表2为准
右外连接【right join】
以右边的表为标准,如果右边的表没有对应的记录,用NULL填充。
语法:select 列名 from 表1 right join 表2 on 表1.公共字段=表2.公共字段
1 | mysql> select * from stuinfo right join stuMarks on stuinfo.stuNo=stuMarks.stuNo; |
思考:select from 表1 left join 表2 on 表1.公共字段=表2.公共字段和select from 表2 right join 表1 on 表1.公共字段=表2.公共字段是否一样?
答:一样。
交叉连接【cross join】
1、如果没有连接表达式返回的是笛卡尔积mysql> select * from stuinfo cross join stuMarks;
等价于mysql> select * from stuinfo,stuMarks;
2、如果有连接表达式等价于内连接
1 | mysql> select * from stuinfo cross join stuMarks on stuinfo.stuNo=stuMarks.stuNo; |
自然连接【natural】
自动的判断连接条件,它是通过同名字段来判断的
自然连接又分为:
1、 自然内连接 natural joinselect * from stuinfo natural join stuMarks;
2、 自然左外连接 natural left joinselect * from stuinfo natural left join stuMarks;
3、 自然右外连接 natural right joinselect * from stuinfo natural rigth join stuMarks;
自然连接结论:
1、 表连接通过同名的字段来连接的
2、 如果没有同名的字段返回笛卡尔积
3、 会对结果进行整理,整理的规则如下
a) 连接字段保留一个
b) 连接字段放在最前面
using()
1、用来指定连接字段。
2、using()也会对连接字段进行整理,整理方式和自然连接是一样的。select * from stuinfo inner join stuMarks using(id);
子查询
1、 语法:select 语句 where 条件 (select … from 表)
2、 外面的查询称为父查询,括号中的查询称为子查询
3、 子查询为父查询提供查询条件
=子查询
使用表连接的方法
1 | mysql> select stuName,writtenExam from stuinfo natural join stuMarks having writtenExam>60; |
使用子查询方法
1 | mysql> select * from stuinfo where stuNo in(select stuNo from stuMarks where writtenExam>60); |
找出最高分
1 | mysql> select * from stuinfo where stuNo=(select stuNo from stuMarks order by writtenExam desc limit 1); |
in|not in子查询
用于子查询的返回结果多个值
some()、any()、all()
some:一些
any:一些 和some()是一样的 类似于in
all:全部
=some(=any) 等于其中任何一个
!=some(!=any)不等于其中任何一个就可以了
=all 等于其中的所有
!=all 不等于其中的所有
思考:!=some()和not in一样吗?
答:不一样;!=some()表示父查询的记录只要不等于some中的任意一条即可。
!=all()才和not in一样
1 | mysql> select * from stuinfo where stuNo=some(select stuNo from stuMarks where writtenExam>60); |
exists和not exists
如果有人大于等于80分就显示所有的学生
1 | mysql> select * from stuinfo where exists (select * from stuMarks where writtenExam>=80); |
如果没有人超过80分就显示所有的学生
1 | mysql> select * from stuinfo where not exists (select * from stuMarks where writtenExam>=80); |
子查询分类
1、 标量子查询:子查询返回的结果就一个
2、 列子查询:子查询返回的结果是一个列表
3、 行子查询:子查询返回的结果返回一行
4、 表子查询:子查询返回的结果当成一个表(一定要取别名)
1 | mysql> select *,count(*) from (select * from stuinfo order by stuAge desc) as new1 group by stuSex; |
子查询一定要取别名
视图【view】
1、 视图是一张虚拟表,它表示一张表的部分或多张表的综合的结构。
2、 视图仅仅是表结构,没有表数据。视图的结构和数据建立在表的基础上。
创建视图
语法
create [or replace] view 视图的名称
as
select语句
因为视图是一个表结构,所以创建视图后,会在数据库文件夹中多一个与视图名同名的.frm文件
1 | mysql> create view vw_stu |
查询视图
视图是一张虚拟表,视图的用法和表的用法一样
1 | mysql> select * from vw_stu; |
查看视图的结构
1 | mysql> desc vw_stu; |
查看创建视图的语法
1 | mysql> show create view vw_stu \g |
显示所有视图
1 | mysql> show tables; |
精确查找视图
方法一:information_schema中的views表保存的是视图信息
1 | mysql> select table_name from information_schema.views; |
方法二:show table status查看表的属性
1 | mysql> show table status\G |
显示所有视图的语法是
1 | mysql> show table status where comment='view'\G |
更改视图
语法:
alter view 视图名
as
select 语句
1 | mysql> alter view vw_stu |
删除视图
语法:drop view [if exists] 视图1,视图2,…
1 | mysql> drop view if exists vw_stu; |
视图的作用
1、 筛选数据,防止未经许可访问敏感数据
2、 增加表的兼容性
3、 隐藏表结构
4、 降低SQL语句的复杂度
视图的算法
找出语文成绩最高的男生和女生
既然子查询当成一个表,我们可以用视图来代替
将子查询写法改为如下
结果不正确!原因:这是因为视图的算法造成的
视图的算法
1、 merge:合并算法,将视图的语句和外层的语句合并后在执行。
2、 temptable:临时表算法,将视图生成一个临时表,再执行外层语句
3、 undefined:未定义,MySQL到底用merge还是用temptable由MySQL决
定,这是一个默认的算法,一般视图都会选择merge算法,因为merge效率高。
解决
在创建视图的时候指定视图的算法
create view algorithm=temptable 视图名
as
select 语句
重写视图
重新执行查询
事务安全【transaction】
1、 事务是一个不可分割的执行单元
2、 事务作为一个整体要么一起执行,要么一起回滚
开启事务1
语法:start transaction或begin [work]
执行sql更新语句2
update 表名 set 字段名=字段内容 条件
设置还原点3
savepoint sp1
执行sql更新语句4
成功提交事务5
语法:commit
失败回滚事务5
语法:rollback
1 | mysql> start transaction; |
思考:事务什么时候产生?什么时候结束?
答:开启的时候产生,提交事务或回滚事务都结束
脚下留心:只有innodb和BDB才支持事务,myisam不支持事务。
事务的特性(ACID)
1、 原子性(Atomicity):事务是一个整体,不可以再分,要么一起执行,要么一起不执行。
2、 一致性(Consistency):事务完成时,数据必须处于一致的状态。
3、 隔离性(Isolation):每个事务都是相互隔离的
4、 永久性(Durability):事务完成后,对数据的修改是永久性的。
自动提交事务
默认情况下,每个独立的SQL语句都在自动提交事务的。
通过如下语句查看是否自动提交事务
可以更改自动提交事务
set autocommit=0 | 1;
MySQL的锁的机制
当在对数据表进行并发(同一时刻会有多个客户端操作同一条记录)操作时,就会触发MySQL的锁的机制
①、表锁当某个客户端对一个表进行更改操作时会锁住整个表(其他客户端只能等待)非键值
②、行锁–键值
索引【index】
索引的优点:查询速度快
索引的缺点:
1、 增、删、改(数据操作语句)效率低了
2、 索引占用空间
索引的类型
1、 普通索引
2、 唯一索引(唯一键)
3、 主键索引:只要主键就自动创建主键索引,不需要手动创建。
4、 全文索引,搜索引擎使用,MySQL不支持中文的全文索引,我们通过sphinx去解决中文的全文索引。
创建普通索引【create index】
语法:create index 索引名 on 表名 (字段名)
创建索引
查看索引
通过修改表的方式创建索引
语法:alter table 表名 add index [索引的名称] (列名)
创建索引
创建表的时候指定索引
1 | mysql> create table temp( |
创建唯一索引
语法一:create unique index 索引名 on 表名 (字段名)
语法二:alter table 表名 add unqiue index [索引的名称] (列名)
语法三:创建表的时候添加唯一索引,和创建唯一键是一样的。
创建多列组合索引
删除索引
语法:drop index 索引名 on 表名
创建索引的指导原则
1、 该列用于频繁搜索
2、 改列用于排序
3、 公共字段要创建索引
4、 如果表中的数据很少,不需要创建索引。MySQL搜索索引的时间比逐条搜索数据的时间要长。
5、 如果一个字段上的数据只有几个不同的值,改字段不适合做索引,比如性别。
存储过程【procedure】
存储过程的优点
1、 存储过程可以减少网络流量
2、 允许模块化设计
3、 支持事务
创建存储过程
语法:
create procedure 存储过程名(参数)
begin
//sql语句
end;
脚下留心:由于过程中有很多SQL语句,每个语句的结束都要用(;)表示。默认情况下,分号既表示语句结束,又表示向服务器发送SQL语句。我们希望分号仅表示语句的结束,不要将SQL语句发送到服务器执行,通过delimiter来更改结束符。
mysql>delimiter //
创建简单的存储过程
调用存储过程
语法:call 存储过程名()
删除存储过程
语法:drop procedure [if exists] 存储过程名
创建复杂的存储过程
1 | mysql> create procedure proc(param char(6)) |
存储过程的参数
存储过程不能使用return返回值,要返回值只能通过“输出参数”来向外传递值。
存储过程的参数分为:输入参数(in)【默认】,输出参数(out),输入输出参数(inout)
变量
局部变量
1、 通过declare关键字来声明变量
2、 语法:declare 变量名 数据类型 [default 默认值]
3、 使用set或select …into…给变量赋值
使用select…into…给变量赋值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16mysql> create procedure proc(in id char(6))
-> begin
-> declare name varchar(20);
-> declare sex char(1);
-> select stuname,stusex into name,sex from stuinfo where stuno=id;
-> select name,sex from dual;
-> end //
Query OK, 0 rows affected (0.00 sec)
mysql> call proc('s25301')//
+--------+------+
| name | sex |
+--------+------+
| 张秋丽 | 男 |
+--------+------+
1 row in set (0.00 sec)
例题:查找同桌
例题:使用set赋值
全局变量
在变量前面加一个@,就是全局变量
全局变量是一个弱类型的变量,它的类型取决于付给变量的值
系统变量
只要变量名前面有两个@@的肯定是系统变量
带输出参数的存储过程【out】
1 | mysql> create procedure proc(num int,out result int) |
带输入输出参数的存储过程【inout】
查看存储过程的信息
—————内连接—————-
–语法一:select i.stuno,stuname,writtenexam from stuinfo i inner join stumarks s on i.stuno=s.stuno;
–语法二:select stuinfo.stuno,stuname,writtenexam from stuinfo,stumarks where stuinfo.stuno=stumarks.stuno;
–inner 可以省略select i.stuno,stuname,writtenexam from stuinfo i join stumarks s on i.stuno=s.stuno;
————–左外连接,右外连接———-select stuinfo.stuno,stuname,writtenexam,labexam from stuinfo left join stumarks on stuinfo.stuno=stumarks.stuno;
select stuinfo.stuno,stuname,writtenexam,labexam from stuinfo right join stumarks on stuinfo.stuno=stumarks.stuno;
————–自然连接—————
–自然内连接select * from t1 natural join t2;
–自然左外连接select * from t1 natural left join t2;
–自然右外连接select * from t1 natural right join t2;
—————-using()——————-select * from t1 inner join t2 using(id);
select * from t1 left join t2 using(id);
——————-子查询————————–
–查找80分的学生
–方法一select stuname,writtenexam from stuinfo natural join stumarks where writtenexam=80;
–方法二select * from stuinfo where stuno=(select stuno from stumarks where writtenexam=80);
–找出最高分select * from stuinfo where stuno=(select stuno from stumarks where writtenexam=(select max(writtenexam) from stumarks));
–查找及格的学生select * from stuinfo where stuno in (select stuno from stumarks where writtenexam>=60);
–查找不及格的学生select * from stuinfo where stuno not in (select stuno from stumarks where writtenexam>=60);
–查找缺考的学生select * from stuinfo where stuno in (select stuno)
select * from stuinfo where stuno in (select stuno from stuinfo natural left join stumarks where writtenexam is null);
–some
select * from stuinfo where stuno=some(select stuno from stumarks where writtenexam>=60);
–exitsselect * from stuinfo where exists (select * from stumarks where writtenexam>=80)
–行子查询select * from stu where (stusex,ch) in (select stusex,max(ch) from stu group by stusex);
–表子查询select * from (select * from stu order by ch desc) as t group by stusex;
–示地区和每个地区参见考试的人数,并按人数降序排列select stuaddress,count(writtenexam) c from stuinfo left join stumarks using(stuno) group by stuaddress order by c desc;
–显示有学生参见考试的地区select stuaddress,count(writtenexam) c from stuinfo left join stumarks using(stuno) group by stuaddress having c>0;
select distinct stuaddress from stuinfo natural right join stumarks where stuaddress is not null;
–显示男生和女生的人数select stusex,count(*) from stuinfo where stusex='男' union select stusex,count(*) from stuinfo where stusex='女';
select sum(stusex='男') 男,sum(stusex='女') 女 from stuinfo;
–显示每个地区的男生和女生,以及总人数select stuaddress,count(*) 总人数,sum(stusex='男') 男,sum(stusex='女') 女 from stuinfo group by stuaddress;
———————–视图———————–
1 | create view vw_stu |
select * from (select * from stu order by ch desc) t group by stusex;
1 | create or replace algorithm=temptable view vw1 |
1 | -- |
多表查询分类
将多个表的数据横向的联合起来。
1、 内连接
2、 外连接
a) 左外连接
b) 右外连接
3、 交叉连接
4、 自然连接