1. 程式人生 > 實用技巧 >MySQL中如何查詢中位數

MySQL中如何查詢中位數

員工薪水中位數

題目描述:

預期答案:

解法1

既然是求解中位數,我們首先想到的是根據中位數的定義進行求解:奇數個數字時,中位數是中間的數字;偶數個數字時,中位數中間兩個數的均值。本題不進行求解均值,而是將兩個中位數全部顯示。

根據定義,為了查詢中位數,我們需要知道3點資訊:

  • 總數是奇數個還是偶數個

  • 待查詢數字總數

  • 每個數字的排序編號

前兩點資訊在MySQL中非常簡單,只需簡單的count計數即可,而排序編號則需要藉助輔助方法。在MySQL8.0以上版本引入了視窗函式後非常容易實現,但以前的版本則僅可通過自定義變數的方式獲得排序值。這裡如何對員工薪水進行分組排序不再展開

在有了排名和數字總數之後,如何判斷是中位數呢?這裡計數字總數為N,則

  • N為奇數,中位數排序編號是(N+1)/2=N/2+0.5

  • N為偶數,中位數排序編號是N/2和N/2+1

進一步地,N為奇數和N為偶數是互斥的,求解出的中位數排序編號也是互斥的,也就是說3個排序編號不會同時取得整數,從而可以不加區分的直接判斷即可。

查詢SQL語句:

SELECT
     e1.Id, e1.Company, e1.Salary
 FROM
     (SELECT Id, Company, Salary, @rnk:=if(@pre=Company, @rnk+1, 1) rnk, @pre:=Company
     FROM Employee, (SELECT @rnk:=0, @pre:=null)init
     ORDER by Company, Salary, Id)e1 
     JOIN 
     (SELECT Company, count(*) cnt FROM Employee GROUP by Company) e2
     using(Company)
WHERE e1.rnk in (cnt/2+0.5, cnt/2, cnt/2+1)

  

查詢效率:

解法2

除了根據中位數的排序編號來定位其位置,實際上還可以換種思路但仍然是在其排序編號上做文章:如果一個數是中位數,那麼就意味著正序和逆序時其位置是一致的:更嚴謹的說,奇數個數字是正逆序排序一致,偶數個數字時,兩中位數順序要互換一下,也就是相差為1。進而,我們發現無論數字總數是奇數還是偶數,中位數的正逆排序相差要麼為0,要麼為1。根據這一性質,我們分別實現正逆兩遍排序,然後判斷數字的排序編號即可。

查詢SQL語句:

 
SELECT
 
    e1.Id, e1.Company, e1.Salary
 
FROM
 
    (SELECT Id, Company, Salary, @rnk:=if(@pre=Company, @rnk+1, 1) rnk, @pre:=Company
 
    FROM Employee, (SELECT @rnk:=0, @pre:=null)init
 
    ORDER by Company, Salary, Id)e1 
 
    JOIN 
 
    (SELECT Id, Company, Salary, @rnk:=if(@pre=Company, @rnk+1, 1) rnk, @pre:=Company
 
    FROM Employee, (SELECT @rnk:=0, @pre:=null)init
 
    ORDER by Company, Salary DESC, Id DESC)e2
 
    on e1.Id=e2.Id
 
WHERE abs(e1.rnk - e2.rnk)<=1

  

查詢效率:

解法3

前2種解法都是根據中位數的定義在數字排序編號上作文章,下面是一個對中位數性質更深的理解(摘抄自官方題解)

根據定義,我們來找一下[1, 3, 2]的中位數。首先 1 不是中位數,因為這個陣列有三個元素,卻有兩個元素(3,2)大於 1。3 也不是中位數,因為有兩個元素小於 3。對於 2 來說,大於 2 和小於 2 的元素數量是相等的,因此 2 是當前陣列的中位數。當陣列長度為偶數,且元素唯一時,中位數等於排序後中間兩個數的平均值。對這兩個數來說,大於當前數的數值個數跟小於當前數的數值個數絕對值之差為 1,恰好等於這個數出現的頻率。

結論:不管陣列長度是奇是偶,也不管元素是否唯一,中位數出現的頻率一定大於等於大於它的數和小於它的數的絕對值之差。

好吧,力扣的官方題解讀起來總是這麼生澀。不過細品之下,我們還是可以發現這個結論是對的。【好像說了句廢話】

根據中位數的這一性質,可以寫出如下查詢語句:

SELECT
 
    e1.Id, e1.Company, e1.Salary
 
FROM
 
    Employee e1,
 
    Employee e2
 
WHERE
 
    e1.Company = e2.Company
 
GROUP BY e1.Company , e1.Salary
 
HAVING SUM(e1.Salary = e2.Salary) >= ABS(SUM(SIGN(e1.Salary - e2.Salary)))
 
ORDER BY e1.Id

  

查詢效率:

實際上,雖然3種解法均為兩表關聯,但由於解法3中涉及到相對更為複雜的計算,其效率竟然要比解法1和解法2中低太多。

所以,不妨想想奧卡姆剃刀原理,大道至簡、大巧不工、簡單之美!