【问题标题】:How to perform a sum for all previous records如何对所有以前的记录进行求和
【发布时间】:2022-01-13 18:50:23
【问题描述】:

我一直在尝试实施解决方案here,增加更新现有记录的味道。作为 MRE,我希望用当前行日期与当前行 p1_id 与前一行 p1_id 匹配的每一行日期之间的所有差异的总和填充表中的 sum_date_diff 列或p2_id。我已经在下面填写了预期的结果:

+-----+------------+-------+-------+---------------+
| id_ | date_time  | p1_id | p2_id | sum_date_diff |
+-----+------------+-------+-------+---------------+
|   1 | 2000-01-01 |     1 |     2 | Null          |
|   2 | 2000-01-02 |     2 |     4 | 1             |
|   3 | 2000-01-04 |     1 |     3 | 3             |
|   4 | 2000-01-07 |     2 |     5 | 11            |
|   5 | 2000-01-15 |     2 |     3 | 35            |
|   6 | 2000-01-20 |     1 |     3 | 35            |
|   7 | 2000-01-31 |     1 |     3 | 68            |
+-----+------------+-------+-------+---------------+

到目前为止,我的查询如下所示:

UPDATE test.sum_date_diff AS sdd0
        JOIN
    (SELECT 
        id_,
            SUM(DATEDIFF(sdd1.date_time, sq.date_time)) AS sum_date_diff
    FROM
        test.sum_date_diff AS sdd1
    LEFT OUTER JOIN (SELECT 
        sdd2.date_time AS date_time, sdd2.p1_id AS player_id
    FROM
        test.sum_date_diff AS sdd2 UNION ALL SELECT 
        sdd3.date_time AS date_time, sdd3.p2_id AS player_id
    FROM
        test.sum_date_diff AS sdd3) AS sq ON sq.date_time < sdd1.date_time
        AND sq.player_id = sdd1.p1_id
    GROUP BY sdd1.id_) AS master_sq ON master_sq.id_ = sdd0.id_ 
SET 
    sdd0.sum_date_diff = master_sq.sum_date_diff

这工作如here所示。

但是,在一个有 150 万条记录的表上,查询在过去一小时内一直挂起。即使我在底部添加 WHERE 子句以将更新限制为单个记录,它也会挂起 5 分钟以上。

下面是查询全表的EXPLAIN语句:

+----+-------------+---------------+------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+-------+---------+----------+--------------------------------------------+
| id | select_type |     table     | partitions | type  |                                                              possible_keys                                                              |                   key                   | key_len |  ref  |  rows   | filtered |                   Extra                    |
+----+-------------+---------------+------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+-------+---------+----------+--------------------------------------------+
|  1 | UPDATE      | sum_date_diff | NULL       | const | PRIMARY                                                                                                                                 | PRIMARY                                 | 4       | const |       1 |      100 | NULL                                       |
|  1 | PRIMARY     | <derived2>    | NULL       | ref   | <auto_key0>                                                                                                                             | <auto_key0>                             | 4       | const |      10 |      100 | NULL                                       |
|  2 | DERIVED     | sum_date_diff | NULL       | index | PRIMARY,ix__match_oc_history__date_time,ix__match_oc_history__p1_id,ix__match_oc_history__p2_id,ix__match_oc_history__date_time_players | ix__match_oc_history__date_time_players | 14      | NULL  | 1484288 |      100 | Using index; Using temporary               |
|  2 | DERIVED     | <derived3>    | NULL       | ALL   | NULL                                                                                                                                    | NULL                                    | NULL    | NULL  | 2968576 |      100 | Using where; Using join buffer (hash join) |
|  3 | DERIVED     | sum_date_diff | NULL       | index | NULL                                                                                                                                    | ix__match_oc_history__date_time_players | 14      | NULL  | 1484288 |      100 | Using index                                |
|  4 | UNION       | sum_date_diff | NULL       | index | NULL                                                                                                                                    | ix__match_oc_history__date_time_players | 14      | NULL  | 1484288 |      100 | Using index                                |
+----+-------------+---------------+------------+-------+-----------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+-------+---------+----------+--------------------------------------------+

这是CREATE TABLE 声明:

CREATE TABLE `sum_date_diff` (
  `id_` int NOT NULL AUTO_INCREMENT,
  `date_time` datetime DEFAULT NULL,
  `p1_id` int NOT NULL,
  `p2_id` int NOT NULL,
  `sum_date_diff` int DEFAULT NULL,
  PRIMARY KEY (`id_`),
  KEY `ix__sum_date_diff__date_time` (`date_time`),
  KEY `ix__sum_date_diff__p1_id` (`p1_id`),
  KEY `ix__sum_date_diff__p2_id` (`p2_id`),
  KEY `ix__sum_date_diff__date_time_players` (`date_time`,`p1_id`,`p2_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1822120 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

MySQL 版本是 8.0.26,运行在 2016 MacBook Pro 上,Monterey 和 16Gb RAM。

在阅读了有关提升 MySQL 可用 RAM 的信息后,我在标准 my.cnf 文件中添加了以下内容:

innodb_buffer_pool_size = 8G
tmp_table_size=2G
max_heap_table_size=2G

我想知道是否:

  1. 我做错了什么
  2. 无论我做什么,这都是一项非常缓慢的任务
  3. 有一种更快的方法

希望有人能赐教!

【问题讨论】:

标签: mysql query-optimization mysql-8.0


【解决方案1】:

虽然 可能 在 SQL 中进行这样的计算,但它很混乱。如果行数不是数百万,我会将必要的列提取到我的应用程序中并在那里进行算术运算。 (循环在 PHP/Java/etc 中比在 SQL 中更容易和更快。)

LEAD()LAG() 是可能的,但它们没有得到很好的优化(或者我的经验也是如此)。在 APP 语言中,以数组形式查找内容既简单又高效。

SELECT 可以(轻松高效地)进行任何过滤和排序,以便应用只接收必要的数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    相关资源
    最近更新 更多