【问题标题】:Why does sql query not output left join data in order by timestamp?为什么sql查询不按时间戳顺序输出左连接数据?
【发布时间】:2016-11-10 09:14:16
【问题描述】:

我已经在自定义论坛上工作了一段时间。我想显示论坛上最近的帖子。我在我的 Ubuntu 服务器上使用 PHP 7.0.8 和 MySQL 5.7.16。

收集论坛数据的表格有两个。 uap4_forum_posts 和 uap4_forum_post_replies

主题保存到 uap4_forum_posts。 主题回复保存到 uap4_forum_post_replies。

目标是获取所有新主题和所有主题回复(如果有)并按时间戳对其进行排序。按日期排序主题没有任何问题,但是当我添加主题回复时,它只使用第一个主题回复。因此,当有多个主题回复时,它不会显示最新的。

我的数据库设置如下: uap4_forum_posts uap4_forum_post_replies

这是我目前的 MySQL 查询:

SELECT sub.* FROM (
    SELECT
        fp.forum_post_id as forum_post_id, fp.forum_id as forum_id,
        fp.forum_user_id as forum_user_id, fp.forum_title as forum_title,
        fp.forum_edit_date as forum_edit_date,
        fp.forum_timestamp as forum_timestamp, fpr.id as id,
        fpr.fpr_post_id as fpr_post_id, fpr.fpr_id as fpr_id,
        fpr.fpr_user_id as fpr_user_id, fpr.fpr_title as fpr_title,
        fpr.fpr_edit_date as fpr_edit_date,
        fpr.fpr_timestamp as fpr_timestamp,
        GREATEST(fp.forum_timestamp, COALESCE(fpr.fpr_timestamp, '00-00-00 00:00:00')) AS tstamp
        FROM uap4_forum_posts fp
        LEFT JOIN uap4_forum_post_replies fpr
        ON fp.forum_post_id = fpr.fpr_post_id
        WHERE fp.allow = 'TRUE'
) sub
GROUP BY forum_post_id
ORDER BY tstamp DESC

以下是上述查询的结果:

2016-11-08 13:36:46 应该是上面数据的最新时间戳。

感谢您提供的任何帮助。这是我一直在从事的一个免费开源项目。

【问题讨论】:

    标签: mysql sql apache


    【解决方案1】:

    这个问题已经被问过很多次了。这是一个常见的“陷阱”。

    请注意,ORDER BY 操作发生在GROUP BY 操作之后。

    MySQL 以外的数据库会在查询中引发错误,类似于“SELECT 列表中的非聚合表达式不在 GROUP BY 子句中”。

    一个非标准的 MySQL 扩展允许您的查询运行,但 SELECT 列表中某些表达式的返回值是不确定的。这些值将来自组中的某个行,但不能保证它是最新的、最早的、最低的,或者来自组中的任何特定行。

    参考:https://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html

    您的查询返回的结果与记录的行为一致。


    这个问题已经被问过很多次了。我推荐一个 SO 搜索

     [mysql] group by wrong results
    

    跟进

    如果(fpr_post_id,fpr_timestamp) 元组在usp4_forum_post_replies 中是唯一的 ...

    我们可以通过这样的查询获得每个fpr_post_id 的“最新”fpr_timestamp:

      SELECT mr.fpr_post_id
           , MAX(mr.fpr_timestamp) AS max_fpr_timestamp
        FROM uap4_forum_post_replies mr
       GROUP BY mr.fpr_post_id
    

    (对于一个非平凡的表,为了获得最佳性能,我们希望有一个合适的索引可用,例如

    ... ON uap4_forum_post_replies (fpr_post_id,fpr_timestamp)
    

    (以这两列作为前导列的任何索引,按此顺序。)


    然后我们可以在另一个查询中将该查询用作内联视图,作为行源。例如:

      SELECT fp.forum_post_id     AS forum_post_id
           , fp.forum_id          AS forum_id
           , fp.forum_user_id     AS forum_user_id
           , fp.forum_title       AS forum_title
           , fp.forum_edit_date   AS forum_edit_date
           , fp.forum_timestamp   AS forum_timestamp
           , fpr.id               AS id
           , fpr.fpr_post_id      AS fpr_post_id
           , fpr.fpr_id           AS fpr_id
           , fpr.fpr_user_id      AS fpr_user_id
           , fpr.fpr_title        AS fpr_title
           , fpr.fpr_edit_date    AS fpr_edit_date
           , fpr.fpr_timestamp    AS fpr_timestamp
           , GREATEST(fp.forum_timestamp,COALESCE(fpr.fpr_timestamp,'0000-00-00 00:00:00')) 
                                      AS tstamp
        FROM uap4_forum_posts fp
        LEFT
        JOIN ( -- latest fpr_timestamp for each post
               SELECT mr.fpr_post_id
                    , MAX(mr.fpr_timestamp) AS max_fpr_timestamp
                 FROM uap4_forum_post_replies mr
                GROUP BY mr.fpr_post_id
             ) lr
        LEFT
        JOIN uap4_forum_post_replies fpr
          ON fpr.fpr_post_id = lr.fpr_post_id
         AND fpr.fpr_timestamp = lr.max_fpr_timestamp
       WHERE fp.allow = 'TRUE'
       ORDER BY tstamp DESC
    

    有了这个,我们使用最新的fpr_timestamp 只检索与fpr_timestamp 匹配的行,对于每个fpr_post_id。

    请注意,如果给定的 fpr_post_id 存在重复的 fpr_timestamp 值,则可能会返回多行。如果不能保证是唯一的,我们可以扩展该方法。假设id 是唯一的,我们可以通过这样的查询获得具有相同最新fpr_timestamp 的行中的最大id:

      SELECT xr.fpr_post_id
           , MAX(xr.id) AS max_id
        FROM ( SELECT mr.fpr_post_id
                    , MAX(mr.fpr_timestamp) AS max_fpr_timestamp
                 FROM uap4_forum_post_replies mr
                GROUP BY mr.fpr_post_id
             ) lr
        JOIN uap4_forum_post_replies xr
          ON xr.fpr_post_id = lr.fpr_post_id
         AND xr.fpr_timestamp = lr.max_fpr_timestamp
    

    然后在外部查询中,我们可以在id 列上加入uap4_forum_post_replies,以检索具有该id 值的行。

    这只是一个例子。还有其他查询模式可以达到相同的结果。

    【讨论】:

      【解决方案2】:

      试试这个

          SELECT
              fp.forum_post_id as forum_post_id, fp.forum_id as forum_id,
              fp.forum_user_id as forum_user_id, fp.forum_title as forum_title,
              fp.forum_edit_date as forum_edit_date,
              fp.forum_timestamp as forum_timestamp, fpr.id as id,
              fpr.fpr_post_id as fpr_post_id, fpr.fpr_id as fpr_id,
              fpr.fpr_user_id as fpr_user_id, fpr.fpr_title as fpr_title,
              fpr.fpr_edit_date as fpr_edit_date,
              fpr.fpr_timestamp as fpr_timestamp,
              GREATEST(fp.forum_timestamp, COALESCE(fpr.fpr_timestamp, '00-00-00 00:00:00')) AS tstamp
              FROM uap4_forum_posts fp
              LEFT JOIN uap4_forum_post_replies fpr
              ON fp.forum_post_id = fpr.fpr_post_id
              WHERE fp.allow = 'TRUE'
      ORDER BY GREATEST(fp.forum_timestamp, COALESCE(fpr.fpr_timestamp, '00-00-00 00:00:00')) DESC
      

      【讨论】:

      • 为什么 OP 想要“试试这个”? “这个”会有什么不同? (在我看来,这个答案错过了标记,回答了被问到的问题......“为什么......不是......按时间戳排序”。(这似乎是一个建议的解决方案,如何“按时间戳顺序”返回行,但该部门似乎也错过了。)
      • 试过了,结果和我按功能删除分组时一样。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-25
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多