[英]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
列都只是伪主键)
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_item_no ![]() |
qty![]() |
dely_no ![]() |
dely_qty![]() |
---|---|---|---|---|
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 ![]() |
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 ![]() |
OrdPOQty![]() |
DelyPOQty![]() |
po_item_no ![]() |
OrdItemQty![]() |
delyItemQty![]() |
dely_no ![]() |
dely_qty![]() |
---|---|---|---|---|---|---|---|
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 ![]() |
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.