數據庫(九):多表查詢②


進擊のpython

*****

數據庫——多表查詢


那接着上一節說,其實在真正的操作中,不光是要把數據聯合,還需要進行篩選數據,比如:

打印員工平均年齡大於三十的部門

拿到一個需求教你怎么寫!

需要員工員工年齡和部門

員工年齡存放在employee中,部門存放在deparement中

應該是什么連接呢?根據需求應該是內連接~(這個,你自己理解一下就可以了嘛)

select * from employee inner join department on employee.dep_id=department.id;
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)

拿到一個虛擬表,怎么叫虛擬表呢?因為這個表不是硬盤尚存的,而是臨時拼在內存里的

但!他是一張表!

根據題意應該是部門之間的員工的年齡進行比較,所以就應該分組,按照什么分組呢???

如果按照id(dep_id),那我select后面拿到的只能是id(dep_id)【參考分組時說的知識點】

所以就應該用部門的名字name字段,但是發現了嗎?有兩個name字段,所以應該強制指定:

select department.name from employee inner join department on employee.dep_id=department.id
group by department.name;

接着我們要員工年齡的平均值大於三十,是不是應該用having過濾一下:

select department.name from employee inner join department on employee.dep_id=department.id
group by department.name
having avg(age)>30;

為了清晰一點,加個字符串拼接:

select concat('平均年齡超過三十的部門:',department.name) from employee inner join department on employee.dep_id=department.id
group by department.name
having avg(age)>30;
mysql> select concat('平均年齡超過三十的部門:',department.name) from employee inner join department on employee.dep_id=department.id
    -> group by department.name
    -> having avg(age)>30;
+----------------------------------------------------------------+
| concat('平均年齡超過三十的部門:',department.name)             |
+----------------------------------------------------------------+
| 平均年齡超過三十的部門:人力資源                               |
+----------------------------------------------------------------+
1 row in set (2.76 sec)

表頭不好看,重命名一下:

select concat('平均年齡超過三十的部門:',department.name) info from employee inner join department on employee.dep_id=department.id
group by department.name
having avg(age)>30;
mysql> select concat('平均年齡超過三十的部門:',department.name) info from employee inner join department on employee.dep_id=department.id
    -> group by department.name
    -> having avg(age)>30;
+--------------------------------------------------+
| info                                             |
+--------------------------------------------------+
| 平均年齡超過三十的部門:人力資源                 |
+--------------------------------------------------+
1 row in set (0.00 sec)

那這個需求就做出來了

而至此!select的終極版語法才算是真正的水落石出:

定義順序:

SELECT DISTINCT <select_list>
FROM <left_table>
<join_type> JOIN <right_table>
ON <join_condition>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
ORDER BY <order_by_condition>
LIMIT <limit_number>

執行順序:

(7)     SELECT 
(8)     DISTINCT <select_list>
(1)     FROM <left_table>
(3)     <join_type> JOIN <right_table>
(2)     ON <join_condition>
(4)     WHERE <where_condition>
(5)     GROUP BY <group_by_list>
(6)     HAVING <having_condition>
(9)     ORDER BY <order_by_condition>
(10)    LIMIT <limit_number>

子查詢

除了這種查詢方法,還有子查詢,子查詢就是把查詢的結果,當做另一個表的查詢條件

那剛才的例子,也可以這么搞,我先拿到部門平均年齡過30的id,然后再去employee去找對應的部門名

帶in的子查詢

先拿到超過30的id:

select dep_id from employee inner join department on employee.dep_id=department.id
group by dep_id
having avg(age)>30;
mysql> select dep_id from employee inner join department on employee.dep_id=department.id
    -> group by dep_id
    -> having avg(age)>30;
+--------+
| dep_id |
+--------+
|    201 |
+--------+
1 row in set (0.00 sec)

拿到了之后進行傳值:

select name from department where id in (

select dep_id from employee inner join department on employee.dep_id=department.id 

group by dep_id 

having avg(age)>30);
mysql> select name from department where id in (
    ->
    -> select dep_id from employee inner join department on employee.dep_id=department.id
    ->
    -> group by dep_id
    ->
    -> having avg(age)>30);
+--------------+
| name         |
+--------------+
| 人力資源     |
+--------------+
1 row in set (0.00 sec)

是不是也達到了需求~

查詢技術部員工的姓名怎么寫?

select name from employee where dep_id in (
select dep_id from employee inner join department on employee.dep_id=department.id
where department.name = "技術"
group by dep_id);
mysql> select name from employee where dep_id in (
    -> select dep_id from employee inner join department on employee.dep_id=department.id
    -> where department.name = "技術"
    -> group by dep_id);
+-----------+
| name      |
+-----------+
| egon      |
| liwenzhou |
+-----------+
2 rows in set (0.00 sec)

查看不足1人的部門名這個怎么做?

這個其實就用到了數學的思想~總數里去掉一人及以上的,是不是就是不足一人的了

而只要是在用戶表里面的用戶擁有dep_id的,是不是都是有部門的,去掉這一部分是不是就滿足了題意

select name from department where id not in (select distinct dep_id from employee);

(子查詢得到的是有人的部門id)

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

帶比較運算符的子查詢

提一句吧,既然拿到的是數據,就像1 12 3 一樣的,你就把它看作個數字,當然是可以比較的

查詢大於所有人平均年齡的員工名與年齡

select avg(age) from employee;查詢所有員工的年齡 # 28.000

那在你心里,此時這一串亂七八糟的東西就是28.000

此時題意就變成,所有年齡大於28.000的員工名與年齡

select name,age from employee where age > 28.000;然后將28.000進行“等量代換”

select name,age from employee where age > (select avg(age) from employee);

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)

查詢大於部門內平均年齡的員工名、年齡

部門內,一看就是分組,而且應該是以部門進行分組

select employee.name,employee.age from employee
inner join 
(select dep_id,avg(age) avg_age from employee group by dep_id) t2
on employee.dep_id = t2.dep_id
where employee.age > t2.avg_age; 
mysql> select employee.name,employee.age from employee
    -> inner join
    -> (select dep_id,avg(age) avg_age from employee group by dep_id) t2
    -> on employee.dep_id = t2.dep_id
    -> where employee.age > t2.avg_age;
+------+------+
| name | age  |
+------+------+
| alex |   48 |
+------+------+
1 row in set (0.34 sec)

但是這個employee太長了,所以,給他重新命名比較好(知道為什么是t2了吧)

select employee.name,employee.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; 

帶exists關鍵字的子查詢

理解成if

select * from employee where exists (select id from department where id=200);

括號里是不是不為空,那就是真,就相當於if True:於是就有打印結果
select * from employee where exists (select id from department where id=204);
括號里是不是為空,那就是假,就相當於if False:於是沒有打印結果


接下來借着這個例子給你講講一種思想:我連我自己

每個職位最新入職的員工

自己創建表:

create table employee(
id int not null unique auto_increment,
name varchar(20) not null,
sex enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int, #一個部門一個屋子
depart_id int
);

插入數據:

insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values
('egon','male',18,'20170301','駐沙河辦事處外交大使',7300.33,401,1), #以下是教學部
('alex','male',78,'20150302','teacher',1000000.31,401,1),
('wupeiqi','male',81,'20130305','teacher',8300,401,1),
('yuanhao','male',73,'20140701','teacher',3500,401,1),
('liwenzhou','male',28,'20121101','teacher',2100,401,1),
('jingliyang','female',18,'20110211','teacher',9000,401,1),
('jinxin','male',18,'19000301','teacher',30000,401,1),
('成龍','male',48,'20101111','teacher',10000,401,1),

('歪歪','female',48,'20150311','sale',3000.13,402,2),#以下是銷售部門
('丫丫','female',38,'20101101','sale',2000.35,402,2),
('丁丁','female',18,'20110312','sale',1000.37,402,2),
('星星','female',18,'20160513','sale',3000.29,402,2),
('格格','female',28,'20170127','sale',4000.33,402,2),

('張野','male',28,'20160311','operation',10000.13,403,3), #以下是運營部門
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬銀','female',18,'20130311','operation',19000,403,3),
('程咬銅','male',18,'20150411','operation',18000,403,3),
('程咬鐵','female',18,'20140512','operation',17000,403,3)
;

select post,max(hire_date) from employee group by post;

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     |
+--------------------------------+----------------+

這是不是每個部門最新入職的員工,但是,你雖然看到了這張表,但事實上是虛擬的,是不存在的

所以我們為了讓它存在的“合法化”,我們應該將其整體重新命名:

(select post,max(hire_date) from employee group by post) as t2;

我們要的是那名員工!

我們新建的表是不是有職位和日期!employee是不是也有部門和日期,這是不是就聯系上了?

那我們就可以通過這個聯系來找到這些員工

select * from employee  as t1 inner join

(select post,max(hire_date) hire_date from employee group by post) as t2 on

t1.post = t2.post and t1.hire_date=t2.hire_date;
mysql> select * from employee  as t1 inner join
    ->
    -> (select post,max(hire_date) hire_date from employee group by post) as t2 on
    ->
    -> t1.post = t2.post and t1.hire_date=t2.hire_date;
+----+--------+--------+-----+------------+--------------------------------+--------------+------------+--------+-----------+--------------------------------+------------+
| id | name   | sex    | age | hire_date  | post                           | post_comment | salary     | office | depart_id | post                           | 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)

當然也可以用where來做,你自己嘗試做一下

select * from employee  as t1 inner join
(select post,max(hire_date) hire_date from employee group by post) as t2 on
t1.post = t2.post
where t1.hire_date=t2.hire_date;

*****
*****


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM