進擊の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;
