数据库练习-多表联查
1多表联查
查询所有学生的 name
,以及该学生在 score
表中对应的 cno
和 degree
。
SELECT no, name FROM student;
+-----+-----------+
| no | name |
+-----+-----------+
| 101 | 曾华 |
| 102 | 匡明 |
| 103 | 王丽 |
| 104 | 李军 |
| 105 | 王芳 |
| 106 | 陆军 |
| 107 | 王尼玛 |
| 108 | 张全蛋 |
| 109 | 赵铁柱 |
+-----+-----------+
SELECT sno, cno, degree FROM score;
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-105 | 92 |
| 103 | 3-245 | 86 |
| 103 | 6-166 | 85 |
| 105 | 3-105 | 88 |
| 105 | 3-245 | 75 |
| 105 | 6-166 | 79 |
| 109 | 3-105 | 76 |
| 109 | 3-245 | 68 |
| 109 | 6-166 | 81 |
+------+-------+--------+
通过分析可以发现,只要把 score
表中的 sno
字段值替换成 student
表中对应的 name
字段值就可以了,如何做呢?
# select 要查询的字段1,要查询的字段2,要查询的字段3 from 这些字段从那个表查,这些字段从那个表查 where 根据那个表的字段进行筛选=根据那个表的字段进行筛选;
mysql> select sname,cno,degree from student,score where student.sno=score.sno;
+--------+-------+--------+
| sname | cno | degree |
+--------+-------+--------+
| 王丽 | 3-105 | 92 |
| 王丽 | 3-245 | 86 |
| 王丽 | 6-166 | 85 |
| 王芳 | 3-105 | 88 |
| 王芳 | 3-245 | 75 |
| 王芳 | 6-166 | 79 |
| 赵铁柱 | 3-105 | 76 |
| 赵铁柱 | 3-245 | 68 |
| 赵铁柱 | 6-166 | 81 |
+--------+-------+--------+
9 rows in set (0.08 sec)
查询所有学生的 sno
、课程名称 ( course
表中的 name
) 和成绩 ( score
表中的 degree
) 列。
mysql> select sname,cname,degree from student,score,course where student.sno=score.sno and score.cno=course.cno;
+--------+------------+--------+
| sname | cname | degree |
+--------+------------+--------+
| 王丽 | 计算机导论 | 92 |
| 王丽 | 操作系统 | 86 |
| 王丽 | 数字电路 | 85 |
| 王芳 | 计算机导论 | 88 |
| 王芳 | 操作系统 | 75 |
| 王芳 | 数字电路 | 79 |
| 赵铁柱 | 计算机导论 | 76 |
| 赵铁柱 | 操作系统 | 68 |
| 赵铁柱 | 数字电路 | 81 |
+--------+------------+--------+
9 rows in set (0.10 sec)
2子查询加分组求平均分
查询 95031
班学生每门课程的平均成绩。
在 score
表中根据 student
表的学生编号筛选出学生的课堂号和成绩:
-- IN (..): 将筛选出的学生号当做 sno 的条件查询
SELECT sno, c_no, degree FROM score
WHERE sno IN (SELECT no FROM student WHERE class = '95031');
+------+-------+--------+
| sno | cno | degree |
+------+-------+--------+
| 105 | 3-105 | 88 |
| 105 | 3-245 | 75 |
| 105 | 6-166 | 79 |
| 109 | 3-105 | 76 |
| 109 | 3-245 | 68 |
| 109 | 6-166 | 81 |
+------+-------+--------+
这时只要将 cno
分组一下就能得出 95031
班学生每门课的平均成绩:
SELECT c_no, AVG(degree) FROM score
WHERE s_no IN (SELECT no FROM student WHERE class = '95031')
GROUP BY c_no;
+-------+-------------+
| c_no | AVG(degree) |
+-------+-------------+
| 3-105 | 82.0000 |
| 3-245 | 71.5000 |
| 6-166 | 80.0000 |
+-------+-------------+
3查询在 3-105
课程中,所有成绩高于 109
号同学的记录。
思路:先查询出3-105中109号同学的成绩,然后在根据查出来的结果去查询大于当前同学成绩的学生
#先查询出3-105中109号同学的成绩
mysql> select degree from score where sno='109' and cno='3-105';
+--------+
| degree |
+--------+
| 76 |
+--------+
1 row in set (0.06 sec)
#根据查出来的结果去查询大于当前同学成绩的学生
#注意:在嵌套查询时,整个sql语句只能有一个结束符-->分号
mysql> select * from score where degree>(select degree from score where sno='109' and cno='3-105') and cno='3-105';
+-----+-------+--------+
| sno | cno | degree |
+-----+-------+--------+
| 103 | 3-105 | 92 |
| 105 | 3-105 | 88 |
+-----+-------+--------+
2 rows in set (0.06 sec)
4YEAR 函数与带 IN 关键字查询
查询所有和 101
、108
号学生同年出生的 no
、name
、birthday
列。
# 先查询出这两位同学的生日所在年份
# 使用year()函数可以直接选取年份
mysql> select year(sbirthday) from student where sno in('108','109');
+-----------------+
| year(sbirthday) |
+-----------------+
| 1975 |
| 1974 |
+-----------------+
2 rows in set (0.06 sec)
#根据查出来的年份作为条件去筛选 ,因为查出来的年份有两个,所以使用in关键字
mysql> select * from student where year(sbirthday) in ( select year(sbirthday) from student where sno in('108','109'));
+-----+--------+------+---------------------+--------+
| sno | sname | ssex | sbirthday | sclass |
+-----+--------+------+---------------------+--------+
| 102 | 匡明 | 男 | 1975-10-02 00:00:00 | 95031 |
| 105 | 王芳 | 女 | 1975-02-10 00:00:00 | 95031 |
| 106 | 陆军 | 男 | 1974-06-03 00:00:00 | 95031 |
| 108 | 张全蛋 | 男 | 1975-02-10 00:00:00 | 95031 |
| 109 | 赵铁柱 | 男 | 1974-06-03 00:00:00 | 95031 |
+-----+--------+------+---------------------+--------+
5 rows in set (0.10 sec)
5UNION 和 NOTIN 的使用
查询 计算机系
与 电子工程系
中的不同职称的教师。
-- NOT: 代表逻辑非
SELECT * FROM teacher WHERE department = '计算机系' AND profession NOT IN (
SELECT profession FROM teacher WHERE department = '电子工程系'
)
-- 合并两个集UNION
UNION
SELECT * FROM teacher WHERE department = '电子工程系' AND profession NOT IN (
SELECT profession FROM teacher WHERE department = '计算机系'
);
6表示所有的 ALL
查询课程 3-105
且成绩高于 3-245
的 score
表。
-- ALL: 符合SQL语句中的所有条件。
-- 也就是说,在 3-105 每一行成绩中,都要大于从 3-245 筛选出来全部行才算符合条件。
SELECT * FROM score WHERE cno = '3-105' AND degree > ALL(
SELECT degree FROM score WHERE cno = '3-245'
);
+-----+-------+--------+
| sno | cno | degree |
+-----+-------+--------+
| 103 | 3-105 | 92 |
| 105 | 3-105 | 88 |
+-----+-------+--------+
2 rows in set (0.09 sec)
7ANY 表示至少一个 - DESC ( 降序 )
查询课程 3-105
且成绩 至少 高于 3-245
的 score
表。
SELECT * FROM score WHERE c_no = '3-105';
+------+-------+--------+
| sno | cno | degree |
+------+-------+--------+
| 101 | 3-105 | 90 |
| 102 | 3-105 | 91 |
| 103 | 3-105 | 92 |
| 104 | 3-105 | 89 |
| 105 | 3-105 | 88 |
| 109 | 3-105 | 76 |
+------+-------+--------+
SELECT * FROM score WHERE cno = '3-245';
+------+-------+--------+
| s_no | c_no | degree |
+------+-------+--------+
| 103 | 3-245 | 86 |
| 105 | 3-245 | 75 |
| 109 | 3-245 | 68 |
+------+-------+--------+
-- ANY: 符合SQL语句中的任意条件。
-- 也就是说,在 3-105 成绩中,只要有一个大于从 3-245 筛选出来的任意行就符合条件,
-- 最后根据降序查询结果。
SELECT * FROM score WHERE cno = '3-105' AND degree > ANY(
SELECT degree FROM score WHERE cno = '3-245'
) ORDER BY degree DESC;
+------+-------+--------+
| sno | cno | degree |
+------+-------+--------+
| 103 | 3-105 | 92 |
| 102 | 3-105 | 91 |
| 101 | 3-105 | 90 |
| 104 | 3-105 | 89 |
| 105 | 3-105 | 88 |
| 109 | 3-105 | 76 |
+------+-------+--------+
8按等级查询
建立一个 grade
表代表学生的成绩等级,并插入数据:
CREATE TABLE grade (
low INT(3),
upp INT(3),
grade char(1)
);
INSERT INTO grade VALUES (90, 100, 'A');
INSERT INTO grade VALUES (80, 89, 'B');
INSERT INTO grade VALUES (70, 79, 'C');
INSERT INTO grade VALUES (60, 69, 'D');
INSERT INTO grade VALUES (0, 59, 'E');
查询所有学生的 sno
、cno
和 grade
列。
思路是,使用区间 ( BETWEEN
) 查询,判断学生的成绩 ( degree
) 在 grade
表的 low
和 upp
之间。
#语句解释:两表联查,以成绩进行区间查询,当成绩在对应的区间的时候显示;
# select 查询的字段1,查询的字段2,查询的字段3 from 字段来自表1,字段来自表2 where 以成绩划分 between 区间条件1 and 区间条件2;
mysql> select sno,cno,grade from score,grade where degree between low and upp;
+-----+-------+-------+
| sno | cno | grade |
+-----+-------+-------+
| 103 | 3-105 | A |
| 103 | 3-245 | B |
| 103 | 6-166 | B |
| 105 | 3-105 | B |
| 105 | 3-245 | C |
| 105 | 6-166 | C |
| 109 | 3-105 | C |
| 109 | 3-245 | D |
| 109 | 6-166 | B |
+-----+-------+-------+
9 rows in set (0.06 sec)
9连接查询
sql的四种连接查询;
内连接 inner join
或者join
内联查询就是两张表中的的数据,通过某个字段有对应关系,查出相关记录数据
外连接
—>左连接 left join
或者 left outer join
完整显示左边的表,右边的表如果符合条件就显示,不符合则补 NULL
。
—>右连接 right join
或者 right outer join
完整显示右边的表 ,左边的表如果符合条件就显示,不符合则补 NULL
。
—>全外连接 full join
或者 full outer join
完全显示两张表的数据(mysql不支持)
连接查询数据准备
CREATE DATABASE testJoin;
CREATE TABLE person (
id INT,
name VARCHAR(20),
cardId INT
);
CREATE TABLE card (
id INT,
name VARCHAR(20)
);
INSERT INTO card VALUES (1, '饭卡'), (2, '建行卡'), (3, '农行卡'), (4, '工商卡'), (5, '邮政卡');
SELECT * FROM card;
+------+-----------+
| id | name |
+------+-----------+
| 1 | 饭卡 |
| 2 | 建行卡 |
| 3 | 农行卡 |
| 4 | 工商卡 |
| 5 | 邮政卡 |
+------+-----------+
INSERT INTO person VALUES (1, '张三', 1), (2, '李四', 3), (3, '王五', 6);
SELECT * FROM person;
+------+--------+--------+
| id | name | cardId |
+------+--------+--------+
| 1 | 张三 | 1 |
| 2 | 李四 | 3 |
| 3 | 王五 | 6 |
+------+--------+--------+
分析两张表发现,person
表并没有为 cardId
字段设置一个在 card
表中对应的 id
外键。如果设置了的话,person
中 cardId
字段值为 6
的行就插不进去,因为该 cardId
值在 card
表中并没有。
内连接
要查询这两张表中有关系的数据,可以使用 INNER JOIN
( 内连接 ) 将它们连接在一起。
-- INNER JOIN: 表示为内连接,将两张表拼接在一起。
-- on: 表示要执行某个条件。
-- 语句解释:SELECT * FROM 查询的表名1 INNER JOIN 查询的表名2 on 筛选条件;
SELECT * FROM person INNER JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id | name | cardId | id | name |
+------+--------+--------+------+-----------+
| 1 | 张三 | 1 | 1 | 饭卡 |
| 2 | 李四 | 3 | 3 | 农行卡 |
+------+--------+--------+------+-----------+
-- 将 INNER 关键字省略掉,结果也是一样的。
-- SELECT * FROM person JOIN card on person.cardId = card.id;
-- ------注意:card 的整张表被连接到了右边。
左外连接
完整显示左边的表 ( person
) ,右边的表如果符合条件就显示,不符合则补 NULL
。
-- LEFT JOIN 也叫做 LEFT OUTER JOIN,用这两种方式的查询结果是一样的。
mysql> select * from person left join card on person.cardid=card.id;
+----+------+--------+------+--------+
| id | name | cardId | id | name |
+----+------+--------+------+--------+
| 1 | 张三 | 1 | 1 | 饭卡 |
| 2 | 李四 | 3 | 3 | 农行卡 |
| 3 | 王五 | 6 | NULL | NULL |
+----+------+--------+------+--------+
3 rows in set (0.07 sec)
右外链接
完整显示右边的表 ( card
) ,左边的表如果符合条件就显示,不符合则补 NULL
。
#-- right join 也叫做right outer join,用这两种方式的查询结果是一样的。
SELECT * FROM person RIGHT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id | name | cardId | id | name |
+------+--------+--------+------+-----------+
| 1 | 张三 | 1 | 1 | 饭卡 |
| 2 | 李四 | 3 | 3 | 农行卡 |
| NULL | NULL | NULL | 2 | 建行卡 |
| NULL | NULL | NULL | 4 | 工商卡 |
| NULL | NULL | NULL | 5 | 邮政卡 |
+------+--------+--------+------+-----------+
全外连接
完全显示两张表的数据
-- MySQL 不支持这种语法的全外连接
-- SELECT * FROM person FULL JOIN card on person.cardId = card.id;
-- 出现错误:
-- ERROR 1054 (42S22): Unknown column 'person.cardId' in 'on clause'
-- MySQL全连接语法,使用 UNION 将两张表合并在一起。
SELECT * FROM person LEFT JOIN card on person.cardId = card.id
UNION
SELECT * FROM person RIGHT JOIN card on person.cardId = card.id;
+------+--------+--------+------+-----------+
| id | name | cardId | id | name |
+------+--------+--------+------+-----------+
| 1 | 张三 | 1 | 1 | 饭卡 |
| 2 | 李四 | 3 | 3 | 农行卡 |
| 3 | 王五 | 6 | NULL | NULL |
| NULL | NULL | NULL | 2 | 建行卡 |
| NULL | NULL | NULL | 4 | 工商卡 |
| NULL | NULL | NULL | 5 | 邮政卡 |
+------+--------+--------+------+-----------+