技.藝.道:SQL的各種join與開窗函數

一、join

在多表關聯查詢場景中,join是常用的方式。join,漢譯爲“連接”。連接分爲內連接(inner join)和外連接(out join)。外連接分爲左外連接和右外連接和全外連接,它們由分別簡稱爲“左連接(left join)”、“右連接(right join)”和“全連接(full join)”。當只使用關鍵字“join”時,等同於內連接(inner join)。

0.準備數據

-- 1.學生表:學號,姓名,性別,年齡
-- 1)建表:
create table t_student(p_id int(4),name varchar(10),gender char(2),age int(2));
-- 2)插入數據:
insert into t_student values (1,'小紅','女',13);
insert into t_student values (2,'小明','男',14);
insert into t_student values (4,'小劉','男',13);
	
-- 2.考試成績表:學號,語文成績,數學成績,英語成績,學期,學年
-- 1)建表:
create table t_result(student_id int(4),chinese_result int(3),math_result int(3),english_result int(3),trimester int(1),year int(4));
-- 2)插入數據:
insert into t_result values (1,95,69,98,1,2019);
insert into t_result values (1,97,77,95,2,2019);
insert into t_result values (1,95,80,90,1,2020);
insert into t_result values (1,86,73,91,2,2020);
insert into t_result values (2,68,89,49,1,2019);
insert into t_result values (2,74,96,66,1,2020);
insert into t_result values (2,85,98,59,2,2020);
insert into t_result values (3,95,99,99,1,2019);
insert into t_result values (3,96,100,100,2,2019);
insert into t_result values (3,97,100,100,1,2020);
insert into t_result values (3,98,100,100,2,2020);

1.內連接(inner join)or join

select t1.name,t2.chinese_result,t2.math_result,t2.english_result,t2.trimester,t2.year 
from t_student t1 join t_result t2 
on t1.p_id=t2.student_id;

select t1.name,t2.chinese_result,t2.math_result,t2.english_result,t2.trimester,t2.year 
from t_student t1 inner join t_result t2 
on t1.p_id=t2.student_id;

查詢結果:

2.left join

select t1.name,t2.chinese_result,t2.math_result,t2.english_result,t2.trimester,t2.year 
from t_student t1 left join t_result t2 
on t1.p_id=t2.student_id;

查詢結果:

3.right join

select t1.name,t2.chinese_result,t2.math_result,t2.english_result,t2.trimester,t2.year 
from t_student t1 RIGHT join t_result t2 
on t1.p_id=t2.student_id;

查詢結果: 

4.full join

select t1.name,t2.chinese_result,t2.math_result,t2.english_result,t2.trimester,t2.year 
from t_student t1 left join t_result t2 
on t1.p_id=t2.student_id 
union
select t1.name,t2.chinese_result,t2.math_result,t2.english_result,t2.trimester,t2.year 
from t_student t1 RIGHT join t_result t2 
on t1.p_id=t2.student_id;
-- 由於我使用的是MySQL數據庫服務,其不支持“full join”
-- 因此我們使用left join + union + right join來實現(上面一段代碼)。等同於:
select t1.name,t2.chinese_result,t2.math_result,t2.english_result,t2.trimester,t2.year 
from t_student t1 full join t_result t2 
on t1.p_id=t2.student_id;

5.小結

5.0 原始數據

5.1 inner join

5.2 left join

5.3 right join

5.4 full join

二、開窗函數

0.數據準備

數據表(Oracle):T_Person 表保存了人員信息,FName 字段爲人員姓名,FCity 字段爲人員所在的城市名,FAge 字段爲人員年齡,FSalary 字段爲人員工資

-- 1)建表
CREATE TABLE T_Person (FName string,FCity string,FAge INT,FSalary INT) 
row format delimited fields terminated by ',';

-- 2)插入數據
INSERT INTO T_Person VALUES('Tom','BeiJing',20,3000);
INSERT INTO T_Person VALUES('Tim','ChengDu',21,4000);
INSERT INTO T_Person VALUES('Jim','BeiJing',22,3500);
INSERT INTO T_Person VALUES('Lily','London',21,2000);
INSERT INTO T_Person VALUES('John','NewYork',22,1000);
INSERT INTO T_Person VALUES('YaoMing','BeiJing',20,3000);
INSERT INTO T_Person VALUES('Swing','London',22,2000);
INSERT INTO T_Person VALUES('Guo','NewYork',20,2800);
INSERT INTO T_Person VALUES('YuQian','BeiJing',24,8000);
INSERT INTO T_Person VALUES('Ketty','London',25,8500);
INSERT INTO T_Person VALUES('Kitty','ChengDu',25,3000);
INSERT INTO T_Person VALUES('Merry','BeiJing',23,3500);
INSERT INTO T_Person VALUES('Smith','ChengDu',30,3000);
INSERT INTO T_Person VALUES('Bill','BeiJing',25,2000);
INSERT INTO T_Person VALUES('Jerry','NewYork',24,3300);

1.聚合開窗函數

聚合開窗函數可以使用SUM、AVG、MAX、MIN、COUNT等聚合函數,但是需要注意聚合開窗函數只能使用PARTITION BY子句,ORDER BY不能與聚合開窗函數一同使用。

1.1 求滿足條件的數據的總行數

count(*)over()

查詢每個工資小於 5000 元的員工信息(城市以及年齡),並且在每行中都顯示所有工資小於 5000 元的員工個數:

select fname, fcity, fsalary, 
count(*) over() a -- 總人數 
from t_person 
where fsalary < 5000;

-- 類似於如下 使用子查詢 實現的效果:
select fname,
fcity,
fsalary,
(select count(*) from t_person where fsalary < 5000) -- 工資少於5000員工總數
from t_person 
where fsalary < 5000;

運行結果:

Guo     NewYork 2800    13
Swing   London  2000    13
YaoMing BeiJing 3000    13
John    NewYork 1000    13
Lily    London  2000    13
Jim     BeiJing 3500    13
Jerry   NewYork 3300    13
Bill    BeiJing 2000    13
Smith   ChengDu 3000    13
Merry   BeiJing 3500    13
Kitty   ChengDu 3000    13
Tim     ChengDu 4000    13
Tom     BeiJing 3000    13

1.2 求各個分區的總行數

count(*) over(partition by fcity)

select fname,
fcity,
fage,
fsalary,
count(*) over(partition by fcity) a -- 所在分區的行數
from t_person;

運行結果:

Tom     BeiJing 20      3000    6
YuQian  BeiJing 24      8000    6
YaoMing BeiJing 20      3000    6
Jim     BeiJing 22      3500    6
Bill    BeiJing 25      2000    6
Merry   BeiJing 23      3500    6
Kitty   ChengDu 25      3000    3
Tim     ChengDu 21      4000    3
Smith   ChengDu 30      3000    3
Lily    London  21      2000    3
Swing   London  22      2000    3
Ketty   London  25      8500    3
Jerry   NewYork 24      3300    3
Guo     NewYork 20      2800    3
John    NewYork 22      1000    3

1.3 求到當前行 對應列 的數值之和

sum(fsalary) over(order by fsalary rows between unbounded preceding and current row) a

(註解:unbounded preceding -- 無上界,從第一行開始;current row -- 當前行)

select fname,
fcity,
fage,
fsalary,
sum(fsalary) over(order by fsalary rows between unbounded preceding and current row) a -- 到當前行工資的和
from t_person;

運行結果:

John    NewYork 22      1000    1000
Swing   London  22      2000    3000
Lily    London  21      2000    5000
Bill    BeiJing 25      2000    7000
Guo     NewYork 20      2800    9800
Smith   ChengDu 30      3000    12800
Kitty   ChengDu 25      3000    15800
YaoMing BeiJing 20      3000    18800
Tom     BeiJing 20      3000    21800
Jerry   NewYork 24      3300    25100
Jim     BeiJing 22      3500    28600
Merry   BeiJing 23      3500    32100
Tim     ChengDu 21      4000    36100
YuQian  BeiJing 24      8000    44100
Ketty   London  25      8500    52600

1.4 小結

聚合函數() over()這裏的“聚合函數() over()”,前者“聚合函數()”用來控制“對哪些列?做什麼聚合?”,後者“over()”表示“對哪些行?”。

聚合函數

聚合函數可以使用SUM、AVG、MAX、MIN、COUNT等聚合函數,來實現聚合操作。

控制列

列通過在調用聚合函數時傳入實參(函數後面的括號)來指定。

控制行

行通過over()的括號中傳入“一些操作”來實現。一些操作:可以是空,表示*,即對所有數據進行聚合操作;可以是分區函數“PARTITION BY”子句用來定義“在行的分區內部進行聚合計算”。 

2.排名開窗函數

2.1 各排名函數的特點

特點

函數名

排名並列算兩個

rank()

排名並列算一個

dense_rank()

排名不併列

row_number()

 

例如:

姓名

地區

分數

rank

dense_rank

row_number

小李

安徽

91

1

1

1

小丁

安徽

90

2

2

2

小趙

安徽

90

2

2

3

小丑

安徽

77

4

3

4

小甲

安徽

68

5

4

5

小子

廣東

98

1

1

1

小卯

廣東

96

2

2

2

小孫

廣東

85

3

3

3

小丙

廣東

67

4

4

4

小紅

江蘇

98

1

1

1

小明

江蘇

98

1

1

2

小錢

江蘇

98

1

1

3

小乙

江蘇

81

4

2

4

小寅

江蘇

79

5

3

5

排名的數值上的特點:

2.2 使用方法

語法:

select *, rank() over(partition by 分組列 order by 排名列 desc)as 排名 
from 成績表;

select *, dense_rank() over(partition by 分組列 order by 排名列 desc)as 排名 
from 成績表;

select *, row_number() over(partition by 分組列 order by 排名列 desc)as 排名 
from 成績表;

實例:

select *, rank() over(partition by 地區 order by 分數 desc)as 排名 
from 成績表;

select *, dense_rank() over(partition by 地區 order by 分數 desc)as 排名 
from 成績表;

select *, row_number() over(partition by 地區 order by 分數 desc)as 排名 
from 成績表;

如果需要獲取指定排名或之前的數據,可配合子查詢實現。

select * from (
    select *, rank() over(partition by 分組列 order by 排名列 desc)as 排名 
    from 成績表) as t 
where 排名=N;

select * from (
    select *, rank() over(partition by 分組列 order by 排名列 desc)as 排名 
    from 成績表) as t 
where 排名<N;
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章