查询依赖子查询太慢

时间:2017-09-13 09:33:00

标签: php mysql codeigniter query-performance

select  mt.from_user, mt.to_user, mt.group_id, g.name, g.created_by as adminuser,
        msg.*, 
    (
        SELECT  id
            from  messages
            where  t.thread_id = thread_id
              and  id NOT IN (
                SELECT  message_id  from  message_deleted
                    where  user_id=275  and  status='deleted' )
            order by  CreatedDate DESC
            limit  1
    ) as msgid, 
    (
        SELECT  CreatedDate
            from  messages
            where  t.thread_id = thread_id
              and  id NOT IN (
                SELECT  message_id  from  message_deleted
                    where  user_id=275  and  status='deleted'  )
            order by  CreatedDate DESC
            limit  1
    ) as msgDate
    from  user_thread as t
    left join  message_thread as mt  ON t.thread_id = mt.id
    left join  group_master as g  ON mt.group_id = g.id
    left join  group_member as gm  ON gm.group_id = g.id
    left join  messages as msg  ON t.thread_id = msg.thread_id
    where  (      gm.user_id=275
              or  msg.from_id=275
              or  msg.to_id=275
           )
      and  t.status = 'Active'
    group by  mt.id
    order by  msgDate DESC 

大约需要50秒。

在上面的代码中,我尝试拆分上面的查询并注意下面的子查询需要花费太多时间来执行。我可以将子查询转换为连接。请帮我。我被卡住了。请注意,所有连接的表都是必需的。

    (
        SELECT  id
            from  messages
            where  t.thread_id = thread_id
              and  id NOT IN (
                SELECT  message_id  from  message_deleted
                    where  user_id=275  and  status='deleted' )
            order by  CreatedDate DESC
            limit  1
    ) as msgid, 
    (
        SELECT  CreatedDate
            from  messages
            where  t.thread_id = thread_id
              and  id NOT IN (
                SELECT  message_id  from  message_deleted
                    where  user_id=275  and  status='deleted'  )
            order by  CreatedDate DESC
            limit  1
    ) as msgDate

1 个答案:

答案 0 :(得分:0)

首先,您正在滥用臭名昭着的MySQL扩展GROUP BY。这可能会导致您的结果无法预测。读这个。 https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

其次,您有几个嵌套的从属子查询。第一个就是这个。

(select id 
   from messages
  where t.thread_id = thread_id
    and id NOT IN (select message_id
                     from message_deleted
                    where user_id=275
                      and status='deleted') 
  order by CreatedDate DESC limit 1) as msgid

这种嵌套的从属子查询表现非常糟糕。当它们包含LIMIT条款时,它们会更糟糕。修复此问题的途径是重构为独立查询,然后加入它。

这可以作为查询的替代,以在线程上找到最新的未删除消息。

          SELECT MAX(m.id) id, m.thread_id
            FROM messages m
            LEFT JOIN message_deleted d 
                        ON m.id = d.id
                       AND d.user_id = 275
                       AND d.status = 'deleted'
           WHERE d.id IS NULL
           GROUP BY m.thread_id

这使用LEFT JOIN .... IS NULL模式代替NOT IN。它更快。它使用MAX(id)方法在表中查找最近的行来代替ORDER BY CreatedDate DESC LIMIT 1方法,这也快得多。这很好,因为它保证每thread_id个值生成0或1行。这意味着您可以在LEFT JOIN ... ON ... thread_id操作中使用它,而不是在结果集中添加任何行。

您可以通过运行它来测试此子查询。然后你将它加入到查询的其余部分,就像它是一个表一样,就像这样。

SELECT whatever,
       q.id, r.CreatedDate
  FROM whatever
  LEFT JOIN (
          SELECT MAX(m.id) id, m.thread_id
            FROM messages m
            LEFT JOIN message_deleted d 
                        ON m.id = d.id
                       AND d.user_id = 275
                       AND d.status = 'deleted'
           WHERE d.id IS NULL
           GROUP BY m.thread_id
       ) q ON q.id = t.id
  LEFT JOIN messages r ON r.id = q.id

此处的第二个LEFT JOIN操作用于从消息表中检索最新的未删除消息的CreatedDate值。