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中低太多。

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


免責聲明!

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



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