如何使用 window 函数对 ( DISTINCT <column> ) OVER () 求和?

How to `sum( DISTINCT <column> ) OVER ()` using window function?

我有下一个数据:

这里我已经计算了 conf_id 的总数。但也想计算整个分区的总数。例如:
根据每个订单的协议计算总金额(不是订单中四舍五入略有不同的货物)

如何求和 737.381238.3?例如。组内只取一个号码

(我不能求和(item_suma),因为它会return1975.67。注意conf_suma的回合作为中间步骤)

UPD
完整查询。在这里,我想为每个组计算四舍五入的总和。然后我需要计算这些组的总 suma

SELECT app_period( '2021-02-01', '2021-03-01' );


WITH
target_date AS ( SELECT '2021-02-01'::timestamptz ),
target_order as (
  SELECT
    tstzrange( '2021-01-01', '2021-02-01') as bill_range,
    o.*
  FROM ( SELECT * FROM "order_bt" WHERE sys_period @> sys_time() ) o
  WHERE FALSE
    OR o.agreement_id = 3385 and o.period_id = 10
),
USAGE AS ( SELECT
  ocd.*,


  o.agreement_id                  as agreement_id,
  o.id                            AS order_id,
  
  (dense_rank() over (PARTITION BY o.agreement_id       ORDER BY o.id                     )) as zzzz_id,
  (dense_rank() over (PARTITION BY o.agreement_id, o.id ORDER BY (ocd.ic).consumed_period )) as conf_id,

  
   sum( ocd.item_suma     ) OVER( PARTITION BY (ocd.o).agreement_id                 ) AS agreement_suma2,

 
  (sum( ocd.item_suma )  OVER( PARTITION BY (ocd.o).agreement_id, (ocd.o).id, (ocd.ic).consumed_period )) AS x_suma,
  (sum( ocd.item_cost )  OVER( PARTITION BY (ocd.o).agreement_id, (ocd.o).id, (ocd.ic).consumed_period )) AS x_cost,
  (sum( ocd.item_suma )  OVER( PARTITION BY (ocd.o).agreement_id, (ocd.o).id, (ocd.ic).consumed_period ))::numeric( 10, 2) AS conf_suma,
  (sum( ocd.item_cost )  OVER( PARTITION BY (ocd.o).agreement_id, (ocd.o).id, (ocd.ic).consumed_period ))::numeric( 10, 2) AS conf_cost,
  max((ocd.ic).consumed) OVER( PARTITION BY (ocd.o).agreement_id, (ocd.o).id, (ocd.ic).consumed_period )                   AS consumed,
  (sum( ocd.item_suma )  OVER( PARTITION BY (ocd.o).agreement_id, (ocd.o).id                           )) AS order_suma2
FROM target_order o
LEFT JOIN order_cost_details( o.bill_range ) ocd
  ON (ocd.o).id = o.id  AND  (ocd.ic).consumed_period && o.app_period
)

SELECT 
  *,
  (conf_suma/6) ::numeric( 10, 2 ) as group_nds,
  (SELECT sum(x) from (SELECT  sum( DISTINCT conf_suma )                       AS x FROM usage sub_u WHERE sub_u.agreement_id = usage.agreement_id GROUP BY agreement_id, order_id) t) as total_suma,
  (SELECT sum(x) from (SELECT (sum( DISTINCT conf_suma ) /6)::numeric( 10, 2 ) AS x FROM usage sub_u WHERE sub_u.agreement_id = usage.agreement_id GROUP BY agreement_id, order_id) t) as total_nds
FROM USAGE
WINDOW w AS ( PARTITION BY usage.agreement_id ROWS CURRENT ROW EXCLUDE TIES)
ORDER BY
  order_id,
  conf_id

我的old question

我找到了解决方案。参见 dbfiddle

为了 运行 window 不同值的函数,我应该从每个对等点获得第一个值。为了完成这个我

  1. aggregate 此节点的行 ID
  2. lag这个聚合一个
  3. 将尚未聚合的行(这是同行的第一行)标记为 _distinct
  4. sum( ) FILTER ( WHERE _distinct ) 在 ( ... )

瞧。您在目标 PARTITION
处获得 sum 超过 DISTINCT 值 PostgreSQL 尚未实现

with data as (
  select * from (values 
      ( 1, 1, 1, 1.0049 ), (2, 1,1,1.0049), ( 3, 1,1,1.0049 ) ,
      ( 4, 1, 2, 1.0049 ), (5, 1,2,1.0057), 
      ( 6, 2, 1, 1.53 ), ( 7,2,1,2.18), ( 8,2,2,3.48 )
 ) t (id, agreement_id, order_id, suma)
),
intermediate as (select 
 *,
 sum( suma ) over ( partition by agreement_id, order_id ) as fract_order_suma,
 sum( suma ) over ( partition by agreement_id           ) as fract_agreement_total,
 (sum( suma::numeric(10,2) ) over ( partition by agreement_id, order_id )) as wrong_order_suma,
 (sum( suma ) over ( partition by agreement_id, order_id ))::numeric( 10, 2) as order_suma,
 (sum( suma ) over ( partition by agreement_id           ))::numeric( 10, 2) as wrong_agreement_total,
 id as xid,
 array_agg( id ) over ( partition by agreement_id, order_id ) as agg
from data),

distinc as (select *,
  lag( agg ) over ( partition by agreement_id ) as prev, 
  id = any (lag( agg ) over ()) is not true as _distinct, -- allow to match first ID from next peer
  order_suma as xorder_suma, -- repeat column to easily visually compare with _distinct
  (SELECT sum(x) from (SELECT  sum( DISTINCT order_suma ) AS x FROM intermediate sub_q WHERE sub_q.agreement_id = intermediate.agreement_id GROUP BY agreement_id, order_id) t) as correct_total_suma
from intermediate
)
select 
*,
sum( order_suma ) filter ( where _distinct ) over ( partition by agreement_id ) as also_correct_total_suma
from distinc

更好的方法dbfiddle

  1. 在每个订单上分配 row_numberrow_number() over (partition by agreement_id, order_id ) as nrow
  2. 只取第一个 suma:filter nrow = 1
with data as (
  select * from (values 
      ( 1, 1, 1, 1.0049 ), (2, 1,1,1.0049), ( 3, 1,1,1.0049 ) ,
      ( 4, 1, 2, 1.0049 ), (5, 1,2,1.0057), 
      ( 6, 2, 1, 1.53 ), ( 7,2,1,2.18), ( 8,2,2,3.48 )
 ) t (id, agreement_id, order_id, suma)
),
intermediate as (select 
 *,
 row_number() over (partition by agreement_id, order_id ) as nrow,
 (sum( suma ) over ( partition by agreement_id, order_id ))::numeric( 10, 2) as order_suma,
from data)

select 
  *,
  sum( order_suma ) filter (where nrow = 1) over (partition by agreement_id)
from intermediate```