php mysql Group By获取最新记录,而不是第一条记录

Posted

技术标签:

【中文标题】php mysql Group By获取最新记录,而不是第一条记录【英文标题】:php mysql Group By to get latest record, not first record 【发布时间】:2012-11-19 18:04:10 【问题描述】:

桌子:

(`post_id`, `forum_id`, `topic_id`, `post_time`) 
(79, 8, 4, '2012-11-19 06:58:08');
(80, 3, 3, '2012-11-19 06:58:42'),
(81, 9, 9, '2012-11-19 06:59:04'),
(82, 11, 6, '2012-11-19 16:05:39'),
(83, 9, 9, '2012-11-19 16:07:46'),
(84, 9, 11, '2012-11-19 16:09:33'),

查询:

SELECT  post_id, forum_id, topic_id FROM posts 
GROUP BY topic_id 
ORDER BY post_time DESC
LIMIT 5

结果:

[0] => [post_id] => 84 [forum_id] => 9 [topic_id] => 11  
[1] => [post_id] => 82 [forum_id] => 11 [topic_id] => 6  
[2] => [post_id] => 81 [forum_id] => 9 [topic_id] => 9  
[3] => [post_id] => 80 [forum_id] => 3 [topic_id] => 3  
[4] => [post_id] => 79 [forum_id] => 8 [topic_id] => 4

问题:

如何重写查询,使其返回 post_id -> 83 而不是 post_id -> 81 ?

他们都有相同的论坛和主题 ID,但 post_id -> 81 的日期比 post_id -> 83 的日期更早。

但 Group By 似乎获得了“第一条”记录,而不是“最新”记录。

我尝试将查询更改为

SELECT  post_id, forum_id, topic_id, MAX(post_time)

但返回 post_id 81 和 83

【问题讨论】:

【参考方案1】:

如果您选择未在 group 子句中使用且不是聚合的属性,则结果未指定。 您不知道其他属性是从哪些行中选择的。 (sql标准不允许这样的查询,但是mysql比较宽松)。

然后应该编写查询,例如作为

SELECT post_id, forum_id, topic_id
FROM posts p
WHERE post_time =
  (SELECT max(post_time) FROM posts p2
   WHERE p2.topic_id = p.topic_id
   AND p2.forum_id = p.forum_id)
GROUP BY forum_id, topic_id, post_id
ORDER BY post_time DESC
LIMIT 5;

SELECT post_id, forum_id, topic_id FROM posts
NATURAL JOIN
(SELECT forum_id, topic_id, max(post_time) AS post_time
 FROM posts
 GROUP BY forum_id, topic_id) p
ORDER BY post_time
LIMIT 5;

【讨论】:

我使用了你的第一个查询,但在我改变之前它不会工作...... WHERE post_time = ... TO ... WHERE post_time IN ... 因为我得到了更多的结果。 为什么这么麻烦?使用 SQL 将只是 SELECT * FROM [table] GROUP BY [column] 选择最新的而不是最旧的,看起来合乎逻辑吧? 还有添加计数的方法。【参考方案2】:

它不是很漂亮,但它很有效:

SELECT * FROM (SELECT  post_id, forum_id, topic_id FROM posts
ORDER BY post_time DESC) as temp
GROUP BY topic_id

【讨论】:

这会删除重复项,但结果按主题 id asc 的顺序排列 将此添加到您的查询中使其工作:ORDER BY post_time DESC LIMIT 5【参考方案3】:

尝试类似的东西

SELECT post_id, forum_id, topic_id 
FROM   (SELECT post_id, forum_id, topic_id
        FROM posts
        ORDER BY post_time DESC) 
GROUP BY topic_id 
ORDER BY topic_id desc
LIMIT 0,5

根据需要更改 order bylimit

【讨论】:

结果是一个空数组。 我稍微编辑了查询。我不明白为什么它不起作用。我没有尝试过,但这是一种常见的技术。即嵌套查询。假设内部查询返回正确的结果,外部查询应该给出一个结果。 如果要调试,请先运行内部查询。 试过你的编辑,仍然返回一个空数组。内部查询返回单个结果,即最近的帖子。 就性能而言,这似乎比公认的选项更好。我们有一个大表,在条件下使用子查询大约需要 45 秒,而此方法运行时间不到一秒。【参考方案4】:

也许不是最好的方法,但有时函数 group_concat() 可以是全用户的,它会返回一个所有聚合值的字符串,这些值按你想要的方式排序并用逗号分隔(耦合值是用空格隔开)。然后我使用函数 SPLIT_STRING() 来剪切字符串中的第一个 id。

SELECT  
post_id, 
SPLIT_STRING( group_concat( forum_id, post_time ORDER BY post_time DESC ) ,' ',1 )as forum_id, 
SPLIT_STRING( group_concat( topic_id, post_time ORDER BY post_time DESC ) ,' ',1 )as topic_id ,
FROM posts 
GROUP BY topic_id 
ORDER BY post_time DESC
LIMIT 5

所以聚合的 forum_id, post_time 将是这样的:

81 2012-11-19 06:59:04,83 2012-11-19 16:07:46

所以你需要使用整数和日期时间对的字符串表示,每对用逗号分隔,所以我使用这个函数来获取第一个 INT:

CREATE FUNCTION SPLIT_STRING(str VARCHAR(255), delim VARCHAR(12), pos INT)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(str, delim, pos),
       LENGTH(SUBSTRING_INDEX(str, delim, pos-1)) + 1),
       delim, '');

注意:函数 SPLIT_STRING(str, delim, pos) 在这里找到:Equivalent of explode() to work with strings in MySQL

【讨论】:

【参考方案5】:

这也适合你。

SELECT *
FROM (
  SELECT post_id, forum_id, topic_id FROM posts
  ORDER BY post_time DESC
  LIMIT 5
) customeTable
GROUP BY topic_id

【讨论】:

以上是关于php mysql Group By获取最新记录,而不是第一条记录的主要内容,如果未能解决你的问题,请参考以下文章

在 mysql 中使用 group by 查询和 order by 查询选择

获取group by platform和semver的两条最新记录

在 sqlalchemy 中使用 distinct()/group_by() 获取基于每个“名称”列的最新记录

具有最新记录的 Oracle 查询 GROUP BY [重复]

SQL - 使用 GROUP BY 获取子查询子集中或连接中的最新记录

MySQL Query - 使用 group-by 时获取丢失的记录