你将要读到的内容相当俗气,所以不要在家尝试!
在SQL中,你的问题的答案通常是NO,但由于GROUP BY的放松模式(@bluefeet提到过),在MySQL中答案是YES。
假设,你有一个BTREE索引(post_status, post_type, post_author, post_date)。索引在引擎盖下看起来如何?
(post_status='publish', post_type='post', post_author='user A', post_date='2012-12-01')
(post_status='publish', post_type='post', post_author='user A', post_date='2012-12-31')
(post_status='publish', post_type='post', post_author='user B', post_date='2012-10-01')
(post_status='publish', post_type='post', post_author='user B', post_date='2012-12-01')
也就是说,数据是由所有这些字段按升序排序的。
当你在默认情况下执行GROUP BY时,它会根据分组字段(在我们的例子中是post_author;post_status, post_type是WHERE子句所需要的),如果有匹配的索引,它将按升序获取每条第一个记录的数据。也就是说,该查询将获取以下内容(每个用户的第一篇文章):
(post_status='publish', post_type='post', post_author='user A', post_date='2012-12-01')
(post_status='publish', post_type='post', post_author='user B', post_date='2012-10-01')
但是MySQL中的GROUP BY允许显式地指定顺序。当你以降序请求post_user时,它会以相反的顺序遍历我们的索引,仍然取每个组的第一条记录,实际上是最后一条。
这是
...
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC
会给我们
(post_status='publish', post_type='post', post_author='user B', post_date='2012-12-01')
(post_status='publish', post_type='post', post_author='user A', post_date='2012-12-31')
现在,当您按照post_date对分组结果进行排序时,您将得到所需的数据。
SELECT wp_posts.*
FROM wp_posts
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC
ORDER BY wp_posts.post_date DESC;
NB:
对于这个特定的查询,我不建议使用这种方法。在这种情况下,我会使用@bluefeet建议的稍微修改版本。但是这个技巧可能非常有用。看一下我的回答:检索每个组中的最后一条记录
缺陷:这种方法的缺点是
查询的结果取决于索引,这违背了SQL的精神(索引应该只加速查询);
索引不知道它对查询的影响(您或其他人将来可能会发现索引太消耗资源,并以某种方式更改它,破坏查询结果,而不仅仅是它的性能)
如果您不理解查询是如何工作的,很可能在一个月内就会忘记解释,查询将使您和您的同事感到困惑。
其优势在于在困难情况下的性能。在这种情况下,查询的性能应该与@bluefeet的查询相同,因为涉及到排序的数据量(所有数据都加载到一个临时表中,然后排序;顺便说一句,他的查询需要(post_status, post_type, post_author, post_date)索引以及)。
我的建议:
正如我所说,这些查询使MySQL在临时表中浪费时间对潜在的大量数据进行排序。如果需要分页(即涉及到LIMIT),大部分数据甚至会被丢弃。我要做的是最小化排序数据的数量:即对子查询中的数据进行排序和限制,然后连接回整个表。
SELECT *
FROM wp_posts
INNER JOIN
(
SELECT max(post_date) post_date, post_author
FROM wp_posts
WHERE post_status='publish' AND post_type='post'
GROUP BY post_author
ORDER BY post_date DESC
-- LIMIT GOES HERE
) p2 USING (post_author, post_date)
WHERE post_status='publish' AND post_type='post';
同样的查询,使用上面描述的方法:
SELECT *
FROM (
SELECT post_id
FROM wp_posts
WHERE post_status='publish' AND post_type='post'
GROUP BY post_author DESC
ORDER BY post_date DESC
-- LIMIT GOES HERE
) as ids
JOIN wp_posts USING (post_id);
所有这些查询及其在SQLFiddle上的执行计划。