mysql四-2:多表查詢

1、介紹

本節主題:mysql

  • 多表鏈接查詢
  • 複合條件鏈接查詢
  • 子查詢

準備表:sql

#建表
create table department(
id int,
name varchar(20) 
);

create table employee(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);

#插入數據
insert into department values
(200,'技術'),
(201,'人力資源'),
(202,'銷售'),
(203,'運營');

insert into employee(name,sex,age,dep_id) values
('egon','male',18,200),
('alex','female',48,201),
('wupeiqi','male',38,201),
('yuanhao','female',28,202),
('liwenzhou','male',18,200),
('jingliyang','female',18,204)
;


#查看錶結構和數據
mysql> desc department;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

mysql> desc employee;
+--------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| sex | enum('male','female') | NO | | male | |
| age | int(11) | YES | | NULL | |
| dep_id | int(11) | YES | | NULL | |
+--------+-----------------------+------+-----+---------+----------------+

mysql> select * from department;
+------+--------------+
| id | name |
+------+--------------+
| 200 | 技術 |
| 201 | 人力資源 |
| 202 | 銷售 |
| 203 | 運營 |
+------+--------------+

mysql> select * from employee;
+----+------------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+------------+--------+------+--------+
| 1 | egon | male | 18 | 200 |
| 2 | alex | female | 48 | 201 |
| 3 | wupeiqi | male | 38 | 201 |
| 4 | yuanhao | female | 28 | 202 |
| 5 | liwenzhou | male | 18 | 200 |
| 6 | jingliyang | female | 18 | 204 |
+----+------------+--------+------+--------+
建立department和employee表,並寫入數據

  須要注意的是在employee表有一個dep_id=204是department表沒有的。同時,department表有一個id=203在employee表沒有對應的記錄。ide

2、多表鏈接查詢

#重點:外連接語法

SELECT 字段列表
    FROM 表1 INNER|LEFT|RIGHT JOIN 表2
    ON 表1.字段 = 表2.字段;

一、交叉鏈接:不適用任何匹配條件,生成笛卡爾積

mysql> select * from employee, department;
+----+------------+--------+------+--------+------+--------------+
| id | name       | sex    | age  | dep_id | id   | name         |
+----+------------+--------+------+--------+------+--------------+
|  1 | egon       | male   |   18 |    200 |  200 | 技術         |
|  1 | egon       | male   |   18 |    200 |  201 | 人力資源     |
|  1 | egon       | male   |   18 |    200 |  202 | 銷售         |
|  1 | egon       | male   |   18 |    200 |  203 | 運營         |
|  2 | alex       | female |   48 |    201 |  200 | 技術         |
|  2 | alex       | female |   48 |    201 |  201 | 人力資源     |
|  2 | alex       | female |   48 |    201 |  202 | 銷售         |
|  2 | alex       | female |   48 |    201 |  203 | 運營         |
|  3 | wupeiqi    | male   |   38 |    201 |  200 | 技術         |
|  3 | wupeiqi    | male   |   38 |    201 |  201 | 人力資源     |
|  3 | wupeiqi    | male   |   38 |    201 |  202 | 銷售         |
|  3 | wupeiqi    | male   |   38 |    201 |  203 | 運營         |
|  4 | yuanhao    | female |   28 |    202 |  200 | 技術         |
|  4 | yuanhao    | female |   28 |    202 |  201 | 人力資源     |
|  4 | yuanhao    | female |   28 |    202 |  202 | 銷售         |
|  4 | yuanhao    | female |   28 |    202 |  203 | 運營         |
|  5 | liwenzhou  | male   |   18 |    200 |  200 | 技術         |
|  5 | liwenzhou  | male   |   18 |    200 |  201 | 人力資源     |
|  5 | liwenzhou  | male   |   18 |    200 |  202 | 銷售         |
|  5 | liwenzhou  | male   |   18 |    200 |  203 | 運營         |
|  6 | jingliyang | female |   18 |    204 |  200 | 技術         |
|  6 | jingliyang | female |   18 |    204 |  201 | 人力資源     |
|  6 | jingliyang | female |   18 |    204 |  202 | 銷售         |
|  6 | jingliyang | female |   18 |    204 |  203 | 運營         |
+----+------------+--------+------+--------+------+--------------+
24 rows in set (0.01 sec)

  每一個員工和四個部門作一次對應關係。但這四次匹配僅有一次是須要的:即dep_id和id相同的那個。工具

mysql> select * from employee, department where employee.dep_id = department.id;
+----+-----------+--------+------+--------+------+--------------+
| id | name      | sex    | age  | dep_id | id   | name         |
+----+-----------+--------+------+--------+------+--------------+
|  1 | egon      | male   |   18 |    200 |  200 | 技術         |
|  2 | alex      | female |   48 |    201 |  201 | 人力資源     |
|  3 | wupeiqi   | male   |   38 |    201 |  201 | 人力資源     |
|  4 | yuanhao   | female |   28 |    202 |  202 | 銷售         |
|  5 | liwenzhou | male   |   18 |    200 |  200 | 技術         |
+----+-----------+--------+------+--------+------+--------------+
5 rows in set (0.00 sec)

  經過對笛卡爾積的再處理,得出了兩張表有對應關係的部分。可是兩張表上,對方沒有對上的記錄並無顯示出來。所以須要使用mysql給的專用處理工具來處理。post

二、內鏈接:只鏈接匹配的行

  即取兩張表共同部分,至關於上面利用條件從笛卡爾積結果中篩選出正確結果。學習

#department沒有204這個部門,於是employee表中關於204這條員工信息沒有匹配出來
mysql> select * from employee inner join department on employee.dep_id = department.id;
+----+-----------+--------+------+--------+------+--------------+
| id | name      | sex    | age  | dep_id | id   | name         |
+----+-----------+--------+------+--------+------+--------------+
|  1 | egon      | male   |   18 |    200 |  200 | 技術         |
|  2 | alex      | female |   48 |    201 |  201 | 人力資源     |
|  3 | wupeiqi   | male   |   38 |    201 |  201 | 人力資源     |
|  4 | yuanhao   | female |   28 |    202 |  202 | 銷售         |
|  5 | liwenzhou | male   |   18 |    200 |  200 | 技術         |
+----+-----------+--------+------+--------+------+--------------+
5 rows in set (0.00 sec)
# 與上面笛卡爾where篩選的執行結果徹底同樣

三、外連接之左鏈接:在內鏈接的基礎上保留左表的記錄

  在內鏈接的基礎上保留左表有但右邊沒有的記錄——以左表爲準,找出全部員工信息,包括沒有部門的員工。spa

mysql> select * from employee left join department on employee.dep_id = department.id;     
+----+------------+--------+------+--------+------+--------------+
| id | name       | sex    | age  | dep_id | id   | name         |
+----+------------+--------+------+--------+------+--------------+
|  1 | egon       | male   |   18 |    200 |  200 | 技術         |
|  5 | liwenzhou  | male   |   18 |    200 |  200 | 技術         |
|  2 | alex       | female |   48 |    201 |  201 | 人力資源     |
|  3 | wupeiqi    | male   |   38 |    201 |  201 | 人力資源     |
|  4 | yuanhao    | female |   28 |    202 |  202 | 銷售         |
|  6 | jingliyang | female |   18 |    204 | NULL | NULL         |
+----+------------+--------+------+--------+------+--------------+
6 rows in set (0.00 sec)

  沒有對應的記錄也顯示出來了,並用NULL顯示。code

四、外連接之右鏈接:在內鏈接的基礎上保留右表的記錄

  在內鏈接的基礎上增長右邊有左邊沒有的結果——以右表爲準,找出全部部門信息,包括沒有員工的部門。blog

mysql> select * from employee right join department on employee.dep_id = department.id;        
+------+-----------+--------+------+--------+------+--------------+
| id   | name      | sex    | age  | dep_id | id   | name         |
+------+-----------+--------+------+--------+------+--------------+
|    1 | egon      | male   |   18 |    200 |  200 | 技術         |
|    2 | alex      | female |   48 |    201 |  201 | 人力資源     |
|    3 | wupeiqi   | male   |   38 |    201 |  201 | 人力資源     |
|    4 | yuanhao   | female |   28 |    202 |  202 | 銷售         |
|    5 | liwenzhou | male   |   18 |    200 |  200 | 技術         |
| NULL | NULL      | NULL   | NULL |   NULL |  203 | 運營         |
+------+-----------+--------+------+--------+------+--------------+
6 rows in set (0.00 sec)

  因爲employee表沒有運營部門員工信息,以NULL顯示。排序

五、全外鏈接:在內鏈接的基礎上保留,左右兩個表沒有對應關係的記錄

  在內鏈接的基礎上增長左邊有右邊沒有和右邊有左邊沒有的結果——顯示所有記錄

注意:mysql不支持full join

mysql> select * from employee full join department on employee.dep_id = department.id;     
ERROR 1054 (42S22): Unknown column 'employee.dep_id' in 'on clause'

mysql使用union來間接實現全外鏈接

mysql> select * from employee left join department on employee.dep_id = department.id
    -> union
    -> select * from employee right join department on employee.dep_id = department.id;
+------+------------+--------+------+--------+------+--------------+
| id   | name       | sex    | age  | dep_id | id   | name         |
+------+------------+--------+------+--------+------+--------------+
|    1 | egon       | male   |   18 |    200 |  200 | 技術         |
|    5 | liwenzhou  | male   |   18 |    200 |  200 | 技術         |
|    2 | alex       | female |   48 |    201 |  201 | 人力資源     |
|    3 | wupeiqi    | male   |   38 |    201 |  201 | 人力資源     |
|    4 | yuanhao    | female |   28 |    202 |  202 | 銷售         |
|    6 | jingliyang | female |   18 |    204 | NULL | NULL         |
| NULL | NULL       | NULL   | NULL |   NULL |  203 | 運營         |
+------+------------+--------+------+--------+------+--------------+
7 rows in set (0.01 sec)

  注意union與union all的區別:union會去掉相同的記錄。

3、符合條件鏈接查詢

  要理解鏈接查詢,能夠如下面這個案例爲例來理解:

查詢出員工平均年齡大於30的部門?

  一、(判斷是否要連表)經過分析要求能夠得出與員工表、部門表都有關係,所以須要作多表鏈接查詢。

  二、(判斷使用哪一種鏈接方式)進一步分析得出須要查詢的就是員工和部門有關係的部門,所以應該使用內鏈接。

mysql> select * from employee inner join department on employee.dep_id = department.id;
+----+-----------+--------+------+--------+------+--------------+
| id | name      | sex    | age  | dep_id | id   | name         |
+----+-----------+--------+------+--------+------+--------------+
|  1 | egon      | male   |   18 |    200 |  200 | 技術         |
|  2 | alex      | female |   48 |    201 |  201 | 人力資源     |
|  3 | wupeiqi   | male   |   38 |    201 |  201 | 人力資源     |
|  4 | yuanhao   | female |   28 |    202 |  202 | 銷售         |
|  5 | liwenzhou | male   |   18 |    200 |  200 | 技術         |
+----+-----------+--------+------+--------+------+--------------+
5 rows in set (0.00 sec)

  上面就獲得了一張虛擬表。

  虛擬表——就是不在硬盤上,存放在內存中的一張臨時表。但能夠在虛擬表的基礎上進行查詢篩選等操做。

mysql> select department.name, avg(age) from employee inner join department on employee.dep_id = department.id
    -> group by department.name   # 按名稱分組,方便取部門名,因爲name有兩個,所以須要指名道姓
    -> having avg(age) > 30;
+--------------+----------+
| name         | avg(age) |
+--------------+----------+
| 人力資源     |  43.0000 |
+--------------+----------+
1 row in set (0.01 sec)

  多表查詢的概念就是把有關係的表經過鏈接的方式拼爲一個總體,進而來進行相應的關聯查詢

#示例1:之內鏈接的方式查詢employee和department表,而且employee表中的age字段值必須大於25,即找出年齡大於25歲的員工以及員工所在的部門
select employee.name,department.name from employee inner join department
    on employee.dep_id = department.id
    where age > 25;

#示例2:之內鏈接的方式查詢employee和department表,而且以age字段的升序方式顯示
select employee.id,employee.name,employee.age,department.name from employee,department
    where employee.dep_id = department.id
    and age > 25
    order by age asc;
其餘示例

4、子查詢

  子查詢概念:子查詢是將一個查詢語句嵌套在另外一個查詢語句中。

        內層查詢語句的查詢結果,能夠爲外層查詢語句提供查詢條件。

  子查詢關鍵字:子查詢能夠包含 IN 、NOT 、IN 、ANY 、ALL EXISTS 和 NOT EXISTS 等關鍵字。

  子查詢還能夠包含比較運算符:= 、!= 、> 、<等

一、帶IN關鍵字的子查詢

#1、查詢平均年齡在25歲以上的部門名
# 分拆問題,先找到平均年齡在25歲以上的人的部門id
mysql> select dep_id from employee 
    -> group by dep_id
    -> having avg(age) > 25;
+--------+
| dep_id |
+--------+
|    201 |
|    202 |
+--------+
2 rows in set (0.01 sec)

mysql> select name from department where id in 
    -> (select dep_id from employee
    -> group by dep_id
    -> having avg(age) > 25);
+--------------+
| name         |
+--------------+
| 人力資源     |
| 銷售         |
+--------------+
2 rows in set (0.00 sec)

#2、查看技術部門員工姓名
mysql> select id from department where name='技術';
+------+
| id   |
+------+
|  200 |
+------+
1 row in set (0.00 sec)

mysql> select * from employee where dep_id=(
    -> select id from department where name='技術'
    -> );
+----+-----------+------+------+--------+
| id | name      | sex  | age  | dep_id |
+----+-----------+------+------+--------+
|  1 | egon      | male |   18 |    200 |
|  5 | liwenzhou | male |   18 |    200 |
+----+-----------+------+------+--------+
2 rows in set (0.00 sec)

#3、查看不足1人的部門名:也就是沒有人的部門
# 先找到有人的部門id,而後在部門表中取反
mysql> select distinct dep_id from employee;
+--------+
| dep_id |
+--------+
|    200 |
|    201 |
|    202 |
|    204 |
+--------+
4 rows in set (0.00 sec)

mysql> select name from department where id not in (
    -> select distinct dep_id from employee
    -> );
+--------+
| name   |
+--------+
| 運營   |
+--------+
1 row in set (0.00 sec)

mysql> select * from department where id not in ( 
    -> select distinct dep_id from employee 
    -> );    
+------+--------+
| id   | name   |
+------+--------+
|  203 | 運營   |
+------+--------+
1 row in set (0.01 sec)

二、帶比較運算符的子查詢

  比較運算符:=、!=、>、>=、<、<=、<>

# 查詢大於全部人平均年齡的員工名與年齡
mysql> select name,age from employee where age > (select avg(age) from employee);
+---------+------+
| name    | age  |
+---------+------+
| alex    |   48 |
| wupeiqi |   38 |
+---------+------+
2 rows in set (0.00 sec)

#查詢大於部門內平均年齡的員工名、年齡
mysql> select t1.name, t1.age from employee t1 
    -> inner join 
    -> (select dep_id, avg(age) avg_age from employee group by dep_id) t2 
    -> on t1.dep_id = t2.dep_id 
    -> where t1.age > t2.avg_age;
+------+------+
| name | age  |
+------+------+
| alex |   48 |
+------+------+
1 row in set (0.00 sec)

三、帶EXISTS關鍵字的子查詢

  EXISTS關字鍵字表示存在。在使用EXISTS關鍵字時,內層查詢語句不返回查詢的記錄。而是返回一個真假值:True或False

  當返回True時,外層查詢語句將進行查詢;當返回值爲False時,外層查詢語句不進行查詢

mysql> select * from employee           # 子查詢能查到結果,執行主查詢語句
    -> where EXISTS                     # EXISTS判斷子查詢是否有結果;與where連用將子查詢結果做爲條件
    -> (select id from department where name = '技術');   # 子查詢爲條件
+----+------------+--------+------+--------+
| id | name       | sex    | age  | dep_id |
+----+------------+--------+------+--------+
|  1 | egon       | male   |   18 |    200 |
|  2 | alex       | female |   48 |    201 |
|  3 | wupeiqi    | male   |   38 |    201 |
|  4 | yuanhao    | female |   28 |    202 |
|  5 | liwenzhou  | male   |   18 |    200 |
|  6 | jingliyang | female |   18 |    204 |
+----+------------+--------+------+--------+
6 rows in set (0.00 sec)

mysql> select * from employee
    -> where not exists   # 取反
    -> (select id from department where id=204);    # dep表沒有id=204的記錄
+----+------------+--------+------+--------+
| id | name       | sex    | age  | dep_id |
+----+------------+--------+------+--------+
|  1 | egon       | male   |   18 |    200 |
|  2 | alex       | female |   48 |    201 |
|  3 | wupeiqi    | male   |   38 |    201 |
|  4 | yuanhao    | female |   28 |    202 |
|  5 | liwenzhou  | male   |   18 |    200 |
|  6 | jingliyang | female |   18 |    204 |
+----+------------+--------+------+--------+
6 rows in set (0.00 sec)

  由此咱們看到咱們實際上是能夠把一個sql語句放在括號內做爲條件給主sql語句執行的。能夠用括號把一個sql語句括起來,起一個別名,當作一張表來操做,與其餘表進行鏈接。如下面的練習爲例。

練習:查詢每一個部門最新入職的那個員工

  準備表:運用上一節的employee和department表

# 查詢語句當作一張表
mysql> select * from  
    -> (select id,name,sex from employee) as t1;
+----+------------+--------+
| id | name       | sex    |
+----+------------+--------+
|  1 | egon       | male   |
|  2 | alex       | female |
|  3 | wupeiqi    | male   |
|  4 | yuanhao    | female |
|  5 | liwenzhou  | male   |
|  6 | jingliyang | female |
+----+------------+--------+
6 rows in set (0.01 sec)


# 每一個部門最新入職的那名員工
select * from employee as t1
inner join
(select post, max(hire_date) as max_hire_date from employee group by post) as t2
on t1.post = t2.post
where t1.hire_date=t2.max_hire_date;

Database changed
mysql> show tables;
+---------------+
| Tables_in_db2 |
+---------------+
| class         |
| course        |
| employee      |
| score         |
| student       |
| teacher       |
+---------------+
6 rows in set (0.00 sec)

mysql> select post, max(hire_date) from employee
    -> group by post;
+-----------------------------------------+----------------+
| post                                    | max(hire_date) |
+-----------------------------------------+----------------+
| operation                               | 2016-03-11     |
| sale                                    | 2017-01-27     |
| teacher                                 | 2015-03-02     |
| 老男孩駐沙河辦事處外交大使              | 2017-03-01     |
+-----------------------------------------+----------------+
4 rows in set (0.01 sec)

mysql> select * from employee as t1
    -> inner join
    -> (select post, max(hire_date) as max_hire_date from employee group by post) as t2
    -> on t1.post = t2.post
    -> where t1.hire_date=t2.max_hire_date;
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+-----------------------------------------+---------------+
| id | name   | sex    | age | hire_date  | post                                    | post_comment | salary     | office | depart_id | post                                    | max_hire_date |
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+-----------------------------------------+---------------+
|  1 | egon   | male   |  18 | 2017-03-01 | 老男孩駐沙河辦事處外交大使              | NULL         |    7300.33 |    401 |         1 | 老男孩駐沙河辦事處外交大使              | 2017-03-01    |
|  2 | alex   | male   |  78 | 2015-03-02 | teacher                                 | NULL         | 1000000.31 |    401 |         1 | teacher                                 | 2015-03-02    |
| 13 | 格格   | female |  28 | 2017-01-27 | sale                                    | NULL         |    4000.33 |    402 |         2 | sale                                    | 2017-01-27    |
| 14 | 張野   | male   |  28 | 2016-03-11 | operation                               | NULL         |   10000.13 |    403 |         3 | operation                               | 2016-03-11    |
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+-----------------------------------------+---------------+
4 rows in set (0.00 sec)
練習答案(子查詢)
mysql> SELECT
    ->     *
    -> FROM
    ->     employee AS t1
    -> INNER JOIN (
    ->     SELECT
    ->         post,
    ->         max(hire_date) max_date
    ->     FROM
    ->         employee
    ->     GROUP BY
    ->         post
    -> ) AS t2 ON t1.post = t2.post
    -> WHERE
    ->     t1.hire_date = t2.max_date;
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+-----------------------------------------+------------+
| id | name   | sex    | age | hire_date  | post                                    | post_comment | salary     | office | depart_id | post                                    | max_date   |
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+-----------------------------------------+------------+
|  1 | egon   | male   |  18 | 2017-03-01 | 老男孩駐沙河辦事處外交大使              | NULL         |    7300.33 |    401 |         1 | 老男孩駐沙河辦事處外交大使              | 2017-03-01 |
|  2 | alex   | male   |  78 | 2015-03-02 | teacher                                 | NULL         | 1000000.31 |    401 |         1 | teacher                                 | 2015-03-02 |
| 13 | 格格   | female |  28 | 2017-01-27 | sale                                    | NULL         |    4000.33 |    402 |         2 | sale                                    | 2017-01-27 |
| 14 | 張野   | male   |  28 | 2016-03-11 | operation                               | NULL         |   10000.13 |    403 |         3 | operation                               | 2016-03-11 |
+----+--------+--------+-----+------------+-----------------------------------------+--------------+------------+--------+-----------+-----------------------------------------+------------+
4 rows in set (0.01 sec)
方法二:鏈表

5、綜合練習

5、綜合練習
/*
 數據導入:
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50624
 Source Host           : localhost
 Source Database       : sqlexam

 Target Server Type    : MySQL
 Target Server Version : 50624
 File Encoding         : utf-8

 Date: 10/21/2016 06:46:46 AM
*/

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `class`
-- ----------------------------
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
  `cid` int(11) NOT NULL AUTO_INCREMENT,
  `caption` varchar(32) NOT NULL,
  PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `class`
-- ----------------------------
BEGIN;
INSERT INTO `class` VALUES ('1', '三年二班'), ('2', '三年三班'), ('3', '一年二班'), ('4', '二年九班');
COMMIT;

-- ----------------------------
--  Table structure for `course`
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
  `cid` int(11) NOT NULL AUTO_INCREMENT,
  `cname` varchar(32) NOT NULL,
  `teacher_id` int(11) NOT NULL,
  PRIMARY KEY (`cid`),
  KEY `fk_course_teacher` (`teacher_id`),
  CONSTRAINT `fk_course_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `course`
-- ----------------------------
BEGIN;
INSERT INTO `course` VALUES ('1', '生物', '1'), ('2', '物理', '2'), ('3', '體育', '3'), ('4', '美術', '2');
COMMIT;

-- ----------------------------
--  Table structure for `score`
-- ----------------------------
DROP TABLE IF EXISTS `score`;
CREATE TABLE `score` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `student_id` int(11) NOT NULL,
  `course_id` int(11) NOT NULL,
  `num` int(11) NOT NULL,
  PRIMARY KEY (`sid`),
  KEY `fk_score_student` (`student_id`),
  KEY `fk_score_course` (`course_id`),
  CONSTRAINT `fk_score_course` FOREIGN KEY (`course_id`) REFERENCES `course` (`cid`),
  CONSTRAINT `fk_score_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `score`
-- ----------------------------
BEGIN;
INSERT INTO `score` VALUES ('1', '1', '1', '10'), ('2', '1', '2', '9'), ('5', '1', '4', '66'), ('6', '2', '1', '8'), ('8', '2', '3', '68'), ('9', '2', '4', '99'), ('10', '3', '1', '77'), ('11', '3', '2', '66'), ('12', '3', '3', '87'), ('13', '3', '4', '99'), ('14', '4', '1', '79'), ('15', '4', '2', '11'), ('16', '4', '3', '67'), ('17', '4', '4', '100'), ('18', '5', '1', '79'), ('19', '5', '2', '11'), ('20', '5', '3', '67'), ('21', '5', '4', '100'), ('22', '6', '1', '9'), ('23', '6', '2', '100'), ('24', '6', '3', '67'), ('25', '6', '4', '100'), ('26', '7', '1', '9'), ('27', '7', '2', '100'), ('28', '7', '3', '67'), ('29', '7', '4', '88'), ('30', '8', '1', '9'), ('31', '8', '2', '100'), ('32', '8', '3', '67'), ('33', '8', '4', '88'), ('34', '9', '1', '91'), ('35', '9', '2', '88'), ('36', '9', '3', '67'), ('37', '9', '4', '22'), ('38', '10', '1', '90'), ('39', '10', '2', '77'), ('40', '10', '3', '43'), ('41', '10', '4', '87'), ('42', '11', '1', '90'), ('43', '11', '2', '77'), ('44', '11', '3', '43'), ('45', '11', '4', '87'), ('46', '12', '1', '90'), ('47', '12', '2', '77'), ('48', '12', '3', '43'), ('49', '12', '4', '87'), ('52', '13', '3', '87');
COMMIT;

-- ----------------------------
--  Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `gender` char(1) NOT NULL,
  `class_id` int(11) NOT NULL,
  `sname` varchar(32) NOT NULL,
  PRIMARY KEY (`sid`),
  KEY `fk_class` (`class_id`),
  CONSTRAINT `fk_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `student`
-- ----------------------------
BEGIN;
INSERT INTO `student` VALUES ('1', '', '1', '理解'), ('2', '', '1', '鋼蛋'), ('3', '', '1', '張三'), ('4', '', '1', '張一'), ('5', '', '1', '張二'), ('6', '', '1', '張四'), ('7', '', '2', '鐵錘'), ('8', '', '2', '李三'), ('9', '', '2', '李一'), ('10', '', '2', '李二'), ('11', '', '2', '李四'), ('12', '', '3', '如花'), ('13', '', '3', '劉三'), ('14', '', '3', '劉一'), ('15', '', '3', '劉二'), ('16', '', '3', '劉四');
COMMIT;

-- ----------------------------
--  Table structure for `teacher`
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
  `tid` int(11) NOT NULL AUTO_INCREMENT,
  `tname` varchar(32) NOT NULL,
  PRIMARY KEY (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `teacher`
-- ----------------------------
BEGIN;
INSERT INTO `teacher` VALUES ('1', '張磊老師'), ('2', '李平老師'), ('3', '劉海燕老師'), ('4', '朱雲海老師'), ('5', '李傑老師');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;
init.sql初始化練習表
# 1、查詢全部的課程的名稱以及對應的任課老師姓名
 select cid, cname, teacher.tname from 
 course inner join teacher on course.teacher_id=teacher.tid;
+-----+--------+-----------------+
| cid | cname  | tname           |
+-----+--------+-----------------+
|   1 | 生物   | 張磊老師        |
|   2 | 物理   | 李平老師        |
|   3 | 體育   | 劉海燕老師      |
|   4 | 美術   | 李平老師        |
+-----+--------+-----------------+

# 2、查詢學生表中男女生各有多少人
select gender, count(sid) from student group by gender;
+--------+------------+
| gender | count(sid) |
+--------+------------+
||          6 |
||         10 |
+--------+------------+

# 3、查詢物理成績等於100的學生的姓名
select student.sname, course.cname, num from score 
inner join
student on score.student_id=student.sid 
inner join
course on score.course_id=course.cid
where num =100 and cname='物理';
+--------+--------+-----+
| sname  | cname  | num |
+--------+--------+-----+
| 張四   | 物理   | 100 |
| 鐵錘   | 物理   | 100 |
| 李三   | 物理   | 100 |
+--------+--------+-----+

# 4、查詢平均成績大於八十分的同窗的姓名和平均成績
select sname,avg(num) from score 
inner join 
student on score.student_id=student.sid 
group by sname
having avg(num)>80;
+--------+----------+
| sname  | avg(num) |
+--------+----------+
| 劉三   |  87.0000 |
| 張三   |  82.2500 |
+--------+----------+

# 5、查詢全部學生的學號,姓名,選課數,總成績
select student_id,concat(student.sname),count(student_id),sum(num) from score 
inner join 
student on score.student_id=student.sid 
group by student_id;
+------------+-----------------------+-------------------+----------+
| student_id | concat(student.sname) | count(student_id) | sum(num) |
+------------+-----------------------+-------------------+----------+
|          1 | 理解                  |                 3 |       85 |
|          2 | 鋼蛋                  |                 3 |      175 |
|          3 | 張三                  |                 4 |      329 |
|          4 | 張一                  |                 4 |      257 |
|          5 | 張二                  |                 4 |      257 |
|          6 | 張四                  |                 4 |      276 |
|          7 | 鐵錘                  |                 4 |      264 |
|          8 | 李三                  |                 4 |      264 |
|          9 | 李一                  |                 4 |      268 |
|         10 | 李二                  |                 4 |      297 |
|         11 | 李四                  |                 4 |      297 |
|         12 | 如花                  |                 4 |      297 |
|         13 | 劉三                  |                 1 |       87 |
+------------+-----------------------+-------------------+----------+
# 6、 查詢姓李老師的個數
select count(tname) as li_teacher_num from teacher where tname like '李%';
+----------------+
| li_teacher_num |
+----------------+
|              2 |
+----------------+

# 7、 查詢沒有報李平老師課的學生姓名
select student.sname from student
where sid not in (
select distinct student_id from score   # 找到學習過這兩門課的學生ID
where course_id in 
(select cid from course inner join teacher on course.teacher_id=teacher.tid where tname='李平老師')   # 查看到李平老師對應的課程
); 
+--------+
| sname  |
+--------+
| 劉三   |
| 劉一   |
| 劉二   |
| 劉四   |
+--------+

# 8、 查詢物理課程比生物課程高的學生的學號
select t1.student_id from (
    select * from score inner join course on score.course_id=course.cid having cname='物理') as t1
inner join (
    select * from score inner join course on score.course_id=course.cid having cname='生物') as t2
on t1.student_id=t2.student_id where t1.num > t2.num;
+------------+
| student_id |
+------------+
|          6 |
|          7 |
|          8 |
+------------+

# 9、 查詢沒有同時選修物理課程和體育課程的學生姓名
select student.sname from student 
where sid in (
    SELECT student_id FROM score WHERE course_id IN (     # 2、找到成績表中,只選修過物理或體育中一門的學生id,
        SELECT cid FROM course WHERE cname = '物理' OR cname = '體育'   # 1、獲得兩門課程的cid
    )   group by student_id having count(course_id) = 1);
+--------+
| sname  |
+--------+
| 理解   |
| 鋼蛋   |
| 劉三   |
+--------+

# 10、查詢掛科超過兩門(包括兩門)的學生姓名和班級
select student.sname, class.caption from student
    inner join(
        select student_id from score where num<60           # 1、全部不及格人的科目
            group by student_id having count(student_id)>=2) as t1      # 2、過濾出全部掛科超過兩門的人
    inner join class ON student.sid=t1.student_id           # 3、t1再和class表作內連
    and student.class_id = class.cid;                       # 4、student和class作內連
+--------+--------------+
| sname  | caption      |
+--------+--------------+
| 理解   | 三年二班     |
+--------+--------------+

# 11、查詢選修了全部課程的學生姓名
select student.sname from student
    where sid in (                 # 注意student的sid能夠 對 student_id使用in子查詢
        select student_id from score        # 按學生分組,找到學了四門的學生id
            group by student_id
                having count(student_id)=(select count(cid) from course)  # 引用全部的課程個數
                );
+--------+
| sname  |
+--------+
| 張三   |
| 張一   |
| 張二   |
| 張四   |
| 鐵錘   |
| 李三   |
| 李一   |
| 李二   |
| 李四   |
| 如花   |
+--------+

# 12、查詢李平老師教的課程的全部成績記錄
SELECT * FROM score
WHERE
    course_id IN (
        SELECT cid FROM course          # 查看全部李平老師課程的cid
            INNER JOIN teacher ON course.teacher_id = teacher.tid
            WHERE teacher.tname = '李平老師'
    );

# 13、查詢所有學生都選修了的課程號和課程名
# student\course\score 和這三張表關聯
select 
    cid,cname
from 
    course
where
    cid in (
        select
            course_id    # 按課程分組,找出count()後等於學生總數的課程
        from 
            score
        group by 
            course_id
        having
            count(student_id)=(
                SELECT
                    count(sid)   # 學生的總數
                from 
                    student
            )
    );
# 14、查詢每門課程被選修的次數
select
    course_id, count(student_id) as student_num
from
    score
group by
    course_id;
+-----------+-------------+
| course_id | student_num |
+-----------+-------------+
|         1 |          12 |
|         2 |          11 |
|         3 |          12 |
|         4 |          12 |
+-----------+-------------+


# 15、查詢只選修了一門課程的學生姓名和學號
-- score——》student
select
    sid,sname
from
    student
where sid in (
    select
        student_id
    from
        score
    group by
        student_id
    having count(student_id)=1
);
+-----+--------+
| sid | sname  |
+-----+--------+
|  13 | 劉三   |
+-----+--------+

# 16、查詢全部學生考出的成績並按從高到低排序(成績去重)
SELECT DISTINCT
    num
FROM
    score
ORDER BY
    num DESC;


# 17、查詢平均成績大於85的學生姓名和平均成績
select
    student.sname, t1.avg_num   # 必需要起別名才能打印出來
from
    student
inner join(
    select 
        student_id, avg(num) as avg_num
    from
        score
    group by
        student_id
    having avg_num>85) as t1
on student.sid=t1.student_id;
+--------+---------+
| sname  | avg_num |
+--------+---------+
| 劉三   | 87.0000 |
+--------+---------+


# 18、查詢生物成績不及格的學生姓名和對應生物分數
select
    sname,num
from
    student
inner join (
    select
        student_id, num
    from
        score
    where course_id in (
        select
            cid   # 生物課程id
        from
            course
        where 
            cname='生物' and num<60
    )
) as t1
on student.sid=t1.student_id;
+--------+-----+
| sname  | num |
+--------+-----+
| 理解   |  10 |
| 鋼蛋   |   8 |
| 張四   |   9 |
| 鐵錘   |   9 |
| 李三   |   9 |
+--------+-----+

# 19、查詢在全部選修了李平老師課程的學生中,這些課程(李平老師的課程,不是全部課程)平均成績最高的學生姓名
-- teacher——》course——》score找到選修李平老師的學生id——》student找到學生的姓名
select
    sname
from 
    student
where sid=(         # 不支持Limit在子查詢裏
    select
        student_id
    from
        score
    where
        course_id in (
            select
                cid             # 拿到了課程id
            from
                course
            where
                teacher_id =(
                    select 
                        tid 
                    from
                        teacher
                    where
                        tname='李平老師'
                )
        )
    group by
        student_id
    order by 
        avg(num) desc
    limit 1
);

# 20、查詢每門課程成績最好的前兩名學生姓名
-- score+course——》分組選出前兩名學生sid——》student的sname
#查看每門課程按照分數排序的信息,爲下列查找正確與否提供依據
SELECT
    *
FROM
    score
ORDER BY
    course_id,
    num DESC;

# 表1:求出每門課程的課程course_id,與最高分數first_num
SELECT
    course_id,
    max(num) first_num
FROM
    score
GROUP BY
    course_id;

# 表2:去掉最高分,再按照課程分組,取得的最高分,就是第二高的分數second_num
SELECT
    score.course_id,
    max(num) second_num
FROM
    score
INNER JOIN (
    SELECT
        course_id,
        max(num) first_num
    FROM
        score
    GROUP BY
        course_id
) AS t ON score.course_id = t.course_id
WHERE
    score.num < t.first_num
GROUP BY
    course_id;

# 將表1和表2聯合到一塊兒,獲得一張表t3,包含課程course_id與該們課程的first_num與second_num
SELECT
    t1.course_id,
    t1.first_num,
    t2.second_num
FROM
    (
        SELECT
            course_id,
            max(num) first_num
        FROM
            score
        GROUP BY
            course_id
    ) AS t1
INNER JOIN (
    SELECT
        score.course_id,
        max(num) second_num
    FROM
        score
    INNER JOIN (
        SELECT
            course_id,
            max(num) first_num
        FROM
            score
        GROUP BY
            course_id
    ) AS t ON score.course_id = t.course_id
    WHERE
        score.num < t.first_num
    GROUP BY
        course_id
) AS t2 ON t1.course_id = t2.course_id;

#查詢前兩名的學生(有可能出現並列第一或者並列第二的狀況)
SELECT
    score.student_id,
    t3.course_id,
    t3.first_num,
    t3.second_num
FROM
    score
INNER JOIN (
    SELECT
        t1.course_id,
        t1.first_num,
        t2.second_num
    FROM
        (
            SELECT
                course_id,
                max(num) first_num
            FROM
                score
            GROUP BY
                course_id
        ) AS t1
    INNER JOIN (
        SELECT
            score.course_id,
            max(num) second_num
        FROM
            score
        INNER JOIN (
            SELECT
                course_id,
                max(num) first_num
            FROM
                score
            GROUP BY
                course_id
        ) AS t ON score.course_id = t.course_id
        WHERE
            score.num < t.first_num
        GROUP BY
            course_id
    ) AS t2 ON t1.course_id = t2.course_id
) AS t3 ON score.course_id = t3.course_id
WHERE
    score.num >= t3.second_num
AND score.num <= t3.first_num;

# 排序後能夠看的明顯點
SELECT
    score.student_id,
    t3.course_id,
    t3.first_num,
    t3.second_num
FROM
    score
INNER JOIN (
    SELECT
        t1.course_id,
        t1.first_num,
        t2.second_num
    FROM
        (
            SELECT
                course_id,
                max(num) first_num
            FROM
                score
            GROUP BY
                course_id
        ) AS t1
    INNER JOIN (
        SELECT
            score.course_id,
            max(num) second_num
        FROM
            score
        INNER JOIN (
            SELECT
                course_id,
                max(num) first_num
            FROM
                score
            GROUP BY
                course_id
        ) AS t ON score.course_id = t.course_id
        WHERE
            score.num < t.first_num
        GROUP BY
            course_id
    ) AS t2 ON t1.course_id = t2.course_id
) AS t3 ON score.course_id = t3.course_id
WHERE
    score.num >= t3.second_num
AND score.num <= t3.first_num
ORDER BY
    course_id;
+------------+-----------+-----------+------------+
| student_id | course_id | first_num | second_num |
+------------+-----------+-----------+------------+
|          9 |         1 |        91 |         90 |
|         10 |         1 |        91 |         90 |
|         11 |         1 |        91 |         90 |
|         12 |         1 |        91 |         90 |
|          9 |         2 |       100 |         88 |
|          6 |         2 |       100 |         88 |
|          7 |         2 |       100 |         88 |
|          8 |         2 |       100 |         88 |
|          2 |         3 |        87 |         68 |
|          3 |         3 |        87 |         68 |
|         13 |         3 |        87 |         68 |
|          2 |         4 |       100 |         99 |
|          3 |         4 |       100 |         99 |
|          4 |         4 |       100 |         99 |
|          5 |         4 |       100 |         99 |
|          6 |         4 |       100 |         99 |
+------------+-----------+-----------+------------+
#能夠用如下命令驗證上述查詢的正確性
SELECT
    *
FROM
    score
ORDER BY
    course_id,
    num DESC;


# 21、查詢不一樣課程但成績相同的學號,課程號,成績
select 
    DISTINCT s1.course_id,s2.course_id,s1.num,s2.num 
from 
    score as s1, 
    score as s2 
where s1.num = s2.num and s1.course_id != s2.course_id;
+-----------+-----------+-----+-----+
| course_id | course_id | num | num |
+-----------+-----------+-----+-----+
|         1 |         2 |   9 |   9 |
|         2 |         4 |  66 |  66 |
|         2 |         1 |  77 |  77 |
|         4 |         2 |  66 |  66 |
|         4 |         3 |  87 |  87 |
|         2 |         4 | 100 | 100 |
|         2 |         1 |   9 |   9 |
|         4 |         2 | 100 | 100 |
|         2 |         4 |  88 |  88 |
|         4 |         2 |  88 |  88 |
|         1 |         2 |  77 |  77 |
|         3 |         4 |  87 |  87 |
+-----------+-----------+-----+-----+

# 22、查詢沒學過「李平」老師課程的學生姓名以及選修的課程名稱;
select student_id,student.sname from score
    left join student on score.student_id = student.sid
    where score.course_id not in (
        select cid from course left join teacher on course.teacher_id = teacher.tid where tname = '張磊老師'
    )
    group by student_id;
+------------+--------+
| student_id | sname  |
+------------+--------+
|          1 | 理解   |
|          2 | 鋼蛋   |
|          3 | 張三   |
|          4 | 張一   |
|          5 | 張二   |
|          6 | 張四   |
|          7 | 鐵錘   |
|          8 | 李三   |
|          9 | 李一   |
|         10 | 李二   |
|         11 | 李四   |
|         12 | 如花   |
|         13 | 劉三   |
+------------+--------+

# 23、查詢全部選修了學號爲1的同窗選修過的一門或者多門課程的同窗學號和姓名;

# 24、任課最多的老師中學生單科成績最高的學生姓名
練習題和答案
相關文章
相關標籤/搜索