【问题标题】:MYSQL Query very slow when using ORDER BY datetime使用 ORDER BY datetime 时 MYSQL 查询非常慢
【发布时间】:2017-10-12 00:01:53
【问题描述】:

我有以下在社交网络上运行的查询。该查询从数据库中获取帖子(如 Facebook 帖子)。

SELECT P.*, 
   P.id_post id_p, 
   PM.meta_content video_title, 
   PM2.meta_content video_views, 
   PM3.meta_content racebooking_views, 
   Greatest(P.creation_date, Coalesce(Max(C.date), P.creation_date)) AS 
   last_activity, 
   P.creation_date creation_date, 
   (SELECT Count(*) 
    FROM   likes 
    WHERE  post_id = P.id_post 
           AND post_type = 'P') 
   likes_count, 
   (SELECT Count(*) 
    FROM   likes L 
    WHERE  post_id = P.id_post 
           AND post_type = 'P' 
           AND L.id_profile = 2796) 
   do_i_like 
FROM   posts P 
       LEFT JOIN comments C 
              ON P.id_post = C.post_id 
                 AND C.post_type = 'P' 
                 AND C.id_profile != P.id_profile 
       LEFT JOIN post_meta PM 
              ON PM.id_post = P.id_post 
                 AND PM.meta_type = 'T' 
       LEFT JOIN post_meta PM2 
              ON PM2.id_post = P.id_post 
                 AND PM2.meta_type = 'V' 
       LEFT JOIN post_meta PM3 
              ON PM3.id_post = P.id_post 
                 AND PM3.meta_type = 'W' 
GROUP  BY P.id_post 
ORDER  BY last_activity DESC 
LIMIT  41, 10 

每个帖子可能有也可能没有 cmets。 我希望查询首先获取具有最新活动的帖子。 所以,如果帖子有评论,我会选择最新评论的日期。如果帖子没有评论,我会以帖子的创建日期为准。

这项工作由Greatest(P.creation_date, Coalesce(Max(C.date), P.creation_date)) 完成,它获取 cmets 日期(如果存在 cmets)和帖子创建日期之间的最大价值。

然后,ORDER BY last_activity DESC 进行排序工作。

问题

查询真的很慢。运行需要 8 秒。 posts 表有 8K 行,cmets 表有 8K 行。

我不明白的是,如果我用这个ORDER BY P.id_post 替换 ORDER BY 子句,它需要 0.5 秒才能运行。但是,如果我再次用ORDER BY P.creation_date 替换 ORDER BY 子句,则需要 8 秒。好像不喜欢约会……

其他信息

  • posts 表在 creation_date 上有一个索引。
  • cmets 表在日期上有一个索引
  • 服务器在 CentOS Linux 6.6 上运行 LAMP
  • 我在 SO like this one 上尝试了其他解决方案,但没有奏效

如何修复此查询以更快地运行?

【问题讨论】:

  • 您是否尝试在列creation_date 上创建索引?
  • @YuJiaao 在问题中我写了“posts table has an index on creation_date”。你是这个意思还是别的意思?
  • 您使用 group by last_activity,但 last_activity 是动态生成的,我认为索引在这种情况下没有帮助,也许添加 last_activity 作为帖子表的列?

标签: php mysql date


【解决方案1】:

select 子句中的相关子查询可能会害死你。相反,加入计算喜欢统计信息的子查询:

SELECT P.*, 
    P.id_post id_p, 
    PM.meta_content video_title, 
    PM2.meta_content video_views, 
    PM3.meta_content racebooking_views, 
    GREATEST(P.creation_date, COALESCE(MAX(C.date), P.creation_date)) AS last_activity, 
    P.creation_date creation_date,
    t.likes_count,
    t.do_i_like
FROM posts P
LEFT JOIN
(
    SELECT
        post_id,
        SUM(CASE WHEN post_type = 'P' THEN 1 ELSE 0 END) AS likes_count,
        SUM(CASE WHEN post_type = 'P' AND L.id_profile = 2796
                 THEN 1 ELSE 0 END) AS do_i_like
    FROM likes
    GROUP BY post_id
) t
    ON t.post_id = P.id_post 
LEFT JOIN comments C 
    ON P.id_post = C.post_id AND
       C.post_type = 'P' AND
       C.id_profile != P.id_profile 
LEFT JOIN post_meta PM 
    ON PM.id_post = P.id_post AND
       PM.meta_type = 'T' 
LEFT JOIN post_meta PM2 
    ON PM2.id_post = P.id_post AND
       PM2.meta_type = 'V' 
LEFT JOIN post_meta PM3 
    ON PM3.id_post = P.id_post AND 
       PM3.meta_type = 'W' 
ORDER BY
    last_activity DESC 
LIMIT 41, 10

另外,在编辑您的查询后,我看不到在外部查询中使用 GROUP BY 的理由,因此我将其删除。而且您应该在适当的情况下使用索引,尽管我的直觉是我的建议本身应该会显着提升性能。

【讨论】:

  • COALESCS 轻微错字? => COALESCE ;-)
  • 感谢您的回答!现在我使用了一个可怕的解决方法只是为了让网站正常工作。周二我会测试你的解决方案并给你反馈!
【解决方案2】:

有一个 MAX(C.Date) 需要 group by 子句,但我相信它也可以替换为子查询:

SELECT P.*, 
    P.id_post id_p, 
    PM.meta_content video_title, 
    PM2.meta_content video_views, 
    PM3.meta_content racebooking_views, 
    GREATEST(P.creation_date, COALESCE(max_c_date, P.creation_date)) AS last_activity, 
    P.creation_date creation_date,
    t.likes_count,
    t.do_i_like
FROM posts P
LEFT JOIN
(
    SELECT
        post_id,
        SUM(CASE WHEN post_type = 'P' THEN 1 ELSE 0 END) AS likes_count,
        SUM(CASE WHEN post_type = 'P' AND L.id_profile = 2796
                 THEN 1 ELSE 0 END) AS do_i_like
    FROM likes
    GROUP BY post_id
) t
    ON t.post_id = P.id_post 
LEFT JOIN (
      SELECT
        comments.post_id,
        MAX(comments.date) max_c_date
      FROM comments
      inner join posts ON comments.post_id = posts.id_post
      where  comments.post_type = 'P' AND
             comments.id_profile != posts.id_profile 
      GROUP BY comments.post_id
) C 
    ON P.id_post = C.post_id AND
LEFT JOIN post_meta PM 
    ON PM.id_post = P.id_post AND
       PM.meta_type = 'T' 
LEFT JOIN post_meta PM2 
    ON PM2.id_post = P.id_post AND
       PM2.meta_type = 'V' 
LEFT JOIN post_meta PM3 
    ON PM3.id_post = P.id_post AND 
       PM3.meta_type = 'W' 
ORDER BY
    last_activity DESC 
LIMIT 41, 10

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 2019-09-02
    • 1970-01-01
    相关资源
    最近更新 更多