2013-04-30 144 views
7

我有以下MySQL查詢這是工作完美:在MySQL中使用沿邊MAX,MIN和AVG函數MEDIAN

select 
    count(*) as `# of Data points`, 
    name, 
    max((QNTY_Sell/QNTYDelivered)*1000) as `MAX Thousand Price`, 
    min((QNTY_Sell/QNTYDelivered)*1000) as `MIN Thousand Price`, 
    avg((QNTY_Sell/QNTYDelivered)*1000) as `MEAN Thousand Price` 
from 
    table_name 
where 
    year(date) >= 2012 and 
    name like "%the_name%" and 
    QNTYDelivered > 0 and 
    QNTY_Sell > 0 
group by name 
order by name; 

現在我還要加上一個結果列,讓我的的MEDIAN每行的數據。在SELECT這看起來像這樣在一個完美的世界:

median((QNTY_Sell/QNTYDelivered)*1000) as `MEDIAN Thousand Price` 

搜索谷歌的一個MySQL值函數把我帶到這個答案,如果你有興趣在數據集中的中位數爲整個表,似乎確定:Simple way to calculate median with MySQL

這裏的區別在於我將name列中的表中的數據分組,並且希望獲得按此列分組的數據的每一行的中位數。

有誰知道一個漂亮的技巧,讓我做到這一點?

謝謝!

+0

mysql中沒有中位數函數 – 2013-10-29 09:33:21

+3

是否必須處於同一個查詢中?如果可以運行第二個查詢,則可以計算該集合的中點,以知道數據點的數量。中點是一行或兩行。運行相同的查詢,但添加LIMIT [中點],[中點mod 2]並返回平均值。 – 2013-10-30 06:50:56

+0

如果你這樣做,你將不得不改變順序 - 我假設你想要QNTY_Sell/QNTYDelivered的中位數,所以你必須命令它來找到中點。 – nickL 2014-01-22 18:50:06

回答

2

我發現做到這一點的唯一方法是通過字符串操作:
GROUP_CONCAT被創建的所有值的列表,然後用鋸齒狀SUBSTRING_INDEX中間值取

SELECT 
    count(*) AS `# of Data points`, 
    name, 
    max((QNTY_Sell/QNTYDelivered)*1000) AS `MAX Thousand Price`, 
    min((QNTY_Sell/QNTYDelivered)*1000) AS `MIN Thousand Price`, 
    avg((QNTY_Sell/QNTYDelivered)*1000) AS `MEAN Thousand Price` 
    , CASE (count(*) % 2) 
    WHEN 1 THEN SUBSTRING_INDEX(
     SUBSTRING_INDEX(
     group_concat((QNTY_Sell/QNTYDelivered)*1000 
         ORDER BY (QNTY_Sell/QNTYDelivered)*1000 SEPARATOR ',') 
     , ',', (count(*) + 1)/2) 
    , ',', -1) 
    ELSE (SUBSTRING_INDEX(
     SUBSTRING_INDEX(
     group_concat((QNTY_Sell/QNTYDelivered)*1000 
         ORDER BY (QNTY_Sell/QNTYDelivered)*1000 SEPARATOR ',') 
     , ',', count(*)/2) 
    , ',', -1) 
    + SUBSTRING_INDEX(
     SUBSTRING_INDEX(
     group_concat((QNTY_Sell/QNTYDelivered)*1000 
         ORDER BY (QNTY_Sell/QNTYDelivered)*1000 SEPARATOR ',') 
     , ',', (count(*) + 1)/2) 
    , ',', -1))/2 
    END median 
FROM 
    sales 
WHERE 
    year(date) >= 2012 AND 
    name LIKE "%art.%" AND 
    QNTYDelivered > 0 AND 
    QNTY_Sell > 0 
GROUP BY name 
ORDER BY name; 

需要的情況下檢查如果我們有一個單一的中間值,奇數個值或兩個中間值,偶數個值,在第二種情況下,中位數是兩個值的平均值。

SQLFiddle

+0

看着這個解決方案的野獸,我真的很喜歡,真的很奇怪爲什麼SQL平臺會拒絕實現Median和Mode函數。擁有AVG,MIN,MAX但不是另外兩個是相當大的...... – Tomm 2014-06-13 10:16:36

+0

不要誤解我的意思,我讚賞你的解決方案。但是,我們不得不對Dostjewski收集的作品的大小提出疑問,以獲得血腥的中位數。我在這裏結束了,因爲我需要在幾個嵌套子查詢的上下文中獲得中位數和模式;並且認識到像中位數稍微煩人一樣看似微不足道的事情是非常困難的。 – Tomm 2014-06-13 10:44:18

+0

嘿,我實現了你的解決方案,但注意到偶數個值的中位數是'關閉了一個單位',即在10個數值的數組中,你的解決方案將中間值設置在第4個和第5個值之間,而不是第5個和第六。我通過在ELSE語句的兩個SUBSTRING_INDEX調用中使用'(count(*)/ 2)+ 1'來解決這個問題。 – Tomm 2014-06-13 13:18:06

3

可以計算與GROUP BY在MySQL中位數即使是沒有內置位機能

考慮表:

Acrington 200.00 
Acrington 200.00 
Acrington 300.00 
Acrington 400.00 
Bulingdon 200.00 
Bulingdon 300.00 
Bulingdon 400.00 
Bulingdon 500.00 
Cardington 100.00 
Cardington 149.00 
Cardington 151.00 
Cardington 300.00 
Cardington 300.00 

對於每一行,你可以計算較少的類似項目的數量。您也可以算多少值都小於或等於:

name  v  < <= 
Acrington 200.00 0 2 
Acrington 200.00 0 2 
Acrington 300.00 2 3 
Acrington 400.00 3 4 
Bulingdon 200.00 0 1 
Bulingdon 300.00 1 2 
Bulingdon 400.00 2 3 
Bulingdon 500.00 3 4 
Cardington 100.00 0 1 
Cardington 149.00 1 2 
Cardington 151.00 2 3 
Cardington 300.00 3 5 
Cardington 300.00 3 5 

通過查詢會發生

SELECT name,v, (SELECT COUNT(1) FROM sale WHERE v<o.v AND name=o.name) as ls 
      , (SELECT COUNT(1) FROM sale WHERE v<=o.v AND name=o.name) as lse 
    FROM sale o 

中值當低於或相等計數的項目數的一半

  • Acrington有4個項目。的這一半是2這是在(對應於200.00)的範圍0..2以及在(對應於300.00)的範圍2..3

  • Bullingdon也有4個項目。 2在範圍1..2(值300.00)和2..3(值400.00)

  • Cardington有5項。值2.5在2和3之間,對應於Cardington 151。

中值是最小的平均值和最大值由歸國:

SELECT cs.name,v 
    FROM 
    (SELECT name,v, (SELECT COUNT(1) FROM sale WHERE v<o.v AND name=o.name) as ls 
       , (SELECT COUNT(1) FROM sale WHERE v<=o.v AND name=o.name) as lse 
     FROM sale o) cs JOIN 
    (SELECT name,COUNT(1)*.5 as cn 
     FROM sale 
     GROUP BY name) cc ON cs.name=cc.name 
WHERE cn between ls and lse 

其中給出:

Acrington 200.00 
Acrington 200.00 
Acrington 300.00 
Bulingdon 300.00 
Bulingdon 400.00 
Cardington 151.00 

最後,我們可以得到位數:

SELECT name,(MAX(v)+MIN(v))/2 FROM 
(SELECT cs.name,v 
    FROM 
    (SELECT name,v, (SELECT COUNT(1) FROM sale WHERE v<o.v AND name=o.name) as ls 
       , (SELECT COUNT(1) FROM sale WHERE v<=o.v AND name=o.name) as lse 
     FROM sale o) cs JOIN 
    (SELECT name,COUNT(1)*.5 as cn 
     FROM sale 
    GROUP BY name) cc ON cs.name=cc.name 
WHERE cn between ls and lse 
) AS medians 
GROUP BY name 

給予

Acrington 250.000000 
Bulingdon 350.000000 
Cardington 151.000000