如何对不同值求和 OVER (PARTITION BY DISTINCT)
How to sum OVER (PARTITION BY DISTINCT) for Distinct Values
我正在寻找在 SQL 服务器中使用 Partition by Over
的巧妙方法。
我在 SQL 服务器中有 3 个表(下面的所有 *_id
列只是伪主键)
- PO (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
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
因此在此示例中,PO1 的总订购数量为 650,但总交付数量为 663。
想要的结果:
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
现在我可以使用子查询来完成这个任务了:
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'
wondering if there is an easier way of doing sums by more clever application of over partition by clause
好吧,基本上使用基本形式,你最终得到一行的 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'
如果您添加另一个 table 导致 pd.dely_no
每个 po/po+item
分区重复值 1,那么您将需要扩展 CASE 逻辑
我正在寻找在 SQL 服务器中使用 Partition by Over
的巧妙方法。
我在 SQL 服务器中有 3 个表(下面的所有 *_id
列只是伪主键)
- PO (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 | 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 |
因此在此示例中,PO1 的总订购数量为 650,但总交付数量为 663。
想要的结果:
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 |
现在我可以使用子查询来完成这个任务了:
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'
wondering if there is an easier way of doing sums by more clever application of over partition by clause
好吧,基本上使用基本形式,你最终得到一行的 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'
如果您添加另一个 table 导致 pd.dely_no
每个 po/po+item
分区重复值 1,那么您将需要扩展 CASE 逻辑