【问题标题】:Can I optimise this MySQL query?我可以优化这个 MySQL 查询吗?
【发布时间】:2014-04-04 22:37:44
【问题描述】:

我的 SQL 是

SELECT authors.*, COUNT(*) FROM authors
    INNER JOIN resources_authors ON authors.author_id=resources_authors.author_id
WHERE
    resource_id IN
        (SELECT resource_id FROM resources_authors WHERE author_id = '1313')
    AND authors.author_id != '1313'
 GROUP BY authors.author_id`

我对查询中的所有字段都有索引,但我仍然得到Using temporary; Using Filesort

id  select_type         table             type            possible_keys                         key      key_len   ref                rows   Extra
1   PRIMARY             authors           ALL             PRIMARY                               NULL        NULL   NULL               16025  Using where; Using temporary; Using filesort
1   PRIMARY             resources_authors ref             author_id                             author_id      4   authors.author_id      3  Using where
2   DEPENDENT SUBQUERY  resources_authors unique_subquery resource_id,author_id,resource_id_2   resource_id  156   func,const             1  Using index; Using where

如何改进我的查询或表结构以加快查询速度?

这里有一个 SQL Fiddle,如果你想试验一下:http://sqlfiddle.com/#!2/96d57/2/0

【问题讨论】:

  • 我认为author_id != 1313不能有效利用索引。
  • @MKhalidJunaid - 这行不通,因为永远不会返回任何结果。
  • 为什么说id必须是1313,然后说不是=1313? ...基本上你是说在资源作者是 1313 而作者不是 1313 的地方拉出来
  • @JohnRuddell - 这是书籍和作者之间的多对多关系(所以每本书可以有很多作者,每个作者可能写很多书)。子查询按作者 1313 查找所有书籍。主查询查找所有(共同)编写这些书籍的作者,但不包括原作者,因为他不需要。
  • 好的,感谢您的澄清。

标签: mysql join query-optimization


【解决方案1】:

我会通过“PreQuery”以不同的方式处理它。获取与另一位作者有共同资源计数的所有作者的列表,但不将原始作者包括在最终列表中。一旦确定了这些作者,获取他们的姓名/联系信息和公共资源的总数,但不是公共的特定资源。那将是一个稍微不同的查询。

现在,查询。为了帮助优化查询,我将有两个索引 仅在(author_id)上的一个 (resource_id, author_id) 上的另一种组合 你已经拥有了。

现在解释内部查询。首先自己做这部分,你可以看到执行计划将使用索引。这里的意图是,查询从资源作者开始,但只关心一个特定的作者(where 子句),这将使这个结果集非常短。那是立即再次加入资源作者表,但仅基于相同的 RESOURCE 并且作者不是主要的(来自 where 子句)只给您那些其他作者。通过添加 COUNT(),我们现在可以确定每个报价有多少具有公共资源,按作者分组,每个作者返回一个条目。最后取那个“PreQuery”结果集(上面已经预先限定的所有记录),并加入作者。获取详细信息并计数()并完成。

SELECT 
      A.*,
      PreQuery.CommonResources
   from 
      ( SELECT 
              ra2.author_id,
              COUNT(*) as CommonResources
           FROM 
              resources_authors ra1
                 JOIN resources_authors ra2
                    ON ra1.resource_id = ra2.resource_id
                    AND NOT ra1.author_id = ra2.author_id
           WHERE 
              ra1.author_id = 1313 
           GROUP BY
              ra2.author_id ) PreQuery
      JOIN authors A
         ON PreQuery.author_id = A.author_id

【讨论】:

  • 太棒了。在我的真实数据上,查询时间从 0.55s 减少到 0.01s。我仍然对查询有所了解,这对我正在尝试优化的其他查询有影响,但如果我理解正确,关键步骤是确保我的 WHERE 子句使用主表中的字段,而不是连接的表,从而消除表扫描。
  • @MarkBarnes,是的,我从您查询的基线开始。一个作者的东西(或者如果你关心多个报价,则使用多个,使用 IN(ID 列表))。然后,通过在这些相同的资源上添加连接可以给其他人(包括原作者),但原作者上的 NOT 将它们排除在最终集合之外。
  • @MarkBanes,顺便说一句,如果您还有其他疑问,请把它们扔在这里,也可以打我看看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 2019-02-24
  • 2018-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多