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 BY
MySQL中,则选择应返回的值.
使用带有聚合函数的子查询将保证每次都返回正确的作者和帖子.
作为旁注,虽然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 次 |
最近记录: |