oracle:中位數查詢整理




中位數的數學定義

中位數的定義如下圖所示:
在這里插入圖片描述
oracle數據查詢中,當N為偶數時,一般不取平均值,因為這有可能創建出新的樣例使得查詢語句為空,所以通常為偶數時,一般返回最中間的一組數據。


oracle中位數查詢

1.求解思路

目前常見的求解主要包括以下兩種思路:

1)從數值角度出發;

先按照目標列進行排序,然后按照總行數的奇偶性,利用類似上圖的數學方法進行方法進行篩選。

2)從數值索引(所在位置)出發

這種方法從中位數可能出現的位置,進行巧妙構造,以選取目標位置。
類似於python中根據目標索引list進行切片。其更符合中位數的定義,即有一半的數大於中位數,有一半數小於中位數。

實際應用中出於不同的考慮,對於內置函數median選擇性使用


2.應用案例

為了方便說明,采用leetcode上的案例:編寫SQL查詢來查找每個公司的薪水中位數。

Id Company Salary
1 A 2341
2 A 341
3 A 15
4 A 15314
5 A 451
6 A 513
7 B 15
8 B 13
9 B 1154
10 B 1345
11 B 1221
12 B 234
13 C 2345
14 C 2645
15 C 2645
16 C 2652
17 C 65
  • 表創建語句:
create table employee(
id number(3) primary key,
    company varchar2(2),
    salary number(8)
);

insert into employee values(1,'A',2341);
insert into employee values(2,'A',341);
insert into employee values(3,'A',15);
insert into employee values(4,'A',15314);
insert into employee values(5,'A',451);
insert into employee values(6,'A',513);
insert into employee values(7,'B',15);
insert into employee values(8,'B',13);
insert into employee values(9,'B',1154);
insert into employee values(10,'B',1354);
insert into employee values(11,'B',1221);
insert into employee values(12,'B',234);
insert into employee values(13,'C',2345);
insert into employee values(14,'C',2645);
insert into employee values(15,'C',2645);
insert into employee values(16,'C',2652);
insert into employee values(17,'C',65);

(一)使用median函數的情況
select id,company,salary
from (
	select tmp.* ,median(r_num) over(partition by company) med
	from (
			select e.*, row_number() over(partition by company order by salary) r_num
				from employee e) tmp)
where abs(r_num-med)<=0.5;

程序解釋:利用median求出的中位數是數學上的定義,不是我們所需要的.所以利用median進行改造.思路:獲取每一份組的行號row_number(對應的新列記為r_num)和median(對應的新列記為med),當abs(r_num-med)<=0.5時,說明取到了中位數;

統計結果如下:
運行結果


(二)不使用median函數的情況

(1)----->>>對應的第一種方法
這種方法需要區分不同分組數據量的奇偶性,在使用where的時候需要考慮單值和多值混合查詢。
具體查詢語句:

select id,company,salary
from (
	select e.*, count(*) over(partition by company) x,row_number() over(partition by company order by salary) r_num
		from employee e) tmp
where r_num in (ceil(x/2),x/2+1);

最終的查詢結果:
最終的查詢結果
值得注意的是where r_num in (ceil(x/2),x/2+1)的設定。當x是奇數時,(ceil(x/2),x/2+1)中只有一個有效(即ceil(x/2)=(x+1)/2);當x是偶數時(ceil(x/2),x/2+1)=(x/2,x/2+1)就是最中間的一組數據。

這類方法的其它查詢案例,大多需要通過group by+連接構造,相對於上面的比較復雜,本文暫且不考慮其它方法.


(2)----->>>對應的第一種方法

select e1.id,e1.company,e1.salary
from employee e1,employee e2
where e1.company=e2.company(+) 
group by e1.company,e1.salary,e1.id
having sum(decode(e1.salary-e2.salary,0,1,0))
        >=abs(sum(sign(e1.salary-e2.salary)))
order by e1.id;

程序分解:以A公司為例,共有6條數據。
A公司數據
上述程序中核心思路是采用自連接+having條件having條件構造的特別巧妙。所以將對其進一步分解:如下圖所示。
having結果
中間的命令行截圖為A公司數據的自連接結果(按照salary)排序。其中“+”表示大於當前對象的樣例個數(后面的具體數字);“0”表示相等;“-”表示小於。sum(sign())表示having中的部分結果。x=sign(a)為符號函數,a>0,x=1;a=0,x=0,a<0,x=-1.

其中having的統計結果如下圖所示,每一組中相等的情況中只有一種圖表中的A列(A公司正好沒有重復數據),abs()的統計結果為(圖表中的B列),具體計算方法見上圖(關聯結果)
having涉及到的統計數據


(2)----->>>對應的第二種方法
思路仍然是從中位數的定義出發,只不過引用的核心函數是row_number,和count(*)。其中row_number的使用方法,請查看oracle學習筆記(六):oracle中排序函數及其應用_數據庫_qq_40584718的博客-CSDN博客

具體程序(來源於leetcode)如下:


/* Write your PL/SQL query statement below */
select
	id, company, salary
from
	(select
		id, company, salary,
		row_number() over (partition by company order by salary) as rn, -- 各薪水記錄在其公司內的順序編號
		count(1) over (partition by company) as cnt -- 各公司的薪水記錄數
	from employee
	)
where abs(rn - (cnt+1)/2) < 1 -- 順序編號在公司薪水記錄數中間的,即為中位數

核心程序解讀:
(1) row_number() over() 按照公司分組,並按照薪水排序,將該結果保存為新列rn;
(2) count(1) over()應該和count(*) over()效果相同,是用來統計不同公司的樣例條數;
(3) where調價,這個程序的靈魂rn可以理解為一個列表,where的運行過程可以理解為下圖:
在這里插入圖片描述

where abs(rn - (cnt+1)/2) < 1 -- 順序編號在公司薪水記錄數中間的,即為中位數

這里的<1說明rn中有(cnt+1)/2特別靠近的行號(或者理解為索引或位置)存在。舉例說明:

假如rn=[1,2,...8],則(cnt+1)/2=4.5,中對數對應的索引為4和5
rn=[1,2,...,7],則(cnt+1)/2=4,那么這個中位數對應的索引只能是4.


參考:
569. 員工薪水中位數 - 力扣(LeetCode):評論+題解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM