SQL进阶,子查询与窗口函数
本節給大家講解SQL在實際過程中用途比較多的子查詢與窗口函數,下面一起學習。
示例工具:MySQL8.0、Navicat Premium 12
本文講解內容:子查詢與窗口函數
適用范圍:SQL進階應用
子查詢
子查詢用于為主查詢返回其所需數據,或者對檢索數據進行進一步的限制,通常將一個查詢(子查詢)的結果作為另一個查詢(主查詢)的數據來源或判斷條件,常見的子查詢有WHERE子查詢,HAVING子查詢,FROM子查詢,SELECT子查詢,EXISTS子查詢。
子查詢是一種嵌套在其他 SQL 查詢的 WHERE 子句中的查詢,可以在 SELECT、INSERT、UPDATE 和 DELETE 語句中,同邏輯運算符一起使用。
使用子查詢必須遵循以下幾個規則:
- 子查詢必須括在圓括號中。 
- 子查詢的 SELECT 子句中只能有一個列。 
- 子查詢不能使用 ORDER BY,在子查詢中,GROUP BY 可以起到同 ORDER BY 相同作用。 
- 返回多行數據的子查詢只能同多值操作符一起使用,比如 IN 操作符。 
- 子查詢不能直接用在聚合函數中。 
- BETWEEN 不能同子查詢一起使用,但 BETWEEN 操作符可以用在子查詢中。 
創建數據表
通常情況下子查詢都與 SELECT 語句一起使用,其基本語法如下所示:
對于子查詢的數據演示創建兩個表,一個是薪水表,另一個是職位表,并且插入數據。
#創建薪水表SALARY CREATE?TABLE?SALARY? (ID?VARCHAR?(?10?), NAME?VARCHAR?(?10?), AGE?VARCHAR?(?10?), ADDRESS?VARCHAR?(?10?), SAL INT(10) );給薪水表插入數據,數據內容如下所示:
#?給薪水表插入數據 INSERT?INTO?SALARY(ID,NAME,AGE,ADDRESS,SAL) VALUES ('C001','Rmesh',35,'Ahmedabad',2000), ('C002','Khilan',25,'Delhi',1500), ('C003','Kaushik',23,'Kota',2000), ('C004','Chaitali',25,'Mumbai',6500), ('C005','Hardik',27,'Bhopal',8500), ('C006','Komal',22,'MP',4500), ('C007','Tom',26,'MP',5500), ('C008','Muffy',24,'Indore',10000);查詢所有的薪水數據如下所示:
SELECT * FROM SALARY;同理創建一個職位表。
#創建職位表JOB CREATE TABLE JOB (JID?VARCHAR?(?10?), JB VARCHAR ( 10 ));給職位表插入數據,數據內容如下所示:
#?給職位表插入數據 INSERT?INTO?JOB(JID,JB) VALUES ('C001','Teacher'), ('C002','Docter'), ('C003','Teacher'), ('C004','Worker'), ('C005','Nurse'), ('C006','Teacher'), ('C007','Docter'), ('C008','Teacher');查詢所有的職位數據如下所示:
SELECT * FROM JOB;子查詢過濾
子查詢最常見的使用是在WHERE子句的IN操作符中,以及用來填充計算列。先看一個簡單的例子,要查詢所有醫生的薪水情況,這里首先在職位表中查詢所有醫生的JID,查詢結果如下:
然后在薪水表中查詢ID為'C002','C007'的薪水情況,查詢結果如下:
SELECT SAL FROM SALARY WHERE ID IN('C002','C007');這里使用子查詢更加簡便,子查詢從內向外依次處理,在下面的SELECT語句中,MySQL實際上執行了兩個操作,首先查詢返回兩個ID號:C002和C007。
然后,這兩個值以IN操作符要求的逗號分隔的格式傳遞給外部查詢的WHERE子句,可以看到輸出的結果是正確的,并且與前面WHERE子句所返回的值相同。
SELECT SAL FROM SALARY WHERE ID IN(SELECT JIDFROM JOBWHERE JB='Docter');使用子查詢查詢薪水大于8000的員工的所有信息,首先內部查詢薪水大于8000的ID,然后外部使用一個WHERE查詢即可得到結果。
SELECT * FROM SALARY WHERE?ID?IN?(SELECT?IDFROM?SALARYWHERE SAL > 8000);作為計算字段使用子查詢
使用子查詢的另一方法是創建計算字段,創建計算字段需要使用聚合函數,例如count,sum,avg,max,min等,這里首先計算平均薪水作為一個內查詢,然后在外部使用WHERE子句進行查詢,得出薪資比平均薪資低的員工的所有信息。
SELECT?* FROM?SALARY WHERE SAL < (SELECT AVG(SAL)FROM SALARY);除使用WHERE過濾,還可以使用HAVING過濾,HAVING子句對分組統計函數進行過濾,也可以在HAVING子句中使用子查詢,要查詢薪資最高的人及其薪資情況,首先內部查詢最高工資,然后外部以人名分組后使用HAVING子句過濾,查詢結果如下。
SELECT NAME,SAL FROM SALARY GROUP BY NAME HAVING SAL = (SELECT MAX(SAL)FROM SALARY);窗口函數
窗口函數與數據分組功能相似,可指定數據窗口進行統計分析,但窗口函數與數據分組又有所區別,窗口函數對每個組返回多行,而數據分組對每個組只返回一行;窗口函數指定分析函數工作的數據窗口大小,這個數據窗口大小可能會隨著行的變化而變化,而數據分組是針對所有數據進行統計,窗口函數的寫法如下。
<窗口函數>?over?(partition?by?<用于分組的列名>order?by?<用于排序的列名>)窗口函數主要有兩種,一種是專用窗口函數,包括rank、dense_rank、row_number等。另一種是聚合函數,包括sum、avg、count、max、min等,下面逐一介紹窗口函數的五個功能,分別是聚合、排序、極值、移動、切片,下面一起來學習。
創建表
首先創建一個金額表,年份、姓名、國家設置為字符串類型,交易金額設置為整型。
給金額表插入數值。
# 給金額表插入數據 INSERT?INTO?pay(year,name,country,payment) VALUES (2017,'Lining','China',1119), (2018,'Lining','China',1176), (2018,'Zhaoqi','China',1388), (2019,'Zhaoqi','China',1597), (2018,'Jackie','USA',1028), (2019,'Jackie','USA',1934), (2020,'Jackie','USA',1837), (2017,'Tom','India',1578), (2018,'Tom','India',1329), (2019,'Tom','India',1578), (2020,'Tom','India',1399);將所有的數據查詢出來結果如下所示。
SELECT?*?from?pay;一、聚合
1、計算列表總金額
SELECT?*,?SUM(payment)?OVER()?as?Total_payment?from?pay;計算當前列表的總金額可以使用窗口函數,sum是求和,over()中不添加參數,則對所有數據進行求和,輸出的結果都是15963。
2、計算各國家總金額
計算各國家總金額就要對各個國家分組,這里分組使用的是PARTITION by,PARTITION by的功能與GROUP BY的功能類似,指定按照那一列進行分組,用country分組求和,則每個country的輸出結果一致。
3、按國家降序累加求和金額
這里使用SQL中常用的向下累計求和的方法,當使用order by時,沒有rows between則意味著窗口是從起始行到當前行,所以對不同國家進行累加求和操作。
4、不同國家人數計數
count()用于計數,與前面sum的用法基本一致,可以用count(distinct country)進行去重,如果用partition by進行分組,則分組后再計數。
SELECT *, COUNT(name) OVER() as Total_people, COUNT(name) OVER(PARTITION by country) as country_people from pay;5、 不同國家平均金額
使用avg聚合函數的用法與前面的聚合運算用法一致,PARTITION by同樣用來分組,這里分組后求均值。
6、各國家最低金額
這里MAX(payment)函數對整個數據計算最大值,使用PARTITION by對于不同的國家分組后然后計算最小值。
二、排序
1、各國家按金額排序
使用窗口函數排序,會使用到三個函數,row_number,rank,dense_rank,他們的使用區別如下:
- row_number從1開始,按照順序,生成分組內記錄的序列; 
- rank生成數據項在分組中的排名,排名相等會在名次中留下空位; 
- dense_rank生成數據項在分組中的排名,排名相等會在名詞中不會留下空位。 
row_number函數,按照行記錄的順序來排序,此處從1到11按順序排列;rank函數,在排名相等會在名次中留下空位,此處共同排名為第4名,同時忽略第5名,繼續往下排列;dense_rank排名相等會在名詞中不會留下空位此處共同排名為第4名,不忽略第5名,繼續往下排列。
三、極值
1、當前行金額最高的人
first_value截止當前行的第一個,last_value截止當前行的最后一個。
select *, first_value(name)over(order by payment desc) as max_id, first_value(name)over(order by payment asc) as min_id, last_value(name)over(order by payment desc) as min_id_1, last_value(name)over(partition by country order by payment desc rows between unbounded preceding and unbounded following) as level_min_id from pay;first_value按分組排序后取范圍內第1個值,last_value取最后1個值,因為默認窗口的關系,last_value會隨著窗口的改變而改變,所以一般不用last_value,如果要用,則改變窗口為所有行,此處用來查詢當前金額最大的人,以及截至當前金額最小的人。
四、移動
1、按國家分組金額排名前1位和后1位人名
lag和lead是按照排序規則,取前多少位和后多少位,參數有3個,第1個是要取出來的列,第2個移動多少位,第3個是如果取不到,賦予的值,默認取不到是NULL。
select *, lag(name,1,null)over(partition by country order by payment desc) as lag_id, lead(name,1,'0')over(partition by country order by payment desc) as lead_id from pay;五、切片
1、按金額切片
ntile(n)用于將分組數據按照順序切分成N片,返回當前切片值,ntile把有序分區中的行分發到指定數據的組中,各個組有編號,編號從1開始,對于每一行,ntile返回此行所屬的組的編號,ntile(3)表示將表切分為3組,ntile可以分組排序后切分,表示對當前的組內進行切分后排序。
select *, ntile(3) over(order by payment desc) as total_part, ntile(2)over(partition by country order by payment desc) as level_part from pay;-?END -
對比Excel系列圖書累積銷量達15w冊,讓你輕松掌握數據分析技能,可以在全網搜索書名進行了解:總結
以上是生活随笔為你收集整理的SQL进阶,子查询与窗口函数的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 淘宝天猫历史最大投入618:今年推出直降
- 下一篇: 回归初心的诚意之作?敞篷电跑MG Cyb
