提高LEFT JOIN / GROUP BY的性能

时间:2017-01-14 13:09:30

标签: mysql sql

我有两张表snippetsplatforms。每个代码段都属于一个平台(fork_id可以为空并链接到同一个表上的另一个记录)。结构:

PLATFORMS (id, name, slug, syntax)
SNIPPETS (id, platform_id, fork_id, private etc.) 

我现在正在尝试运行查询以获取每个平台的摘要总数。当片段表有一百万条记录时,查询很慢(在10到20秒之间)。

SELECT platforms.id, name, slug, syntax, COUNT(*) AS total FROM platforms 
LEFT JOIN snippets on platforms.id = snippets.platform_id
WHERE fork_id IS NULL
AND private = 0
GROUP BY platforms.id, name
ORDER BY total DESC, name asc;

其他一些信息:

  • snippets.id和platform.id有主键索引。
  • fork_id和platform_id有外键索引。
  • private和platforms.name有索引。

运行EXPLAIN查询可提供以下信息:

Explain query

如何将性能提升到可接受的水平? 谢谢!

2 个答案:

答案 0 :(得分:0)

在MySQL中,使用相关子查询可以更快地进行此类查询:

SELECT p.id, p.name, p.slug, p.syntax,
       (SELECT COUNT(*)
        FROM snippets s
        WHERE p.id = s.platform_id AND
              s.fork_id IS NULL AND
              s.private = 0
       ) AS total
FROM platforms  p
ORDER BY total DESC, name asc;

然后,您需要snippets(platform_id, fork_id, private)上的索引。

我应该注意你的原始查询相当于:

SELECT p.id, p.name, p.slug, p.syntax, COUNT(*) AS total
FROM platforms p JOIN
     snippets s
     on p.id = s.platform_id
WHERE s.fork_id IS NULL AND s.private = 0
GROUP BY p.id, p.name
ORDER BY total DESC, name asc;

因为WHERE子句会将LEFT JOIN变为INNER JOIN。对于此查询,您可以尝试snippets(private, fork_id, platform_id)上的索引。

答案 1 :(得分:0)

我可以看到这里发生的两件事。一个是计数,另一个是platforms表中的详细信息显示。

让我们先计算一下。

                     SELECT platform_id, COUNT(*) snips
                       FROM snippets
                      WHERE fork_id IS NULL
                        AND private = 0
                      GROUP BY platform_id

为了尽可能快地创建,请在private, fork_id, platform_id)表的snippets列上创建一个复合索引。这样内部查询就可以进行所谓的索引扫描,可以阅读。

现在让我们报告详细信息。

       SELECT a.id, a.name, a.slug, a.syntax, b.snips
         FROM platforms a
    LEFT JOIN (
                     SELECT platform_id, COUNT(*) snips
                       FROM snippets
                      WHERE fork_id IS NULL
                        AND private = 0
                      GROUP BY platform_id
              ) b ON a.platform_id = b.platform_id
       ORDER BY b.snips DESC, a.name ASC;

技巧是:在聚合(分组)大表时简化简化。