2013-04-30 146 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