使用子查询改进 MySql 查询左外连接

Posted

技术标签:

【中文标题】使用子查询改进 MySql 查询左外连接【英文标题】:Improve MySql query left outer joins with subquery 【发布时间】:2018-04-27 12:01:16 【问题描述】:

我们正在维护内容的历史记录。我们要获取每个内容的更新条目,其中创建时间和更新时间应该是内容的第一个条目。该查询包含多个选择和具有如此多左连接的 where 子句。数据集非常庞大,因此执行查询需要 60 多秒。请帮助改进。查询:

select * from (select * from (

    SELECT c.*, initCMS.initcreatetime, initCMS.initupdatetime, user.name as partnerName, r.name as rightsName, r1.name as copyRightsName, a.name as agelimitName, ct.type as contenttypename, cat.name as categoryname, lang.name as languagename FROM ContentCMS c 

        left join ContentCategoryType ct on ct.id = c.contentType 
        left join User user on c.contentPartnerId = user.id 
        left join Category cat on cat.id = c.categoryId 
        left join Language lang on lang.id = c.languageCode 
        left join CopyRights r on c.rights = r.id 
        left join CopyRights r1 on c.copyrights = r1.id 
        left join Age a on c.ageLimit = a.id 
        left outer join (

            SELECT contentId, createTime as initcreatetime, updateTime as initupdatetime from ContentCMS cms where cms.deleted='0'

        ) as initCMS on initCMS.contentId = c.contentId WHERE c.deleted='0' order by c.id  DESC

) as temp group by contentId) as c where c.editedBy='0'

任何帮助将不胜感激。谢谢。

【问题讨论】:

嗨。请合理地格式化您的代码 - 请参阅编辑框下方的格式化帖子。 minimal reproducible example 请。阅读查询优化简介——约束、索引和计划很重要,但您没有提供任何信息。 【参考方案1】:

只是部分评估和建议,因为您的查询似乎格式不正确

这个左连接似乎没用

    FROM ContentCMS c 
    ......
    left join (
        SELECT contentId
            , createTime as initcreatetime
            , updateTime as initupdatetime 
        from ContentCMS cms 
        where cms.deleted='0'
    ) as initCMS on initCMS.contentId = c.contentId 

同一张桌子

join 子查询中的 order by(无限制)是无用的,因为 join 有序值或无序值会产生相同的结果

group by contentId 很奇怪,因为没有聚合函数,并且不推荐使用没有聚合函数的 group by 是 sql 并且在 mysql 的最新版本中不允许(默认)如果您需要不同的值或每个 contentId 只需要一行,您应该使用不同的或以不随意的方式检索值(使用 group by 而不使用聚合函数 retrive非聚合列的临时值。

对于部分评估,您的查询应重构为

SELECT c.*
          , c.initcreatetime
          , c.initupdatetime
          , user.name as partnerName
          , r.name as rightsName
          , r1.name as copyRightsName
          , a.name as agelimitName
          , ct.type as contenttypename
          , cat.name as categoryname
          , lang.name as languagename 
      FROM ContentCMS c 
      left join ContentCategoryType ct on ct.id = c.contentType 
      left join User user on c.contentPartnerId = user.id 
      left join Category cat on cat.id = c.categoryId 
      left join Language lang on lang.id = c.languageCode 
      left join CopyRights r on c.rights = r.id 
      left join CopyRights r1 on c.copyrights = r1.id 
      WHERE c.deleted='0' 
) as temp 

对于其余部分,您应该明确选择您有效需要的列,为其他列添加适当的聚合函数

此外,嵌套子查询仅用于不恰当地减少行数并无助于提高性能……您还应该重新评估您的数据建模和设计。

【讨论】:

以上是关于使用子查询改进 MySql 查询左外连接的主要内容,如果未能解决你的问题,请参考以下文章

与子查询相比,为啥左外连接查询给出不同的结果?

带有子查询的左外连接?

对两个 MySQL 查询执行左外连接?

分页结果集中的选择子查询或左外连接哪个更快

MySql - 在查询中连接左外连接

真正的左外连接