【问题标题】:Generic greatest N per group query is too slow每组查询的通用最大 N 太慢
【发布时间】:2015-05-13 12:10:13
【问题描述】:

以下查询需要 18 分钟才能完成。如何优化它以更快地执行?

基本上,我对每个公民的查询从citizens_staticcitizens_dynamic 表中加入行,其中update_id_to 列最高。

INSERT INTO latest_tmp (...)

SELECT cs1.*, cd1.*
FROM citizens c

JOIN citizens_static  cs1 ON c.id = cs1.citizen_id
JOIN citizens_dynamic cd1 ON c.id = cd1.citizen_id

JOIN (
    SELECT citizen_id, MAX(update_id_to) AS update_id_to
    FROM citizens_static
    GROUP BY citizen_id
) AS cs2 ON c.id = cs2.citizen_id AND cs1.update_id_to = cs2.update_id_to

JOIN (
    SELECT citizen_id, MAX(update_id_to) AS update_id_to
    FROM citizens_dynamic
    GROUP BY citizen_id
) cd2 ON c.id = cd2.citizen_id AND cd1.update_id_to = cd2.update_id_to;

latest_tmp 表是 MyISAM 表,在导入期间禁用了索引。禁用它们将执行时间从 20 分钟缩短到 18 分钟,因此这不是最大的问题。

我还用WHERE t2.column IS NULLLEFT JOIN 方法进行了基准测试。与我使用的 INNER JOIN 方法相比,它需要几个小时。

解释下面的查询输出。它似乎正在使用索引。

citizens_dynamiccitizens_staticcitizen_id,update_id_to 上具有主键,在 update_id_to,citizen_id 列上具有名为“id”的辅助键。

【问题讨论】:

    标签: mysql query-optimization greatest-n-per-group


    【解决方案1】:

    你能用英语解释一下你想要什么吗?

    然后查看Groupwise Max 并根据需要编辑以下内容:

    SELECT
            province, n, city, population
        FROM
          ( SELECT  @prev := '', @n := 0 ) init
        JOIN
          ( SELECT  @n := if(province != @prev, 1, @n + 1) AS n,
                    @prev := province,
                    province, city, population
                FROM  Canada
                ORDER BY
                    province,
                    population DESC
          ) x
        WHERE  n <= 3
        ORDER BY  province, n;
    

    不管内部 ORDER BY 上的 ASC/DESC 是什么,都会进行全表扫描和“文件排序”。

    【讨论】:

    • 如果[province ASC, population DESC] 上有方向索引,你的答案会不会更快?我知道 MySQL 不允许有这样的索引,但是有额外的列 population_negative 应该是可能的。我的问题是不相关的子查询非常快(7M 输出行,大约 40M 输入行),但不够快。
    • 啊,这可能是部分优化。在编辑中查看粗体。
    • 不,因为需要扫描所有行,所以执行全表扫描比文件排序更有效。
    • 总的“努力”大约是3N,其中N是表中的行数。
    【解决方案2】:

    我对 MySQL 不够熟悉,无法预测它是否会运行得更好,但我建议尝试一下:

    SELECT cs1.*, cd1.*
    FROM citizens c
    
    JOIN citizens_static  cs1 ON c.id = cs1.citizen_id
    AND NOT EXISTS ( SELECT *
                       FROM citizens_static cs2
                      WHERE cs2.citizen_id = cs1.citizen_id
                        AND cs2.update_id > cs1.update_id )
    
    JOIN citizens_dynamic cd1 ON c.id = cd1.citizen_id
    AND NOT EXISTS ( SELECT *
                       FROM citizens_dynamic cd2
                      WHERE cd2.citizen_id = cd1.citizen_id
                        AND cd2.update_id > cd1.update_id )
    

    PS:请评论运行时间(如果它在一小时内返回=),这样我可能会学习(不)将来再次提出这种结构。

    【讨论】:

    • 15s 限制 10k,38s 限制 50k,62s 限制 100k。当我关闭限制时,它不会在 30 分钟内完成,所以我想它需要的时间比我的解决方案要长得多。
    • 好的,感谢您的尝试。那不是一个选择。另一个学到的东西 =) 只是大声思考:假设 update_id 保证 是完全连续的,你可以用 AND c*2.update_id = c*1.update_id + 1 替换 AND c*2.update_id &gt; c*1.update_id 但是当有一个“洞”时这会搞砸结果' 在 update_id 中。值得一试只是为了好玩,但实际上这可能是一场等待发生的意外。
    猜你喜欢
    • 2018-12-12
    • 2013-11-04
    • 2011-12-26
    • 2020-01-03
    • 2022-01-06
    • 2013-04-16
    • 2017-03-02
    • 2013-04-04
    • 2014-01-27
    相关资源
    最近更新 更多