數據庫練習-多表聯查
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 | 郵政卡 |
+------+--------+--------+------+-----------+