web-dev-qa-db-ja.com

少なくとも負の数の場合、合計します。そうでなければ単に表示

パーティションに負の値が存在する場合にのみ、値を合計する必要があります。パーティションに負の値がない場合は、行を出力するだけです。

これが私が今持っているものです。初期データは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

5
Kevin

ウィンドウ関数を使用した後の結果を達成できますが、このクエリを多数の行に対して実行している場合、パフォーマンスについては保証できません。アイデアは、すべてのパーティションの合計、最小、および順序付けされていない行番号を計算することです。最小> 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);
6
Joe Obbish