【一周入门MySQL—3】多表查询、子查询、常用函数
多表查詢、子查詢、常用函數
一、多表查詢
多表查詢:通過不同表中具有相同意義的關鍵字段,將多個表進行連接,查詢不同表中的字段信息。
對應關系
一對一:比如下圖的人員信息表和人員身份證對應表,一個員工只會有一個身份證號碼;
一對多:比如下圖的部門信息表和部門人員表,一個部門可能會有多個員工存在;
多對多:多對多的情況就比較復雜了,建議拆分表,這樣可以節省存儲空間,避免數據冗余;
連接方式
內連接和外連接(左外連接和右外連接)。
多表連接的結果通過三個屬性決定
比如上圖中的t1表和t2表:
若左外連接:t1是主表,t2是附表;
若右外連接:t2是主表,t1是附表;
左連接:
結果中除了包括滿足連接條件的行外,還包括左表的所有行。
select 字段1[,...] from表1 left join 表2 on 表1.key = 表2.key;
右連接:
結果中除了包括滿足的連接條件的行外,還包括右邊的所有行。
select 字段1[,...] from表1 right join 表2 on 表1.key = 表2.key;
內連接:
按照連接條件合并兩個表,返回滿足條件的行。
沒有主附關系,也沒有方向性。
select 字段1[,...] from表1 [inner] join 表2 on 表1.key = 表2.key;
另外在其他工具中還有全連接、左反和右反連接(Power BI)。
-- 內連接select * from t1 inner join t2 on t1.key1 = t2.key2;-- 左連接select * from t1 left join t2 on t1.key1 = t2.key2;-- 右連接select * from t1 right join t2 on t1.key1 = t2.key2;縱向合并(聯合查詢):
把多條select語句查詢的結果合并為一個結果集,即追加 / 增加記錄。
指數據集的縱向合并,從數據集被合并到主數據集中。
注意 :
兩張表必須擁有相同數量的字段;
兩張表字段的順序必須相同;
兩張表獨贏的字段的數據類型必須一致;
字段名可以不相同,選取主數據集中的字段名(第一個表)。
union 去重:select 字段1[,字段2,...] from 表名 union select 字段1[,字段2,...] from 表名;
union all不去重:select 字段1[,字段2,...] from 表名 union all select 字段1[,字段2,...] from 表名;
-- 合并查詢select * from t1union allselect * from t2;-- union去重select * from t1unionselect * from t2;【測試題】
表a userid
表b userid
查詢出現在a表,不在b表的userid。
思路:左反連接和右反連接
select *from table_aleft join table_bon table_a.userid = table_b.useridwhere table_b.userid is null;order表:userid,endtime
求每個userid的最新結束時間
select userid,max(endtime)from ordergroup by uderid;order表:userid,endtime
user表:userid,tel
找出用戶結束時間在3月份的userid的tel
select order.userid, user.telfrom user right join order on user.userid = order.useridwhere month(endtime) = 3;select order.userid, user.telfrom user right join order on user.userid = order.useridwhere endtime between ‘2021-03-01’ and ‘2021-03-31’;?在一張表中查詢出員工姓名和對應的領導姓名
-- 自連接:通過設置別名實現,將同一張表視為兩張表select t1.ename 員工姓名,t2.ename 領導姓名from emp t1left join emp t2 on t1.mgr = t2.empno;查詢入職日期早于其直屬領導的員工信息:empno、ename、dname(部門表)
select t1.empno 員工編號,t1.ename 員工姓名,t3.dname 部門名稱from emp t1left join emp t2 on t1.mgr = t2.empnoinner join dept t3 on t1.deptno = t3.deptnowhere t1.hiredate < t2.hiredate;二、子查詢
子查詢:在一個select語句中包含另一個或者多個完整的select語句。
子查詢出現的位置
出現在where子句中:將子查詢返回的結果作為主查詢的條件;
出現在from子句中:將子查詢返回的結果作為主查詢的一個表;
子查詢的分類
- 標量子查詢:返回的結果是一個數據(單行單列);
- 行子查詢:返回的結果是一行(單行多列);
- 列子查詢:返回的結果是一列(多行單列);
- 表子查詢:返回的結果是一張臨時表(多行多列);
子查詢的操作符:
- [NOT] IN:在【不在】其中
- ANY:其中任何一個
- ALL:全部(每個)
-- 子查詢:標量子查詢
-- 查詢基本工資高于公司平均工資的員工信息(where子句中不能直接使用聚合函數)select * from empwhere sal > (select avg(sal) from emp);-- 查詢與“張曉明”同一個領導的員工信息:empno、ename、job、mgrselect empno,ename,job,mgrfrom empwhere mgr = (select mgr from emp where ename = '張曉明')and ename <> '張曉明';-- 子查詢:行子查詢
-- 查詢和“許飛龍”同部門同職位的員工信息:empno,ename,job,deptnoselect empno,ename,job,deptnofrom empwhere (deptno,job) = (select deptno,job from emp where ename = '許飛龍')and ename <> '許飛龍';-- 子查詢:列子查詢
-- 查詢普通員工的工資等級:empno,ename,sal,gradeselect empno 員工號,ename 員工姓名,sal 基本工資,grade 工資等級from empleft join salgrade on sal between losal and hisalwhere empno not in (select distinct mgr from emp where mgr is not null);-- 查詢員工數不少于3個人的部門所有員工信息:empno,ename,deptno-- 思路:先查找出大于等于3人的部門編號select empno,ename,deptnofrom empwhere deptno in (select deptno from emp group by deptno having count(*) >= 3);-- 查詢基本工資高于51部門任意員工的員工信息select *from empwhere sal > any (select sal from emp where deptno = 51)and deptno <> 51;select *from empwhere sal > (select min(sal) from emp where deptno = 51)and deptno <> 51;-- 查詢基本工資高于51部門所有員工的員工信息select *from empwhere sal > all (select sal from emp where deptno = 51)and deptno <> 51;-- 子查詢:from子查詢
-- 查詢各個部門最高工資的員工:empno,ename,sal,deptno-- 表子查詢必須設置別名select empno,ename,sal,emp.deptnofrom empleft join (select deptno,max(sal) as 最高工資 from emp group by deptno) as ton emp.deptno = t.deptnowhere sal = 最高工資;三、函數
-- 字符串函數
select concat('My','Name','Is','Jack'); #MyNameIsJackselect concat('My','Name','Is',null); #nullselect instr('ABCDE','C'); #3select left('ABCDE',4); #ABCDselect right('ABCDE',4); #BCDEselect mid('ABCDEFG', 3, 4); #CDEFselect mid('ABCDEFG', 3); #CDEFGselect substring('ABCDEFG', 3, 4); #CDEFselect substring('ABCDEFG', 3); #CDEFGselect ltrim('?? ABC'); #ABCselect rtrim('ABC?? '); #ABCselect trim('?? ABC?? '); #ABCselect replace('ABCdeF','de','DE'); #ABCDEFselect repeat('Shit',3); #ShitShitShitselect reverse('ABCDE'); #EDCBAselect upper('abcde'); #ABCDEselect lower('ABCDE');? #abcde-- 將員工表中姓名首字母大寫,其他字母小寫顯示select concat(upper(left(ename,1)),lower(mid(ename,2))) from emp;數學函數
例如:
abs() 絕對值
floor() 向下取整 – 地板
ceiling() 向上取整 – 天花板
round() 四舍五入,第二個參數為保留小數位數
rand() 返回一個0-1之間的隨機小數
select rand(1); 輸入的隨機種子一樣,得到的結果一樣。
日期時間函數
date() 返回指定日期時間表達式的日期或者將文本字符串格式日期轉換成標準的日期格式
select date('20200101'); #2020-01-01select date('2020-01-01 11:11:11'); #2020-01-01select week('2022-01-01'); #0select month('2020-01-01 11:11:11'); #1select quarter('2020-12-01 11:11:11'); #4select year('2020-12-01 11:11:11'); #2020select year('20-12-01'); #2020select date_add('2022-01-01',interval 1 day); #2022-01-02select date_add('2022-01-01',interval 1 year); #2023-01-01select adddate('2022-01-01',interval 1 month); #2022-02-01select date_sub('2022-01-01',interval 1 day); #2021-12-31select date_sub('2022-01-01',interval 1 year); #2021-01-01select subdate('2022-01-01',interval 1 month); #2021-12-01select date_format('2022-01-06 15:05:20','%Y-%m-%d'); #2022-01-06select date_format('2022-01-06 15:05:20','%Y-%m'); #2022-01select date_format('2022-01-06 15:05:20','%m'); #01select curdate(); #無參函數,當前電腦系統日期select curtime(); #無參函數,當前電腦系統時間select now();???? #無參函數,當前電腦系統日期時間select datediff('20220106','20211228'); #9 日期間隔天數-- 計算員工表中每個員工的工齡use test;select ename 姓名,hiredate 入職日期,round(datediff(curdate(),hiredate)/365) 工齡from emp;select unix_timestamp(); #當前日期從1970-01-01 00:00:00開始到現在過了多少秒select unix_timestamp('2022-01-06'); #1641398400select from_unixtime(1641473220); #2022-01-06 20:47:00-- 查詢每個員工的試用截止日期(試用期三個月):ename,hiredate,試用截止日期select ename,hiredate,adddate(hiredate,interval 3 month)? 試用截止日期from emp;分組合并函數 group_concat()
對文本字符串進行合并,跟group by結合使用,返回一個字符串結果。
平時我們group by之后只能對數值型進行聚合,不能對字符串數據聚合。
忽略空置null
-- 查詢每個部門有哪些員工select deptno,group_concat(ename)from empgroup by deptno;-- 查詢每個部門有哪些員工(去重)select deptno,group_concat(distinct ename)from empgroup by deptno;-- 查詢每個部門有哪些員工(排序)select deptno,group_concat(distinct ename order by sal desc)from empgroup by deptno;-- 查詢每個部門有哪些員工(指定分隔符/)select deptno,group_concat(distinct ename order by sal desc separator '/')from empgroup by deptno;邏輯函數
ifnull()
-- 計算每位員工的實發工資:基本工資 + 提成-- 思路:沒有提成的null 需要用0代替select ename,sal,ifnull(comm,0)+sal 實發工資from emp;if()
-- 判斷每位員工的工資級別select ename,sal,if(sal>=15000,'高',if(sal<=9000,'低','中')) 工資級別from emp;case when end
-- 判斷每位員工的工資級別select ename,sal,case when sal>=15000 then'高'when sal<=9000 then'低'else '中'end 工資級別from emp;開窗函數
MySQL 8.0才支持。
開窗函數是在滿足某種條件的記錄集合上執行的特殊函數。
靜態窗口 滑動窗口
本質還是聚合運算,但是使用靈活,在每一個記錄行上來執行并返回計算結果。
語法:
開窗函數名稱([<字段名>]) over([partition by <分組字段>] [order by<排序字段>[desc]] [<細分窗口>])
對于滑動窗口的范圍指定,通常使用between frame_start and frame_end語法來表示行范圍,rame_start 和frame_end可以支持如下關鍵字,來確定不同的動態行記錄:
- current row 邊界是當前行,一般和其他范圍關鍵字一起使用;
- unbounded preceding 邊界是分區中的第一行;
- unbounded following 邊界是分區中的最后一行;
- expr preceding 邊界是當前行減去expr的值;
- expr following 邊界是當前行加上expr的值;
比如,下面都是合法的范圍:
rows between 1 preceding and 1 following 窗口范圍是當前行、前一行、后一行一共三行記錄;
rows unbounded preceding 窗口范圍是當前行到分區的最后一行;
rows between unbounded preceding and unbounded following 窗口范圍是當前分區中的所有行,等同于不寫;
序號函數
- row_number():顯示分區中不重復不間斷的序號;
- dense_rank():顯示分區中重復不間斷的序號;
- rank():顯示分區中重復間斷的序號;
【練習題】
計算2017年每筆投資均大于50萬元的用戶
select user_idfrom cmn_investment_requestwhere year(created_at)=2017group by user_idhaving min(invest_amount)>500000;計算2017年僅投資過CFH和AX產品的用戶
select user_id,group_concat(distinct invest_item order by invest_item desc)from cmn_investment_requestwhere year(created_at)=2017group by user_idhaving group_concat(distinct invest_item order by invest_item desc)=’CFH,AX’;計算歸屬于10002業務員的投資金額
select sum(invest_amount)from dim_agentleft join cmn_investment_requeston cmn_investment_request.user_id=dim_agent.user_idand created_at between start_date and end_datewhere agent_id=’10002’; 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的【一周入门MySQL—3】多表查询、子查询、常用函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【一周入门MySQL—2】单表查询
- 下一篇: 【一周入门MySQL—4】数据库进阶练习