简体   繁体   English

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

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

I am looking for a clever way of using Partition by Over in SQL Server.我正在寻找一种在 SQL 服务器中使用Partition by Over的巧妙方法。

I have 3 tables in SQL Server (all *_id columns below are just pseudo primary key)我在 SQL 服务器中有 3 个表(下面的所有*_id列都只是伪主键)

  • PO (po_id, po_no);采购订单(po_id,po_no);
  • PO_ITEM (po_item_id, po_id, po_item_no, qty); PO_ITEM (po_item_id, po_id, po_item_no, 数量); // stores ordered quantity for a PO ITEM // 存储 PO ITEM 的订购数量
  • PO_ITEM_DELY (po_item_dely_id, po_item_id, dely_no, dely_qty); PO_ITEM_DELY (po_item_dely_id, po_item_id, dely_no, dely_qty); // stores delivered quantity for every item of PO per delivery no. // 存储每个交货编号的每个 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'

Result of this SQL query for reference:此 SQL 查询的结果供参考:

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

So in this example, the Total Ordered Quantity for PO1 is 650, but Total Delivered Qty is 663.因此,在此示例中,PO1 的总订购数量为 650,但总交付数量为 663。

Desired result:期望的结果:

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

Now I am able to do this task by using subqueries:现在我可以通过使用子查询来完成这项任务:

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'
;

However, I was just wondering if there is an easier way of doing sums by more clever application of over partition by clause.但是,我只是想知道是否有一种更简单的方法可以通过更巧妙地应用over partition by子句来进行求和。 The main challenge is with the Query below, as I cannot use distinct in partition by clause.主要挑战在于下面的查询,因为我不能在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'

Use multiple different window specs to solve this one:使用多个不同的 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'

Technically the partition by po_no is unnecessary because the where clause ensures there is only one, but i've left it in in case you want to expand the query to consider multiple po_no从技术上讲partition by po_no是不必要的,因为 where 子句确保只有一个,但我把它留了下来,以防你想扩展查询以考虑多个po_no

If you will always only ever query one 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'

wondering if there is an easier way of doing sums by more clever application of over partition by clause想知道是否有更巧妙地应用 over partition by 子句来进行求和的更简单方法

Well, essentially with the basic form you end up with N repeats of a row, and you could count the repeats and divide the sum of the values in the group by the number of repeats of the group, so you're summing values that are a third of their original but repeated 3 times for the same overall sum.. But I do feel it makes a bigger mess than just doing the summing and counting at the level where there is no cartesian product, and then that result just gets carried out and repeated..好吧,基本上使用基本形式,您最终会得到 N 行重复,并且您可以计算重复次数并将组中值的总和除以组的重复次数,因此您将值相加他们原来的三分之一,但重复了 3 次以获得相同的总和。但我确实觉得这比在没有笛卡尔积的水平上进行求和和计数更混乱,然后这个结果就被执行了并重复..

Or we could count only one of the items, presuming that every item will at least have an delivery #1:或者我们可以只计算其中一个项目,假设每个项目至少有一个交付 #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'

If you add another table that causes the pd.dely_no to have repeats of value 1 per po/po+item partitions then you'll need to extend the CASE logic如果您添加另一个表,导致pd.dely_no每个po/po+item分区的重复值为 1,那么您需要扩展 CASE 逻辑

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

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