select distinct 列* from 表1 innser|left|right join 表2 on 表1与表2的关系 where ... group by ... (5个聚合函数,avg,max,sum,min,count) having ... order by ... (排序desc|asc) limit start,count
1.关系
- 创建成绩表scores,结构如下
- id
- 学生
- 科目
- 成绩
- 思考:学生列应该存什么信息呢?
- 答:学生列的数据不是在这里新建的,而应该从学生表引用过来,关系也是一条数据;根据范式要求应该存储学生的编号,而不是学生的姓名等其它信息
- 同理,科目表也是关系列,引用科目表中的数据
2.外键约束
- 思考:怎么保证关系列数据的有效性呢?任何整数都可以吗?
- 答:必须是学生表中id列存在的数据,可以通过外键约束进行数据的有效性验证
- 为stuid添加外键约束
alter table scores add constraint stu_sco foreign key(stuid) references students(id);
- 在创建表时可以直接创建约束
create table scores( id int primary key auto_increment, stuid int, subid int, score decimal(5,2), foreign key(stuid) references students(id), foreign key(subid) references subjects(id) );
mysql> create table scores( -> id int auto_increment primary key not null, -> score decimal(4,1), -> stuid int, -> subid int, -> foreign key(stuid) references students(id), -> foreign key(subid) references subjects(id));
- 添加数据
mysql> insert into scores values(0,100,1,1); Query OK, 1 row affected (0.01 sec) mysql> select * from scores; +----+-------+-------+-------+ | id | score | stuid | subid | +----+-------+-------+-------+ | 1 | 100.0 | 1 | 1 | +----+-------+-------+-------+
mysql> insert into scores values(0,100,10,10); ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`py31`.`scores`, CONSTRAINT `scores_ibfk_2` FOREIGN KEY (`subid`) REFERENCES `subjects` (`id`))
3.外键的级联操作()逻辑删除
- 在删除students表的数据时,如果这个id值在scores中已经存在,则会抛异常
- 推荐使用逻辑删除,还可以解决这个问题
- 可以创建表时指定级联操作,也可以在创建表后再修改外键的级联操作
级联操作的类型包括:
- restrict(限制):默认值,抛异常
- cascade(级联):如果主表的记录删掉,则从表中相关联的记录都将被删除
- set null:将外键设置为空
- no action:什么都不做
alter table scores add constraint stu_sco foreign key(stuid) references students(id) on delete cascade;
4.连接查询
- 问:查询每个学生每个科目的分数
- 分析:学生姓名来源于students表,科目名称来源于subjects,分数来源于scores表,怎么将3个表放到一起查询,并将结果显示在同一个结果集中呢?
- 答:当查询结果来源于多张表时,需要使用连接查询
- 关键:找到表间的关系,当前的关系是
- students表的id---scores表的stuid
- subjects表的id---scores表的subid
- 则上面问题的答案是:
select students.sname,subjects.stitle,scores.score from scores inner join students on scores.stuid=students.id inner join subjects on scores.subid=subjects.id;
1)查询成绩
mysql> select * from scores; +----+-------+-------+-------+ | id | score | stuid | subid | +----+-------+-------+-------+ | 1 | 100.0 | 1 | 1 | | 3 | 100.0 | 3 | 2 | | 4 | 100.0 | 4 | 5 | | 5 | 94.0 | 3 | 5 | | 6 | 94.0 | 7 | 5 | | 7 | 92.0 | 7 | 5 | | 8 | 92.0 | 8 | 5 | | 9 | 72.0 | 8 | 5 | +----+-------+-------+-------+
2)连接查询
学生表 - 班级表 郭靖 python 100 student.name subjects.title scores.score select students.name,subjects.title,scores.score from scores inner join students on scores.stuid=students.id inner join subjects on scores.subid=subjects.id;
+-----------+---------+-------+ | name | title | score | +-----------+---------+-------+ | 腾旭 | python | 100.0 | | 网易 | linux | 100.0 | | 小米 | mysqlDB | 100.0 | | 网易 | mysqlDB | 94.0 | | QQ | mysqlDB | 94.0 | | QQ | mysqlDB | 92.0 | | 腾讯云 | mysqlDB | 92.0 | | 腾讯云 | mysqlDB | 72.0 | +-----------+---------+-------+
- 表A inner join 表B:表A与表B匹配的行会出现在结果中
select * from students inner join scores on students.id=scores.stuid
- 表A left join 表B:表A与表B匹配的行会出现在结果中,外加表A中独有的数据,未对应的数据使用null填充
select * from students left join scores on students.id=scores.stuid
mysql> mysql> select * from students left join scores on students.id=scores.stuid; +----+-----------+--------+---------------------+----------+------+-------+-------+-------+ | id | name | gender | birthday | isDelete | id | score | stuid | subid | +----+-----------+--------+---------------------+----------+------+-------+-------+-------+ | 1 | 腾旭 | | 1999-09-09 00:00:00 | | 1 | 100.0 | 1 | 1 | | 3 | 网易 | | NULL | | 3 | 100.0 | 3 | 2 | | 4 | 小米 | | NULL | | 4 | 100.0 | 4 | 5 | | 3 | 网易 | | NULL | | 5 | 94.0 | 3 | 5 | | 7 | QQ | | NULL | | 6 | 94.0 | 7 | 5 | | 7 | QQ | | NULL | | 7 | 92.0 | 7 | 5 | | 8 | 腾讯云 | | NULL | | 8 | 92.0 | 8 | 5 | | 8 | 腾讯云 | | NULL | | 9 | 72.0 | 8 | 5 | | 2 | 腾旭 | | 1990-02-02 00:00:00 | | NULL | NULL | NULL | NULL | | 6 | 酷狗 | | 2017-02-13 00:00:00 | | NULL | NULL | NULL | NULL | | 9 | 华为 | | NULL | | NULL | NULL | NULL | NULL | | 10 | 京东 | | NULL | | NULL | NULL | NULL | NULL | | 11 | 微博 | | NULL | | NULL | NULL | NULL | NULL | | 12 | 微信 | | NULL | | NULL | NULL | NULL | NULL | +----+-----------+--------+---------------------+----------+------+-------+-------+-------+
- 表A right join 表B:表A与表B匹配的行会出现在结果中,外加表B中独有的数据,未对应的数据使用null填充
select * from scores right join students on students.id=scores.stuid
+------+-------+-------+-------+----+-----------+--------+---------------------+----------+ | id | score | stuid | subid | id | name | gender | birthday | isDelete | +------+-------+-------+-------+----+-----------+--------+---------------------+----------+ | 1 | 100.0 | 1 | 1 | 1 | 腾旭 | | 1999-09-09 00:00:00 | | | 3 | 100.0 | 3 | 2 | 3 | 网易 | | NULL | | | 4 | 100.0 | 4 | 5 | 4 | 小米 | | NULL | | | 5 | 94.0 | 3 | 5 | 3 | 网易 | | NULL | | | 6 | 94.0 | 7 | 5 | 7 | QQ | | NULL | | | 7 | 92.0 | 7 | 5 | 7 | QQ | | NULL | | | 8 | 92.0 | 8 | 5 | 8 | 腾讯云 | | NULL | | | 9 | 72.0 | 8 | 5 | 8 | 腾讯云 | | NULL | | | NULL | NULL | NULL | NULL | 2 | 腾旭 | | 1990-02-02 00:00:00 | | | NULL | NULL | NULL | NULL | 6 | 酷狗 | | 2017-02-13 00:00:00 | | | NULL | NULL | NULL | NULL | 9 | 华为 | | NULL | | | NULL | NULL | NULL | NULL | 10 | 京东 | | NULL | | | NULL | NULL | NULL | NULL | 11 | 微博 | | NULL | | | NULL | NULL | NULL | NULL | 12 | 微信 | | NULL | | +------+-------+-------+-------+----+-----------+--------+---------------------+----------+
5.练习
- 查询学生的姓名、平均分
mysql> select stuid,avg(score) from scores group by stuid; +-------+------------+ | stuid | avg(score) | +-------+------------+ | 1 | 100.00000 | | 3 | 97.00000 | | 4 | 100.00000 | | 7 | 93.00000 | | 8 | 82.00000 | +-------+------------+
mysql> select stuid,avg(score) from scores inner join students on scores.stuid=students.id group by stuid; +-------+------------+ | stuid | avg(score) | +-------+------------+ | 1 | 100.00000 | | 3 | 97.00000 | | 4 | 100.00000 | | 7 | 93.00000 | | 8 | 82.00000 | +-------+------------+
mysql> select name,stuid,avg(score) from scores inner join students on scores.stuuid=students.id group by stuid; +-----------+-------+------------+ | name | stuid | avg(score) | +-----------+-------+------------+ | 腾旭 | 1 | 100.00000 | | 网易 | 3 | 97.00000 | | 小米 | 4 | 100.00000 | | QQ | 7 | 93.00000 | | 腾讯云 | 8 | 82.00000 | +-----------+-------+------------+ 5 rows in set (0.00 sec)
mysql> select name,avg(score) from scores inner join students on scores.stuid=students.id group by stuid; +-----------+------------+ | name | avg(score) | +-----------+------------+ | 腾旭 | 100.00000 | | 网易 | 97.00000 | | 小米 | 100.00000 | | QQ | 93.00000 | | 腾讯云 | 82.00000 | +-----------+------------+
mysql> select name,avg(score) from scores inner join students on scores.stuid=students.id group by stuid order by avg(score) desc; +-----------+------------+ | name | avg(score) | +-----------+------------+ | 小米 | 100.00000 | | 腾旭 | 100.00000 | | 网易 | 97.00000 | | QQ | 93.00000 | | 腾讯云 | 82.00000 | +-----------+------------+
- 查询男生的姓名、总分
select students.sname,avg(scores.score) from scores inner join students on scores.stuid=students.id where students.gender=1 group by students.sname;
- 查询科目的名称、平均分
select subjects.stitle,avg(scores.score) from scores inner join subjects on scores.subid=subjects.id group by subjects.stitle;
- 查询未删除科目的名称、最高分、平均分
select subjects.stitle,avg(scores.score),max(scores.score) from scores inner join subjects on scores.subid=subjects.id where subjects.isdelete=0 group by subjects.stitle;