代码之家  ›  专栏  ›  技术社区  ›  Tomas

“分组依据”是否自动保证“排序依据”?

  •  24
  • Tomas  · 技术社区  · 10 年前

    “groupby”子句是否自动保证结果将按该键排序?换言之,写下:

    select * 
    from table
    group by a, b, c
    

    还是必须写

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

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

    6 回复  |  直到 10 年前
        1
  •  30
  •   Tomas    8 年前

    group by 不需要对数据进行排序。DB的设计目的是尽可能快地获取数据,并仅在必要时进行排序。

    因此,添加 order by 如果你需要有保证的订单。

        2
  •  5
  •   deFreitas    7 年前

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

    使用PostgreSQL的示例证明了这一概念

    创建一个包含1M条记录的表,其中包含从今天到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))
    

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

    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

        3
  •  3
  •   Hasan Tuncay    5 年前

    这取决于数据库供应商。

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

    但Sybase和Microsoft SQL Server确实如此。在这里,您可以使用order by更改默认排序。

        4
  •  1
  •   Pecheneg    9 年前

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

        5
  •  -1
  •   Başar Kaya    7 年前

    我试过了Msdn的Adventureworks数据库。

    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<

    它返回已排序的雇佣日期数据,但在任何情况下都不依赖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

        6
  •  -2
  •   Dev    8 年前

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

    推荐文章