使用DISTINCT代替GROUP BY是否正确?

时间:2019-07-11 09:11:03

标签: postgresql

示例数据:

id | docn | item | suma
---------------------
1     33     x   |   10
1     33     y   |   20
2     37     a   |   10
2     37     b   |   20
2     37     c   |   30

要对结果进行分组,我可以写:

SELECT sum( suma ),  
  (ocd.o).* 
FROM order_cost_details() ocd
where (ocd.o).id IN ( 6154, 10805 )
GROUP BY ocd.o

enter image description here

但是在一个有群组的地方,我想为每个群组选择last_value。下一步不起作用:

SELECT sum( suma ),  
  (ocd.o).*, 
  last_value( ocd.c ) OVER (PARTITION BY ocd.o ) 
FROM order_cost_details() ocd
where (ocd.o).id IN ( 6154, 10805 )
GROUP BY ocd.o
SQL Error [42803]: ERROR: column "ocd.c" must appear in the GROUP BY clause or be used in an aggregate function

我将查询重写如下:

SELECT DISTINCT sum( suma ) OVER ( PARTITION BY ocd.o ),  
  (ocd.o).*, 
  last_value( ocd.c ) OVER (PARTITION BY ocd.o ) 
FROM order_cost_details() ocd
where (ocd.o).id IN ( 6154, 10805 )

结果似乎是预期的: enter image description here 正确的last_valueenter image description here

但是我不确定在这里使用DISTINCT代替GROUP BY是否正确?

3 个答案:

答案 0 :(得分:2)

last_value()通常无法按预期运行Window Functions: last_value(ORDER BY ... ASC) same as last_value(ORDER BY ... DESC)

要获取分区的最后一个值,一种更有效的方法是获取降序的第一个值:

SELECT
    first_value(my_column) OVER (PARTITION BY partitioned_column ORDER BY order_column DESC)
FROM
    ...

答案 1 :(得分:0)

您可以使用子选择:

SELECT sum(suma),  
       (o).*,
       last_c
FROM (SELECT suma,
             ocd.o
             last_value(ocd.c)
                OVER (PARTITION BY ocd.o
                      ORDER BY some_col)
                AS last_c 
      FROM order_cost_details() ocd
      where (ocd.o).id IN (6154, 10805)
     ) AS q
GROUP BY o, last_c;

答案 2 :(得分:0)

来自IRC

  

RhodiumToad :在上使用 DISTINCT 几乎总是错误(请记住,这完全是标准)

     

基本经验法则是,当您要减少输出行数时,请使用GROUP BY;而在要保持行数相同时,请使用窗口函数

     

在分组依据之后,使用窗口功能 不会阻止您对订单进行总计

因此,我将查询重写为:

 SELECT *,
   sum( t.group_suma     ) OVER( PARTITION BY (t.o).id ) AS total_suma
 FROM (
    SELECT 
     sum( ocd.item_cost     ) AS group_cost,
     sum( ocd.item_suma     ) AS group_suma,
     max( (ocd.ic).consumed ) AS consumed,
     ocd.o
    FROM order_cost_details() ocd
    where (ocd.o).id IN ( 6154, 10805 )
    GROUP BY ocd.o, (ocd.ic).consumed_period
 ) t