繁体   English   中英

如何对不同值求和 OVER (PARTITION BY DISTINCT)

[英]How to sum OVER (PARTITION BY DISTINCT) for Distinct Values

我正在寻找一种在 SQL 服务器中使用Partition by Over的巧妙方法。

我在 SQL 服务器中有 3 个表(下面的所有*_id列都只是伪主键)

  • 采购订单(po_id,po_no);
  • PO_ITEM (po_item_id, po_id, po_item_no, 数量); // 存储 PO ITEM 的订购数量
  • PO_ITEM_DELY (po_item_dely_id, po_item_id, dely_no, dely_qty); // 存储每个交货编号的每个 PO 项目的交货数量。
select
    po.po_no, pt.po_item_no, pt.qty, pd.dely_no, pd.dely_qty
from 
    PO
inner join 
    PO_ITEM pt on pt.po_id = po.po_id
inner join 
    PO_ITEM_DELY pd on pd.po_item_id = pt.po_item_id
where 
    po.po_no = 'PO1'

此 SQL 查询的结果供参考:

po_no po_item_no 数量 dely_no 延迟数量
PO1 PoI11 300 1 210
PO1 PoI11 300 2 48
PO1 PoI11 300 3 55
PO1 PoI12 100 1 100
PO1 PoI13 250 1 150
PO1 PoI13 250 2 100

因此,在此示例中,PO1 的总订购数量为 650,但总交付数量为 663。

期望的结果:

po_no 订购量 交货数量 po_item_no 订单项数量 交货数量 dely_no 延迟数量
PO1 650 663 PoI11 300 313 1 210
PO1 650 663 PoI11 300 313 2 48
PO1 650 663 PoI11 300 313 3 55
PO1 650 663 PoI12 100 100 1 100
PO1 650 663 PoI13 250 250 1 150
PO1 650 663 PoI13 250 250 2 100

现在我可以通过使用子查询来完成这项任务:

with poOrdQtyDtl as (
-- Form a Join between PO and PO_ITEM to get Total Ordered Qty Per PO
select
    po.po_id,
    po.po_no,
    sum(pt.qty) OrdPoQty
from PO
inner join PO_ITEM pt on pt.po_id = po.po_id
group by po.po_id, po.po_no
)
select
    poOrdQtyDtl.po_no [PO No.],
    poOrdQtyDtl.OrdPoQty [Ordered Qty For PO],
    sum(itemDely.currDelyQty) over (partition by poOrdQtyDtl.po_no) as [Delivered Qty For Po],
    itemDely.po_item_no [Item No.],
    itemDely.OrdItemQty [Ordred Item Qty],
    itemDely.DelItemQty [Delivered Item Qty],
    itemDely.dely_no [Dely No.],
    itemDely.currDelyQty [Item Qty Delivered in Current Dely]
from poOrdQtyDtl
inner join (
-- Join PO_ITEM and PO_ITEM_DELY to get Item Quantity details
select
    pt.po_id,
    pt.po_item_id,
    pt.po_item_no,
    pt.qty OrdItemQty,
    sum(pd.dely_qty) over (partition by pt.po_item_no) DelItemQty,
    pd.dely_no,
    pd.dely_qty currDelyQty
from PO_ITEM pt
inner join PO_ITEM_DELY pd on pd.po_item_id = pt.po_item_id
) itemDely on itemDely.po_id = poOrdQtyDtl.po_id
WHERE poOrdQtyDtl.po_no = 'PO1'
;

但是,我只是想知道是否有一种更简单的方法可以通过更巧妙地应用over partition by子句来进行求和。 主要挑战在于下面的查询,因为我不能在partition by子句中使用distinct

select
    po.po_no,
    -- sum (pt.qty) over (partition by distinct po.po_no, pt.po_item_no) TotPoQOrd, -- INCORRECT
    sum (pt.qty) over (partition by po.po_no, pt.po_item_no) TotPoQOrd,
    sum(pd.dely_qty) over (partition by po.po_no) TotPoQDely,
    pt.po_item_no,
    pt.qty,
    sum(pd.dely_qty) over (partition by po.po_no, pt.po_item_no) TotItemQ,
    pd.dely_no,
    pd.dely_qty
from PO
inner join PO_ITEM pt on pt.po_id = po.po_id
inner join PO_ITEM_DELY pd on pd.po_item_id = pt.po_item_id
where po.po_no = 'PO1'

使用多个不同的 window 规格来解决这个问题:

    select
      x.po_no, 

      x.OrdPOQty,
      SUM(pd.dely_qty) OVER(PARTITION BY x.po_no) as DelyPOQty,
      
      x.po_item_no,

      x.OrdItemQty,
      SUM(pd.dely_qty) OVER(PARTITION BY x.po_no, x.po_item_no) as DelyItemQty,
       
      x.qty, 
      pd.dely_no, 
      pd.dely_qty
    from 
      ( 
        SELECT 
          po.po_id, po.po_no, pt.po_item_id, pt.po_item_no, pt.qty, 
          SUM(pt.qty) OVER(PARTITION BY po.po_no) as OrdPOQty, 
          SUM(pt.qty) OVER(PARTITION BY po.po_no, pt.po_item_no) as OrdItemQty
        FROM PO inner join PO_ITEM pt on pt.po_id = po.po_id
      ) x
      inner join PO_ITEM_DELY pd on pd.po_item_id = x.po_item_id
    where 
      x.po_no = 'PO1'

从技术上讲partition by po_no是不必要的,因为 where 子句确保只有一个,但我把它留了下来,以防你想扩展查询以考虑多个po_no

如果您总是只查询一个po_no

    select
      x.po_no, 

      x.OrdPOQty,
      SUM(pd.dely_qty) OVER() as DelyPOQty,
      
      x.po_item_no,

      x.OrdItemQty,
      SUM(pd.dely_qty) OVER(PARTITION BY x.po_item_no) as DelyItemQty,
       
      x.qty, 
      pd.dely_no, 
      pd.dely_qty
    from 
      ( 
        SELECT 
          po.po_id, po.po_no, pt.po_item_id, pt.po_item_no, pt.qty, 
          SUM(pt.qty) OVER(PARTITION BY po.po_no) as OrdPOQty, 
          SUM(pt.qty) OVER(PARTITION BY po.po_no, pt.po_item_no) as OrdItemQty
        FROM PO inner join PO_ITEM pt on pt.po_id = po.po_id
      ) x
      inner join PO_ITEM_DELY pd on pd.po_item_id = x.po_item_id
    where 
      x.po_no = 'PO1'

想知道是否有更巧妙地应用 over partition by 子句来进行求和的更简单方法

好吧,基本上使用基本形式,您最终会得到 N 行重复,并且您可以计算重复次数并将组中值的总和除以组的重复次数,因此您将值相加他们原来的三分之一,但重复了 3 次以获得相同的总和。但我确实觉得这比在没有笛卡尔积的水平上进行求和和计数更混乱,然后这个结果就被执行了并重复..

或者我们可以只计算其中一个项目,假设每个项目至少有一个交付 #1:

select
  po.po_no, 

  SUM(CASE WHEN pd.dely_no = 1 THEN pt.qty ELSE 0 END) OVER(PARTITION BY po.po_no) as OrdPOQty,
  SUM(pd.dely_qty) OVER(PARTITION BY po.po_no) as DelyPOQty,
  
  pt.po_item_no,

  SUM(CASE WHEN pd.dely_no = 1 THEN pt.qty ELSE 0 END) OVER(PARTITION BY po.po_no, pt.po_item_no) as OrdItemQty,
  SUM(pd.dely_qty) OVER(PARTITION BY po.po_no, pt.po_item_no) as DelyItemQty,
   
  pt.qty, 
  pd.dely_no, 
  pd.dely_qty
from 
  PO
  inner join PO_ITEM pt on pt.po_id = po.po_id
  inner join PO_ITEM_DELY pd on pd.po_item_id = pt.po_item_id
where 
  po.po_no = 'PO1'

如果您添加另一个表,导致pd.dely_no每个po/po+item分区的重复值为 1,那么您需要扩展 CASE 逻辑

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM