【一周入门MySQL—3】多表查询、子查询、常用函数
多表查詢、子查詢、常用函數(shù)
一、多表查詢
多表查詢:通過不同表中具有相同意義的關(guān)鍵字段,將多個表進(jìn)行連接,查詢不同表中的字段信息。
對應(yīng)關(guān)系
一對一:比如下圖的人員信息表和人員身份證對應(yīng)表,一個員工只會有一個身份證號碼;
一對多:比如下圖的部門信息表和部門人員表,一個部門可能會有多個員工存在;
多對多:多對多的情況就比較復(fù)雜了,建議拆分表,這樣可以節(jié)省存儲空間,避免數(shù)據(jù)冗余;
連接方式
內(nèi)連接和外連接(左外連接和右外連接)。
多表連接的結(jié)果通過三個屬性決定
比如上圖中的t1表和t2表:
若左外連接:t1是主表,t2是附表;
若右外連接:t2是主表,t1是附表;
左連接:
結(jié)果中除了包括滿足連接條件的行外,還包括左表的所有行。
select 字段1[,...] from表1 left join 表2 on 表1.key = 表2.key;
右連接:
結(jié)果中除了包括滿足的連接條件的行外,還包括右邊的所有行。
select 字段1[,...] from表1 right join 表2 on 表1.key = 表2.key;
內(nèi)連接:
按照連接條件合并兩個表,返回滿足條件的行。
沒有主附關(guān)系,也沒有方向性。
select 字段1[,...] from表1 [inner] join 表2 on 表1.key = 表2.key;
另外在其他工具中還有全連接、左反和右反連接(Power BI)。
-- 內(nèi)連接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;縱向合并(聯(lián)合查詢):
把多條select語句查詢的結(jié)果合并為一個結(jié)果集,即追加 / 增加記錄。
指數(shù)據(jù)集的縱向合并,從數(shù)據(jù)集被合并到主數(shù)據(jù)集中。
注意 :
兩張表必須擁有相同數(shù)量的字段;
兩張表字段的順序必須相同;
兩張表獨贏的字段的數(shù)據(jù)類型必須一致;
字段名可以不相同,選取主數(shù)據(jù)集中的字段名(第一個表)。
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
查詢出現(xiàn)在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的最新結(jié)束時間
select userid,max(endtime)from ordergroup by uderid;order表:userid,endtime
user表:userid,tel
找出用戶結(jié)束時間在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’;?在一張表中查詢出員工姓名和對應(yīng)的領(lǐng)導(dǎo)姓名
-- 自連接:通過設(shè)置別名實現(xiàn),將同一張表視為兩張表select t1.ename 員工姓名,t2.ename 領(lǐng)導(dǎo)姓名from emp t1left join emp t2 on t1.mgr = t2.empno;查詢?nèi)肼毴掌谠缬谄渲睂兕I(lǐng)導(dǎo)的員工信息: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語句。
子查詢出現(xiàn)的位置
出現(xiàn)在where子句中:將子查詢返回的結(jié)果作為主查詢的條件;
出現(xiàn)在from子句中:將子查詢返回的結(jié)果作為主查詢的一個表;
子查詢的分類
- 標(biāo)量子查詢:返回的結(jié)果是一個數(shù)據(jù)(單行單列);
- 行子查詢:返回的結(jié)果是一行(單行多列);
- 列子查詢:返回的結(jié)果是一列(多行單列);
- 表子查詢:返回的結(jié)果是一張臨時表(多行多列);
子查詢的操作符:
- [NOT] IN:在【不在】其中
- ANY:其中任何一個
- ALL:全部(每個)
-- 子查詢:標(biāo)量子查詢
-- 查詢基本工資高于公司平均工資的員工信息(where子句中不能直接使用聚合函數(shù))select * from empwhere sal > (select avg(sal) from emp);-- 查詢與“張曉明”同一個領(lǐng)導(dǎo)的員工信息: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);-- 查詢員工數(shù)不少于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-- 表子查詢必須設(shè)置別名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 = 最高工資;三、函數(shù)
-- 字符串函數(shù)
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;數(shù)學(xué)函數(shù)
例如:
abs() 絕對值
floor() 向下取整 – 地板
ceiling() 向上取整 – 天花板
round() 四舍五入,第二個參數(shù)為保留小數(shù)位數(shù)
rand() 返回一個0-1之間的隨機(jī)小數(shù)
select rand(1); 輸入的隨機(jī)種子一樣,得到的結(jié)果一樣。
日期時間函數(shù)
date() 返回指定日期時間表達(dá)式的日期或者將文本字符串格式日期轉(zhuǎn)換成標(biāo)準(zhǔn)的日期格式
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(); #無參函數(shù),當(dāng)前電腦系統(tǒng)日期select curtime(); #無參函數(shù),當(dāng)前電腦系統(tǒng)時間select now();???? #無參函數(shù),當(dāng)前電腦系統(tǒng)日期時間select datediff('20220106','20211228'); #9 日期間隔天數(shù)-- 計算員工表中每個員工的工齡use test;select ename 姓名,hiredate 入職日期,round(datediff(curdate(),hiredate)/365) 工齡from emp;select unix_timestamp(); #當(dāng)前日期從1970-01-01 00:00:00開始到現(xiàn)在過了多少秒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;分組合并函數(shù) group_concat()
對文本字符串進(jìn)行合并,跟group by結(jié)合使用,返回一個字符串結(jié)果。
平時我們group by之后只能對數(shù)值型進(jìn)行聚合,不能對字符串?dāng)?shù)據(jù)聚合。
忽略空置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;邏輯函數(shù)
ifnull()
-- 計算每位員工的實發(fā)工資:基本工資 + 提成-- 思路:沒有提成的null 需要用0代替select ename,sal,ifnull(comm,0)+sal 實發(fā)工資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;開窗函數(shù)
MySQL 8.0才支持。
開窗函數(shù)是在滿足某種條件的記錄集合上執(zhí)行的特殊函數(shù)。
靜態(tài)窗口 滑動窗口
本質(zhì)還是聚合運算,但是使用靈活,在每一個記錄行上來執(zhí)行并返回計算結(jié)果。
語法:
開窗函數(shù)名稱([<字段名>]) over([partition by <分組字段>] [order by<排序字段>[desc]] [<細(xì)分窗口>])
對于滑動窗口的范圍指定,通常使用between frame_start and frame_end語法來表示行范圍,rame_start 和frame_end可以支持如下關(guān)鍵字,來確定不同的動態(tài)行記錄:
- current row 邊界是當(dāng)前行,一般和其他范圍關(guān)鍵字一起使用;
- unbounded preceding 邊界是分區(qū)中的第一行;
- unbounded following 邊界是分區(qū)中的最后一行;
- expr preceding 邊界是當(dāng)前行減去expr的值;
- expr following 邊界是當(dāng)前行加上expr的值;
比如,下面都是合法的范圍:
rows between 1 preceding and 1 following 窗口范圍是當(dāng)前行、前一行、后一行一共三行記錄;
rows unbounded preceding 窗口范圍是當(dāng)前行到分區(qū)的最后一行;
rows between unbounded preceding and unbounded following 窗口范圍是當(dāng)前分區(qū)中的所有行,等同于不寫;
序號函數(shù)
- row_number():顯示分區(qū)中不重復(fù)不間斷的序號;
- dense_rank():顯示分區(qū)中重復(fù)不間斷的序號;
- rank():顯示分區(qū)中重復(fù)間斷的序號;
【練習(xí)題】
計算2017年每筆投資均大于50萬元的用戶
select user_idfrom cmn_investment_requestwhere year(created_at)=2017group by user_idhaving min(invest_amount)>500000;計算2017年僅投資過CFH和AX產(chǎn)品的用戶
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業(yè)務(wù)員的投資金額
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位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的【一周入门MySQL—3】多表查询、子查询、常用函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【一周入门MySQL—2】单表查询
- 下一篇: 【一周入门MySQL—4】数据库进阶练习