MySQL按前面的顺序排列

在这里可以找到很多类似的问题,但是我不认为这个问题有足够的答案。

我会继续从当前最流行的问题,并使用他们的例子,如果没关系。

在这个例子中,任务是获取数据库中每个作者的最新帖子。

示例查询产生不可用的结果,因为它并不总是返回的最新帖子。

SELECT wp_posts.* FROM wp_posts WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post' GROUP BY wp_posts.post_author ORDER BY wp_posts.post_date DESC 

目前接受的答案是

 SELECT wp_posts.* FROM wp_posts WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post' GROUP BY wp_posts.post_author HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR ORDER BY wp_posts.post_date DESC 

不幸的是,这个答案是简单明了的错误,在许多情况下,产生的结果比原始查询的结果要少。

我最好的解决方案是使用表单的子查询

 SELECT wp_posts.* FROM ( SELECT * FROM wp_posts ORDER BY wp_posts.post_date DESC ) AS wp_posts WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post' GROUP BY wp_posts.post_author 

那么我的问题是一个简单的问题: 无论如何,无需诉诸子查询就可以在分组之前对行进行排序?

编辑 :这个问题是从另一个问题的延续,我的情况细节略有不同。 你可以(也应该)假定还有一个wp_posts.id是该特定帖子的唯一标识符。

在子查询中使用ORDER BY不是解决此问题的最佳方法。

获得作者max(post_date)的最佳解决方案是使用子查询来返回最大日期,然后将它加入post_author和max日期的表中。

解决方案应该是:

 SELECT p1.* FROM wp_posts p1 INNER JOIN ( SELECT max(post_date) MaxPostDate, post_author FROM wp_posts WHERE post_status='publish' AND post_type='post' GROUP BY post_author ) p2 ON p1.post_author = p2.post_author AND p1.post_date = p2.MaxPostDate WHERE p1.post_status='publish' AND p1.post_type='post' order by p1.post_date desc 

如果您有以下示例数据:

 CREATE TABLE wp_posts (`id` int, `title` varchar(6), `post_date` datetime, `post_author` varchar(3)) ; INSERT INTO wp_posts (`id`, `title`, `post_date`, `post_author`) VALUES (1, 'Title1', '2013-01-01 00:00:00', 'Jim'), (2, 'Title2', '2013-02-01 00:00:00', 'Jim') ; 

子查询将返回最大日期和作者:

 MaxPostDate | Author 2/1/2013 | Jim 

那么既然你加入了这个表格,那么你将返回这个帖子的全部细节。

看演示与SQL小提琴 。

为了扩大我对使用子查询准确地返回这些数据的意见。

MySQL并不强迫你将GROUP BY包含在SELECT列表中。 因此,如果只有GROUP BY一列,但总共返回10列,则不能保证属于返回的post_author的其他列值。 如果该列不在GROUP BY MySQL选择应返回的值。

使用具有聚合函数的子查询将保证每次都返回正确的作者和帖子。

值得一提的是,虽然MySQL允许你在子查询中使用ORDER BY ,并允许你将GROUP BYSELECT列表中的每一列,但是在包括SQL Server在内的其他数据库中不允许这种行为。

您的解决方案使用了GROUP BY子句的扩展 ,允许按某些字段进行分组(在本例中,只是post_author ):

 GROUP BY wp_posts.post_author 

并选择nonaggregated列:

 SELECT wp_posts.* 

没有在group by子句中列出,或者在聚合函数(MIN,MAX,COUNT等)中没有使用。

正确使用扩展到GROUP BY子句

当非聚合列的所有值对于每一行都相等时,这非常有用。

例如,假设你有一张桌子GardensFlowers (花园的name ,在花园里生长的flower ):

 INSERT INTO GardensFlowers VALUES ('Central Park', 'Magnolia'), ('Hyde Park', 'Tulip'), ('Gardens By The Bay', 'Peony'), ('Gardens By The Bay', 'Cherry Blossom'); 

你想要提取花园里生长的所有花朵,那里有多朵花朵。 然后你必须使用一个子查询,例如你可以使用这个:

 SELECT GardensFlowers.* FROM GardensFlowers WHERE name IN (SELECT name FROM GardensFlowers GROUP BY name HAVING COUNT(DISTINCT flower)>1); 

如果你需要提取花朵中所有花朵,那么你可以将HAVING条件改为HAVING COUNT(DISTINCT flower)=1 ,但是MySql也允许你使用这个:

 SELECT GardensFlowers.* FROM GardensFlowers GROUP BY name HAVING COUNT(DISTINCT flower)=1; 

没有子查询,没有标准的SQL,但更简单。

对GROUP BY子句使用扩展名不正确

但是如果你选择非聚合的列对于每一行都是不相等的呢? MySql为该列选择的值是什么?

它看起来像MySql总是选择它遇到的FIRST值。

为了确保它遇到的第一个值恰好是您想要的值,您需要将GROUP BY应用于有序查询,因此需要使用子查询。 否则你不能这样做。

假设MySql总是选择遇到的第一行,那么您正在对GROUP BY之前的行进行正确的排序。 但不幸的是,如果你仔细阅读文档,你会注意到这个假设是不正确的。

当选择不总是相同的非聚合列时, MySql可以自由选择任何值,因此实际显示的结果值是不确定的

我发现这个获得非聚合列的第一个值的技巧使用了很多,而且通常/几乎总是有效的,我有时也会使用它(在我自己的风险之下)。 但由于没有记录,你不能依赖这种行为。

这个链接(感谢ypercube!) GROUP BY技巧已经被优化了,显示了相同的查询在MySql和MariaDB之间返回不同结果的情况,可能是因为有不同的优化引擎。

所以,如果这个技巧有效,那只是一个运气问题。

另一个问题上接受的答案对我来说是错误的:

 HAVING wp_posts.post_date = MAX(wp_posts.post_date) 

wp_posts.post_date是一个非聚合列,它的值将被正式确定,但它可能是遇到的第一个post_date 。 但是,由于GROUP BY技巧应用于无序表,所以不能确定哪个是遇到的第一个post_date

它可能会返回帖子,这是一个作者的唯一职位,但即使这并不总是确定的。

可能的解决方案

我认为这可能是一个可能的解决方案:

 SELECT wp_posts.* FROM wp_posts WHERE id IN ( SELECT max(id) FROM wp_posts WHERE (post_author, post_date) = ( SELECT post_author, max(post_date) FROM wp_posts WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post' GROUP BY post_author ) AND wp_posts.post_status='publish' AND wp_posts.post_type='post' GROUP BY post_author ) 

在内部查询中,我返回每个作者的最大发布日期。 然后我考虑到同一个作者可以在同一时间理论上有两个职位的事实,所以我只得到最大的ID。 然后我返回所有具有最大ID的行。 使用连接而不是IN子句可以使速度更快。

(如果你确定ID只是增加了,如果ID1 > ID2也意味着post_date1 > post_date2 ,那么查询可以变得更简单,但我不确定是否是这种情况)。

你要读的是相当黑,所以不要在家里试试这个!

在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; 

注意

这不是我会推荐这个特定的查询。 在这种情况下,我会使用@bluefeet建议的稍微修改过的版本。 但是这个技术可能非常有用。 看看我的答案在这里: 检索每个组中的最后一个记录

陷阱 :这种方法的缺点是

  • 查询的结果取决于索引,这违背了SQL的精神(索引只能加快查询速度);
  • 索引不知道它对查询的影响(你或将来的其他人可能会发现索引太耗费资源,并以某种方式改变它,打破查询结果,而不仅仅是它的性能)
  • 如果你不明白查询是如何工作的,那么很可能你会在一个月内忘记解释,查询会让你和你的同事感到困惑。

好处是在困难的情况下表现。 在这种情况下,查询的性能应该与@ bluefeet的查询相同,因为在排序中涉及的数据量(所有数据都被加载到一个临时表中,然后排序; btw,他的查询需要(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上执行计划的查询。

试试这个。 只要从每位作者那里得到最新发布日期的列表 。 而已

 SELECT wp_posts.* FROM wp_posts WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post' AND wp_posts.post_date IN(SELECT MAX(wp_posts.post_date) FROM wp_posts GROUP BY wp_posts.post_author) 

不。在分组之前排序记录是没有意义的,因为分组将会改变结果集。 子查询的方式是首选的方式。 如果这太慢了,你将不得不改变你的表格设计,例如把每个作者的最后一篇文章的id存储在一个单独的表格中,或者引入一个布尔列来指示每个作者的哪篇文章是最后的一。

简单来说,标准解决方案使用不相关的子查询,如下所示:

 SELECT x.* FROM my_table x JOIN (SELECT grouping_criteria,MAX(ranking_criterion) max_n FROM my_table GROUP BY grouping_criteria) y ON y.grouping_criteria = x.grouping_criteria AND y.max_n = x.ranking_criterion; 

如果您使用的是MySQL的古老版本或相当小的数据集,那么您可以使用以下方法:

 SELECT x.* FROM my_table x LEFT JOIN my_table y ON y.joining_criteria = x.joining_criteria AND y.ranking_criteria < x.ranking_criteria WHERE y.some_non_null_column IS NULL; 

只需使用最大功能和组功能

  select max(taskhistory.id) as id from taskhistory group by taskhistory.taskid order by taskhistory.datum desc 

**与大型数据集一起使用时,子查询可能会对性能造成不良影响**

原始查询

 SELECT wp_posts.* FROM wp_posts WHERE wp_posts.post_status = 'publish' AND wp_posts.post_type = 'post' GROUP BY wp_posts.post_author ORDER BY wp_posts.post_date DESC; 

修改后的查询

 SELECT p.post_status, p.post_type, Max(p.post_date), p.post_author FROM wp_posts P WHERE p.post_status = "publish" AND p.post_type = "post" GROUP BY p.post_author ORDER BY p.post_date; 

因为我在select clause使用了max ==> max(p.post_date)所以可以避免子选择查询和group by之后的max列排序。

首先,不要在选择中使用*,影响他们的表现,妨碍组织的使用。 试试这个查询:

 SELECT wp_posts.post_author, wp_posts.post_date as pdate FROM wp_posts WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post' GROUP BY wp_posts.post_author ORDER BY pdate DESC 

当你没有在ORDER BY中指定表时,只需要别名,他们就会命令select的结果。