【问题标题】:MySQL 8 Calculating Average by Partitioning By DateMySQL 8 按日期分区计算平均值
【发布时间】:2020-07-24 21:50:45
【问题描述】:

我在这里设置了一个小提琴:https://www.db-fiddle.com/f/snDGExYZgoYASvWkDGHKDC/2

还有:

架构:

CREATE TABLE `scores` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `shift_id` int unsigned NOT NULL,
  `employee_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `score` double(8,2) unsigned NOT NULL,
  `created_at` timestamp NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO scores(shift_id, employee_name, score, created_at) VALUES
(1, "John",   6.72, "2020-04-01 00:00:00"),
(1, "Bob",   15.71, "2020-04-01 00:00:00"),
(1, "Bob",   54.02, "2020-04-01 08:00:00"),
(1, "John",  23.55, "2020-04-01 13:00:00"),
(2, "John",   9.13, "2020-04-02 00:00:00"),
(2, "Bob",   44.76, "2020-04-02 00:00:00"),
(2, "Bob",   33.40, "2020-04-02 08:00:00"),
(2, "James", 20,    "2020-04-02 00:00:00"),
(3, "John",  20,    "2020-04-02 00:00:00"),
(3, "Bob",   20,    "2020-04-02 00:00:00"),
(3, "Bob",   30,    "2020-04-02 08:00:00"),
(3, "James", 10,    "2020-04-02 00:00:00")

查询 1:

-- This doesn't work

SELECT
    employee_name,
    DATE_FORMAT(created_at, '%Y-%m-%d') AS `date`,
    ANY_VALUE(AVG(score) OVER(PARTITION BY(ANY_VALUE(created_at)))) AS `average_score`
FROM
  scores
GROUP BY
    employee_name, date;

查询 2:

SELECT
    employee_name,
    DATE_FORMAT(created_at, '%Y-%m-%d') AS `date`,
    ANY_VALUE(AVG(score)) AS `average_score`
FROM
  scores
GROUP BY
    employee_name, date;

查询 3:

-- This works but scales very poorly with millions of rows

SELECT
    t1.employee_name,
    ANY_VALUE(DATE_FORMAT(t1.created_at, '%Y-%m-%d')) AS `date`,
    ANY_VALUE(SUM(t1.score) / (
      SELECT SUM(t2.score)
      FROM scores t2
      WHERE date(t2.created_at) = date(t1.created_at)
    ) * 100) AS `average_score`
FROM
  scores t1
GROUP BY
    t1.employee_name, date;

第三个查询正确执行,但在我的测试中,当扩展到数百万行时,速度非常慢。我认为这是因为它是一个相关的子查询并且运行了数百万次。

前两次尝试是我尝试创建使用 MySQL 8 窗口函数来分区平均计算。然而,这些都产生了意想不到的结果。给定日期的average_scores 总数应为 100,就像在第三个查询中一样。

有谁知道更有效的计算方法吗?

还值得注意的是,实际上,查询中还会有一个WHERE IN,以按特定的shift_ids 进行过滤。给出的shift_ids 的数量可能有数十万,最高可达一百万。

正在考虑的另一件事是 ElasticSearch。是否有助于以更快的方式计算这些?

【问题讨论】:

  • 请向我们展示您期望的结果。
  • @GMB 查询三给出了正确的结果,并显示在小提琴中。但它根本不能很好地扩展。

标签: mysql aggregate-functions window-functions mysql-8.0


【解决方案1】:

您可以使用窗口函数。诀窍是获取每个员工每天的总分的窗口总和,如下所示:

select
    employee_name,
    date(created_at) created_date,
    100 * sum(score) / sum(sum(score)) over(partition by date(created_at)) monthly_score
from scores
group by employee_name, date(created_at)

your DB Fiddle 中,这会产生:

| employee_name | created_date | monthly_score |
| ------------- | ------------ | ------------- |
| John          | 2020-04-01   | 30.27         |
| Bob           | 2020-04-01   | 69.73         |
| John          | 2020-04-02   | 15.55342      |
| Bob           | 2020-04-02   | 68.42864      |
| James         | 2020-04-02   | 16.01794      |

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-04
    • 2014-12-28
    • 1970-01-01
    • 2010-10-30
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多