如果至少是负数,则求和;否则就显示

Kev*_*vin 5 sql-server aggregate window-functions group-by sql-server-2016

仅当分区中存在负值时,我才需要将值相加。如果分区中没有负值,它应该只输出行。

这就是我现在所拥有的。初始数据作为 CTE 提供。

数据管理语言

;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
Run Code Online (Sandbox Code Playgroud)

我期待这样的输出,并且上面的查询输出正确。

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)
Run Code Online (Sandbox Code Playgroud)

到目前为止,这个查询看起来很糟糕,而且感觉不必要地复杂。是否有我可以编写但我忽略的更简单的查询?

这是该问题的一个细微变化:如果为正,则对所有项目求和。如果为负,则返回每一个

reextester -> https://rextester.com/EZRT33825

Joe*_*ish 6

您可以在使用窗口函数后完成您的任务,但如果您在很多行上运行此查询,我无法对性能做出任何承诺。这个想法是计算每个分区的总和、最小值和一个无序的行号。保留最小值 > 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);
Run Code Online (Sandbox Code Playgroud)