【问题标题】:Percentile in MySQL grouped by dateMySQL中的百分位数按日期分组
【发布时间】:2013-10-30 08:03:01
【问题描述】:

我有一个数据库表,它基本上包含 date Date, int UserId, double Value 列。

我希望能够进行查询,为我提供所有用户的每个日期价值 10% 和 90% 的百分位数,例如 SELECT Date, Pct10(Value), Pct90(Value) from Table group by Date

我知道在 MySQL 中使用 Count(*)LIMIT 计算百分位数并计算行数的不同方法,但是,我不知道如何对一条语句中的每个日期值迭代地应用此方法。

示例数据:

Date       | UserId  | Value
2013-01-01 |      0  |     1
2013-01-01 |      1  |     1
2013-01-01 |      2  |     1
2013-01-01 |      3  |     1
2013-01-01 |      4  |     2
2013-01-01 |      5  |     2
2013-01-01 |      6  |     2
2013-01-01 |      7  |     2
2013-01-01 |      8  |     2
2013-01-01 |      9  |     2
2013-01-01 |     10  |     9
2013-01-02 |      1  |     1
2013-01-02 |      9  |     1

预期结果是

Date       | Pct10  | Pct90
2013-01-01 |     1  |     2
2013-01-02 |     1  |     1

【问题讨论】:

  • 你如何计算百分比?
  • 这是我的问题。 MySQL 中没有 Pctxx() 函数。 90% 的百分位数将计算该日期的所有匹配行,并获取位置 0.9 * count 处的行的值(按值排序的行)。
  • 我的意思是你从哪里得到 10% 或 90%!对不起,如果我误解了你。但我没有得到“在位置 0.9 处取行的值”。你必须有一个值来计算,所以,你想对所有值求和,然后从中取 10% 或 90% 还是什么?再次抱歉,如果我不了解你...我的英语不太好
  • Jorge,请参阅此处了解百分位数的说明:en.wikipedia.org/wiki/Percentile
  • 我已经不明白了,你想要的行对应于按日期分组的查询行数的 10% 或 90%

标签: mysql statistics


【解决方案1】:

我不确定获得百分位数。我正在使用基于下面select nth percentile from mysql 的子查询,但我不太确定我是否已正确修改它。我的回答的重点在于子查询的组合。

随着表大小的增加,以下查询会很慢并且呈指数级变慢,但它应该可以满足您的需求:

SELECT p10.Date, Pct10, Pct90
FROM (
    SELECT Date, count(Value) AS Pct10
    FROM mydata 
    GROUP BY Date, Value
    ORDER BY ABS(0.1-(count(Value)/(select count(*) from mydata)))
    LIMIT 1) AS p10
INNER JOIN (
    SELECT Date, count(Value) AS Pct9
    FROM mydata 
    GROUP BY Date, Value
    ORDER BY ABS(0.9-(count(Value)/(select count(*) from mydata)))
    LIMIT 1) AS p90 ON p10.Date = p90.Date
GROUP BY p1.Date

这是我的第二个想法。如果它有效,它将比我列出的第一个更快、更高效,但对于较大的表来说仍然相当慢。

SELECT p10.Date, count(Value) AS Pct10, Pct90
FROM mydata p10
INNER JOIN (
    SELECT Date, count(Value) AS Pct90
    FROM mydata 
    GROUP BY Date, Value
    ORDER BY ABS(0.9-(count(Value)/(select count(*) from mydata)))
    LIMIT 1) AS p90 ON p10.Date = p90.Date
GROUP BY Date, Value
ORDER BY ABS(0.1-(count(Value)/(select count(*) from mydata)))
LIMIT 1

编辑

好的,头脑风暴时间。鉴于这是一个日期百分位数的子查询(我什至不确定它是如何工作的):

    SELECT Date, count(Value) AS Pct90
    FROM mydata 
    WHERE Date = ?
    GROUP BY Value
    ORDER BY ABS(0.9-(count(Value)/(select count(*) from mydata WHERE Date = ?)))
    LIMIT 1

然后让我们尝试修复 ORDER BY:

   SELECT Date, count(Value) as Pct90
   FROM mydata
   INNER JOIN (SELECT Date, COUNT(*) AS DateTotal FROM mydata GROUP BY Date) AS d 
       ON d.Date = mydata.Date
   GROUP BY Date, Value
   ORDER BY (ABS(0.9-(COUNT(Value)/d.DateTotal)))
   LIMIT 1

如果您在我之前的示例中使用此模式,也许它会起作用。

编辑 2

所以,我们又来了,因为我们不能使用 LIMIT 1(我之前应该意识到这一点)。我实际上在我自己的数据库上测试了以下内容(希望我将所有字段和表名改回它们应该的样子!),它似乎工作。您必须为 p10 再次执行此操作并将两者结合起来。

--- removed due to typos ---

编辑 3

我在编辑 2 中发现了一些错误,因此我将其删除。这是整个百分比查询。据我所知,此查询适用于我的数据库(使用不同的字段和表)。

SELECT n.Date, n.Pct AS Pct10, n.Value AS Pct10Value, q.Pct AS Pct90, q.Value AS Pct90Value FROM (
    SELECT p.Date, p.Pct, p.Value, m.Selector FROM (
        SELECT mydata.Date, Value, COUNT(Value) as Pct, (ABS(0.1-(COUNT(Value)/d.DateTotal))) AS Abs10
        FROM mydata
        INNER JOIN (SELECT Date, COUNT(*) AS DateTotal FROM mydata GROUP BY Date) AS d 
            ON d.Date = mydata.Date
        GROUP BY Date, Value
        ) p
    INNER JOIN (
        SELECT Date, MIN(Abs10) AS Selector FROM (
            SELECT mydata.Date, Value, COUNT(Value) as Pct, (ABS(0.1-(COUNT(Value)/d.DateTotal))) AS Abs10
            FROM mydata
            INNER JOIN (SELECT Date, COUNT(*) AS DateTotal FROM mydata GROUP BY Date) AS d 
            ON d.Date = mydata.Date
            GROUP BY Date, Value
        ) x GROUP BY Date
    ) AS m ON m.Selector = p.Abs10
    GROUP BY p.Date) n
INNER JOIN (
    SELECT p.Date, p.Pct, p.Value, m.Selector FROM (
        SELECT mydata.Date, Value, COUNT(Value) as Pct, (ABS(0.9-(COUNT(Value)/d.DateTotal))) AS Abs90
        FROM mydata
        INNER JOIN (SELECT Date, COUNT(*) AS DateTotal FROM mydata GROUP BY Date) AS d 
            ON d.Date = mydata.Date
        GROUP BY Date, Value
        ) p
    INNER JOIN (
        SELECT Date, MIN(Abs90) AS Selector FROM (
            SELECT mydata.Date, Value, COUNT(Value) as Pct, (ABS(0.9-(COUNT(Value)/d.DateTotal))) AS Abs90
            FROM mydata
            INNER JOIN (SELECT Date, COUNT(*) AS DateTotal FROM mydata GROUP BY Date) AS d 
                ON d.Date = mydata.Date
            GROUP BY Date, Value
        ) x GROUP BY Date
    ) AS m ON m.Selector = p.Abs90
    GROUP BY p.Date) q ON q.Date = n.Date

【讨论】:

  • 我会说这不可能是正确的,因为你有 select count(*) from mydata 没有任何日期限制,但是每个日期都会有另一个计数。
  • @stacktracer 是的,我很确定我没有正确修改其他答案。让我再考虑一下。
  • 似乎不起作用:由于限制 1,查询仅返回一行,而不是每个日期一行。
  • 我已经编辑了答案以包含一个在我自己的数据库上测试时似乎可以工作的版本。
  • 感谢您的努力。运行这个给了我:Date Pct10 Pct10Value Pct90 Pct90Value2013-10-11 8 0.679958057956 8 0.6799580579562013-10-12 8 0.679958057956 8 0.6799580579562013-10-13 9 0.688678662728 9 0.688678662728所以有些东西不起作用,因为从数据来看,90% 和 10% 的百分位数实际上是不同的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-30
  • 1970-01-01
  • 2021-01-06
  • 2011-03-23
  • 2011-07-21
  • 2010-10-31
相关资源
最近更新 更多