“group by”会自动保证“order by”吗?

Posted

技术标签:

【中文标题】“group by”会自动保证“order by”吗?【英文标题】:Does "group by" automatically guarantee "order by"? 【发布时间】:2015-03-24 20:26:15 【问题描述】:

“group by”子句是否自动保证结果将按该键排序?换句话说,写就够了:

select * 
from table
group by a, b, c

还是一定要写

select * 
from table
group by a, b, c
order by a, b, c

我知道例如在 mysql 中我不必这样做,但我想知道我是否可以在 SQL 实现中依赖它。有保障吗?

【问题讨论】:

顺便说一句,我刚刚检查过:我的 PostgreSQL 9.3.4 在使用 group by 时不会自动返回排序数据。 我尝试了 Msdn 的 AdventureWorks db。它可以按排序数据分组,我想知道它取决于数据类型吗?所以我尝试了一个日期,并且只有一个 group by 语句。 【参考方案1】:

group by 不必对数据进行排序。数据库旨在尽可能快地获取数据,并且仅在必要时进行排序。

如果您需要保证订单,请添加order by

【讨论】:

group by 的有效实现将通过在内部对数据进行排序来执行分组。这就是为什么一些 RDBMS 在分组时返回排序输出的原因。然而,SQL 规范并没有强制要求这种行为,所以除非 RDBMS 供应商明确记录,否则我不会打赌它会起作用(明天)。 OTOH,如果 RDBMS 隐式进行排序,它也可能足够聪明,可以优化(消除)冗余的order by 感谢@JimmyB。如果放在一起,您在此页面上的 cmets 可能会包含一个很好的答案:-)【参考方案2】:

绝对不会。我经历过,一旦我的一个查询突然开始返回未排序的结果,随着表中的数据增长。

【讨论】:

这是 GROUP BY 真正危险的行为 - 你开发你的系统是围绕返回排序数据的观察和在百万分之一组的操作,突然间,没有 -返回排序后的数据。让我头疼了一个星期,直到我制作了一个最小的可重现示例来发现我的错误! 您至少应该提及您使用的数据库引擎...【参考方案3】:

这取决于记录的数量。当记录较少时,Group by 会自动排序。当记录多于(超过 15 条)时,需要添加 Order by 子句

【讨论】:

你能引用任何参考资料吗?这可能只适用于某些特定的引擎吧?【参考方案4】:

我试过了。 Msdn 的 Adventureworks db。

select HireDate, min(JobTitle)
from AdventureWorks2016CTP3.HumanResources.Employee
group by HireDate

结果:

2009-01-10生产技术员 - WC40

2009-01-11应用专家

2009-01-12首席财务官助理

2009-01-13生产技术员 - WC50

它返回已排序的hiredate数据,但在任何情况下您都不依赖GROUP BY进行排序。

例如;索引可以改变这个排序的数据。

我添加了以下索引(雇用日期、职位)

CREATE NONCLUSTERED INDEX NonClusturedIndex_Jobtitle_hireddate ON [HumanResources].[Employee]
(
    [JobTitle] ASC,
    [HireDate] ASC
)

结果会随着相同的选择查询而改变;

2006-06-30 生产技术员 - WC60

2007-01-26 营销助理

2007-11-11 工程经理

2007-12-05 高级工具设计师

2007-12-11 工具设计师

2007-12-20 市场部经理

2007-12-26 生产主管 - WC60

您可以在以下地址下载Adventureworks2016

https://www.microsoft.com/en-us/download/details.aspx?id=49502

【讨论】:

【参考方案5】:

group by 的有效实现是通过在内部对数据进行排序来执行分组。这就是为什么一些 RDBMS 在分组时返回排序输出的原因。然而,SQL 规范并没有强制要求这种行为,所以除非 RDBMS 供应商明确记录,否则我不会打赌它会起作用(明天)。 OTOH,如果 RDBMS 隐式地进行排序,它也可能足够聪明,然后优化(远离)冗余顺序。 @jimmyb

一个使用 PostgreSQL 的例子来证明这个概念

创建一个包含 100 万条记录的表,随机日期从今天到 90 天,并按日期索引

CREATE TABLE WITHDRAW AS
  SELECT (random()*1000000)::integer AS IDT_WITHDRAW,
    md5(random()::text) AS NAM_PERSON,
    (NOW() - ( random() * (NOW() + '90 days' - NOW()) ))::timestamp AS DAT_CREATION, -- de hoje a 90 dias atras
    (random() * 1000)::decimal(12, 2) AS NUM_VALUE
  FROM generate_series(1,1000000);

CREATE INDEX WITHDRAW_DAT_CREATION ON WITHDRAW(DAT_CREATION);

按日期分组,按日期截断,限制在两天范围内按日期选择

EXPLAIN 
SELECT
    DATE_TRUNC('DAY', W.dat_creation), COUNT(1), SUM(W.NUM_VALUE)
FROM WITHDRAW W
WHERE W.dat_creation >= (NOW() - INTERVAL '2 DAY')::timestamp
AND W.dat_creation < (NOW() - INTERVAL '1 DAY')::timestamp
GROUP BY 1

HashAggregate  (cost=11428.33..11594.13 rows=11053 width=48)
  Group Key: date_trunc('DAY'::text, dat_creation)
  ->  Bitmap Heap Scan on withdraw w  (cost=237.73..11345.44 rows=11053 width=14)
        Recheck Cond: ((dat_creation >= ((now() - '2 days'::interval))::timestamp without time zone) AND (dat_creation < ((now() - '1 day'::interval))::timestamp without time zone))
        ->  Bitmap Index Scan on withdraw_dat_creation  (cost=0.00..234.97 rows=11053 width=0)
              Index Cond: ((dat_creation >= ((now() - '2 days'::interval))::timestamp without time zone) AND (dat_creation < ((now() - '1 day'::interval))::timestamp without time zone))

使用更大的限制日期范围,它选择应用 SORT

EXPLAIN 
SELECT
    DATE_TRUNC('DAY', W.dat_creation), COUNT(1), SUM(W.NUM_VALUE)
FROM WITHDRAW W
WHERE W.dat_creation >= (NOW() - INTERVAL '60 DAY')::timestamp
AND W.dat_creation < (NOW() - INTERVAL '1 DAY')::timestamp
GROUP BY 1

GroupAggregate  (cost=116522.65..132918.32 rows=655827 width=48)
  Group Key: (date_trunc('DAY'::text, dat_creation))
  ->  Sort  (cost=116522.65..118162.22 rows=655827 width=14)
        Sort Key: (date_trunc('DAY'::text, dat_creation))
        ->  Seq Scan on withdraw w  (cost=0.00..41949.57 rows=655827 width=14)
              Filter: ((dat_creation >= ((now() - '60 days'::interval))::timestamp without time zone) AND (dat_creation < ((now() - '1 day'::interval))::timestamp without time zone))

只需在最后加上ORDER BY 1即可(没有显着差异)

GroupAggregate  (cost=116522.44..132918.06 rows=655825 width=48)
  Group Key: (date_trunc('DAY'::text, dat_creation))
  ->  Sort  (cost=116522.44..118162.00 rows=655825 width=14)
        Sort Key: (date_trunc('DAY'::text, dat_creation))
        ->  Seq Scan on withdraw w  (cost=0.00..41949.56 rows=655825 width=14)
              Filter: ((dat_creation >= ((now() - '60 days'::interval))::timestamp without time zone) AND (dat_creation < ((now() - '1 day'::interval))::timestamp without time zone))

PostgreSQL 10.3

【讨论】:

【参考方案6】:

这取决于数据库供应商。

例如 PostgreSQL 不会自动对分组结果进行排序。 在这里,您必须使用 order by 来对数据进行排序。

但是 Sybase 和 Microsoft SQL Server 可以。在这里您可以使用 order by 来更改默认排序。

【讨论】:

以上是关于“group by”会自动保证“order by”吗?的主要内容,如果未能解决你的问题,请参考以下文章

sql语句执行顺序之group by、order by

SQL语句中,如果有group by 和order by两个语句,是先分组还是先排序?

order by 和 group by 的区别

order by 和 group by 的区别?

SQL中,group by 跟order by有什么区别?

Mysql之order by|group by 排序优化