パーティションに負の値が存在する場合にのみ、値を合計する必要があります。パーティションに負の値がない場合は、行を出力するだけです。
これが私が今持っているものです。初期データはCTEとして提供されます。
[〜#〜] dml [〜#〜]
;with ledger as (
select accountId, type, amount
from (
values
(1, 'R', -10)
,(1, 'V', 10)
,(1, 'R', 30)
,(2, 'R', 20)
,(2, 'R', -5)
,(2, 'V', 5)
,(3, 'R', 20)
,(3, 'R', 30)
) x (accountId, type, amount)
)
,b as ( --identifies accountid, type with negatives
select
accountid
,type
from ledger
group by accountid, type
having min(amount) < 0
)
,onlyPositives as (
select
l.accountid
,l.type
,l.amount
from ledger l
left join b on b.accountid = l.accountid
and b.type = l.type
where b.accountid is null
)
,aggregatedNegatives as (
select
l.accountid
,l.type
,amount = sum(l.amount)
from ledger l
inner join b on b.accountid = l.accountid
and b.type = l.type
group by l.accountid, l.type
)
select accountid, type, amount
from onlyPositives
union all
select accountid, type, amount
from aggregatedNegatives
私はこのような出力を期待しています、そして上記のクエリは正しく出力します。
1, R, 20 (summed because -10+30=20)
1, V, 10 (left alone)
2, R, 15 (summed because 20-5=15)
2, V, 5 (left alone)
3, R, 20 (left alone)
3, R, 30 (left alone)
これまでのクエリは野獣であり、不必要に複雑に感じられます。見落としている、もっと簡単なクエリはありますか?
これは問題のわずかなバリエーションです: 正の場合、すべての項目を合計します。負の場合、それぞれを返します
rextester-> https://rextester.com/EZRT33825
ウィンドウ関数を使用した後の結果を達成できますが、このクエリを多数の行に対して実行している場合、パフォーマンスについては保証できません。アイデアは、すべてのパーティションの合計、最小、および順序付けされていない行番号を計算することです。最小> 0ですべての行を保持しますが、最小<0の場合、パーティションの最初の行のみを保持します。
-- put data into temp table for illustration purposes
select accountId, type, amount into #t220618
from (
values
(1, 'R', -10)
,(1, 'R', 30)
,(1, 'V', 10)
,(2, 'R', 20)
,(2, 'R', -5)
,(2, 'V', 5)
,(3, 'R', 20)
,(3, 'R', 30)
) x (accountId, type, amount);
SELECT
accountId
, type
, CASE WHEN part_min < 0 THEN part_sum else amount END amount
FROM (
SELECT
accountId
, type
, amount
, SUM(amount) OVER (PARTITION BY accountId, type) part_sum
, MIN(amount) OVER (PARTITION BY accountId, type) part_min
, ROW_NUMBER() OVER (PARTITION BY accountId, type ORDER BY (SELECT NULL)) part_rn
FROM #t220618
) q
WHERE q.part_min > 0 OR (part_min < 0 AND part_rn = 1);