Rob*_*est 228 mysql group-by sql-order-by
这里有很多类似的问题,但我认为没有充分回答这个问题.
我会从当前最流行的问题继续,并使用他们的例子,如果这没关系.
此实例中的任务是获取数据库中每个作者的最新帖子.
示例查询产生不可用的结果,因为它并不总是返回的最新帖子.
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
Run Code Online (Sandbox Code Playgroud)
目前接受的答案是
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
Run Code Online (Sandbox Code Playgroud)
不幸的是,这个答案简单明了,并且在很多情况下产生的结果不如原始查询那么稳定.
我最好的解决方案是使用表单的子查询
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
Run Code Online (Sandbox Code Playgroud)
我的问题是一个简单的问题: 无论如何在分组之前订购行而不诉诸子查询?
编辑:这个问题是另一个问题的延续,我的情况细节略有不同.您可以(并且应该)假设还有一个wp_posts.id,它是该特定帖子的唯一标识符.
Tar*_*ryn 351
使用ORDER BY子查询是不是解决这个问题的最佳解决方案.
获取max(post_date)作者的最佳解决方案是使用子查询返回最大日期,然后在最大日期和最大日期将其加入表中post_author.
解决方案应该是:
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
Run Code Online (Sandbox Code Playgroud)
如果您有以下示例数据:
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')
;
Run Code Online (Sandbox Code Playgroud)
子查询将返回最大日期和作者:
MaxPostDate | Author
2/1/2013 | Jim
Run Code Online (Sandbox Code Playgroud)
然后,由于您将该表重新加入表中,因此您将返回该帖子的完整详细信息.
扩展我关于使用子查询准确返回此数据的注释.
MySQL不会强制您GROUP BY使用SELECT列表中包含的每一列.因此,如果您只有GROUP BY一列但总共返回10列,则无法保证post_author返回属于该列的其他列值.如果列不在GROUP BYMySQL中,则选择应返回的值.
使用带有聚合函数的子查询将保证每次都返回正确的作者和帖子.
作为旁注,虽然MySQL允许您ORDER BY在子查询中使用a并允许您应用列表GROUP BY中的不是每个列,但SELECT在包括SQL Server的其他数据库中不允许这种行为.
fth*_*lla 19
您的解决方案使用GROUP BY子句的扩展,允许按某些字段分组(在这种情况下,只是post_author):
GROUP BY wp_posts.post_author
Run Code Online (Sandbox Code Playgroud)
并选择非聚合列:
SELECT wp_posts.*
Run Code Online (Sandbox Code Playgroud)
未在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');
Run Code Online (Sandbox Code Playgroud)
并且你想要提取在花园里生长的所有花朵,在那里生长多种花朵.然后你必须使用子查询,例如你可以使用它:
SELECT GardensFlowers.*
FROM GardensFlowers
WHERE name IN (SELECT name
FROM GardensFlowers
GROUP BY name
HAVING COUNT(DISTINCT flower)>1);
Run Code Online (Sandbox Code Playgroud)
如果您需要提取所有花朵,而不是花朵中的唯一花朵,您可以将HAVING条件更改为HAVING COUNT(DISTINCT flower)=1,但MySql也允许您使用此:
SELECT GardensFlowers.*
FROM GardensFlowers
GROUP BY name
HAVING COUNT(DISTINCT flower)=1;
Run Code Online (Sandbox Code Playgroud)
没有子查询,不是标准SQL,但更简单.
GROUP BY子句的扩展名使用不正确
但是如果您选择每行不相等的非聚合列会发生什么?MySql为该列选择的值是多少?
看起来MySql总是选择它遇到的FIRST值.
要确保它遇到的第一个值正是您想要的值,您需要将a GROUP BY应用于有序查询,因此需要使用子查询.否则你不能这样做.
假设MySql总是选择它遇到的第一行,你就正确地在GROUP BY之前对行进行排序.但不幸的是,如果你仔细阅读文档,你会发现这种假设是不正确的.
选择并非总是相同的非聚合列时,MySql可以自由选择任何值,因此实际显示的结果值是不确定的.
我看到这个获取非聚合列的第一个值的技巧被大量使用,它通常/几乎总是有效,我有时也使用它(我自己承担风险).但由于没有记录,你不能依赖这种行为.
此链接(感谢ypercube!)GROUP BY技巧已被优化,显示了同一查询在MySql和MariaDB之间返回不同结果的情况,可能是因为不同的优化引擎.
所以,如果这个技巧有效,那只是运气问题.
在对其他问题接受的答案 看起来我错了:
HAVING wp_posts.post_date = MAX(wp_posts.post_date)
Run Code Online (Sandbox Code Playgroud)
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
)
Run Code Online (Sandbox Code Playgroud)
在内部查询中,我将返回每个作者的最大发布日期.然后我考虑到同一作者理论上可以同时有两个帖子,所以我只得到最大ID.然后我将返回具有最大ID的所有行.使用连接而不是IN子句可以更快地完成它.
(如果你确定这ID只是增加,如果ID1 > ID2也意味着post_date1 > post_date2,那么查询可以变得更简单,但我不确定是否是这种情况).
你要读的是相当hacky,所以不要在家里试试!
在SQL中一般来说,你的问题的答案是否定的,但是由于松弛的模式GROUP BY(由@bluefeet提到),MySQL 的答案是肯定的.
假设您有一个BTREE索引(post_status,post_type,post_author,post_date).索引如何在引擎盖下看起来像?
(post_status ='发布',post_type ='发布',post_author ='用户A',post_date ='2012-12-01')(post_status ='发布',post_type ='发布',post_author ='用户A', post_date ='2012-12-31')(post_status ='发布',post_type ='发布',post_author ='用户B',post_date ='2012-10-01')(post_status ='发布',post_type =' post',post_author ='用户B',post_date ='2012-12-01')
也就是说,数据按升序排列所有这些字段.
GROUP BY默认情况下,当您执行a时,它会按分组字段对数据进行排序(post_author在我们的示例中,WHERE子句需要post_status,post_type ),如果存在匹配的索引,则会按升序获取每个第一个记录的数据.即查询将获取以下内容(每个用户的第一篇文章):
(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')
但是GROUP BY在MySQL中允许您明确指定顺序.当你post_user按降序请求时,它将以相反的顺序遍历我们的索引,仍然记录实际上最后的每个组的第一条记录.
那是
...
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC
Run Code Online (Sandbox Code Playgroud)
会给我们
(post_status ='发布',post_type ='发布',post_author ='用户B',post_date ='2012-12-01')(post_status ='发布',post_type ='发布',post_author ='用户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;
Run Code Online (Sandbox Code Playgroud)
NB:
这不是我为这个特定查询推荐的内容.在这种情况下,我会使用@bluefeet建议的略微修改版本.但这种技术可能非常有用.在这里看一下我的答案:检索每组中的最后一条记录
陷阱:这种方法的缺点是
优点是在困难情况下的性能.在这种情况下,查询的性能应该与@ 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';
Run Code Online (Sandbox Code Playgroud)
使用上述方法的相同查询:
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);
Run Code Online (Sandbox Code Playgroud)
所有这些查询及其在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)
Run Code Online (Sandbox Code Playgroud)
只需使用 max 函数和 group 函数
select max(taskhistory.id) as id from taskhistory
group by taskhistory.taskid
order by taskhistory.datum desc
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
313214 次 |
| 最近记录: |