这是受这篇文章启发的中位数的另一种看法,使用SUBSTRING_INDEX
and GROUP_CONCAT
。相对于@fancyPants 所描述的使用行号的方法,我不确定大型表的性能,但在较小的表(~20K 行)上,它的运行速度非常快。
SET SESSION group_concat_max_len = 1000000;
SELECT
created_at,
(
CAST(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(
price ORDER BY price SEPARATOR ','),
',', FLOOR((COUNT(*)+1)/2) ), ',', -1) AS DECIMAL) +
CAST(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(
price ORDER BY price SEPARATOR ','),
',', FLOOR((COUNT(*)+2)/2) ), ',', -1) AS DECIMAL)
) / 2.0 AS median_price
FROM
mediana
GROUP BY
created_at
;
这是问题中给出的sqlfiddle的输出(小提琴似乎坏了,但我在 MySQL 本身的小提琴中显示的表上运行它):
+------------+--------------+
| created_at | median_price |
+------------+--------------+
| 2012-03-05 | 3.5000 |
| 2012-03-06 | 1.5000 |
+------------+--------------+
本质上创建了每个日期GROUP_CONCAT
价格数组的字符串表示形式。created_at
然后这两个SUBSTRING_INDEX
命令寻找中间值,即中值。有必要对它们进行两次调用并对它们进行平均处理以处理单个日期GROUP_CONCAT
有偶数个元素的情况。price
created_at
更新:
值得一提的是,该GROUP_CONCAT
函数的默认长度为 1024 字节,请参见此处。这可能会导致很长的结果被截断,从而导致计算错误。如果您担心较大的结果,您可以使用命令SET SESSION group_concat_max_len = N;
where is some other,较大的值设置更大的默认值。N
我已将该设置添加到上面的代码片段中。我选择了 1000000,但您也可以使用其他值。
COUNT(*)
您还可以使用您的值OFFSET
之一来抽查您的结果。GROUP BY
例如,
- 首先获取特定
GROUP BY
值的行数,
SELECT COUNT(*) FROM mediana WHERE created_at = '2012-03-06';
让我们X
从第 1 步得到的行数。除以X
2 得到一半的值,Y
。
使用该值Y
作为偏移量来找到中位数。
一种。如果Y
是一个整数,那么两者都做
SELECT price FROM mediana WHERE created_at = '2012-03-06' ORDER BY price LIMIT 1 OFFSET (Y-1);
和
SELECT price FROM mediana WHERE created_at = '2012-03-06' ORDER BY price LIMIT 1 OFFSET Y;
并平均两个结果以获得中值。
湾。如果Y
是小数,则向下舍Y
入到最接近的整数(调用它W
)并将其用作单个偏移量,
SELECT price FROM mediana WHERE created_at = '2012-03-06' ORDER BY price LIMIT 1 OFFSET W;
这将是您的中值。