累计总和不低于零

时间:2018-08-07 11:57:55

标签: sql sql-server

我有一张分数如下的表

.spampro

我正在寻找的是SQL Server(2012年起)中的累积SUM,因此它忽略了负底限,即SUM不能低于零。因此,在上述情况下,将为 2 + 3 = 5,+ -8 = 0,+ 6 = 6 。所以我想要的输出是

 RatingID | UserID| Score | 
   1      |   1   |   2   | 
   2      |   1   |   3   | 
   3      |   1   |   -8  | 
   4      |   1   |   6   | 
   5      |   2   |   3   | 

我已经研究了循环和游标,但是事实证明,这些选择对于大选择而言效率不高。 MSSQL中还有其他替代方法吗?

先谢谢了。

3 个答案:

答案 0 :(得分:1)

下面的示例包含一个ID,因为您需要一些东西来按累积和排序分数。

它使用具有特殊规则的递归CTE作为累积和。

可以找到SQL小提琴here

示例代码段

declare @Scores table (ID int identity(1,1) primary key, UserID int, Score int);

insert into @Scores (UserId, Score) values
(1,2),
(1,3),
(1,-8),
(1,6),
(2,3);

with CTE as
(
  select ID, UserId, Score,
  row_number() over (partition by UserID order by ID) as RN,
  row_number() over (partition by UserID order by ID desc) as reverseRN
  from @Scores
)
, RCTE as
(
  select ID, UserId, Score, RN, reverseRN, 
   iif(Score>=0,Score,0) as SpecialCummSum
  from CTE 
  where RN = 1

  union all

  select c.ID, c.UserId, c.Score,  c.RN, c.reverseRN, 
   case when r.SpecialCummSum + c.Score >= 0 then r.SpecialCummSum + c.Score else 0 end
  from RCTE r
  join CTE c on c.UserId = r.UserId AND c.RN = r.RN + 1
)
select UserId, SpecialCummSum as Score
from RCTE
where reverseRN = 1
order by UserId;

是大桌子吗?
然后,首先将数据加载到临时表中可能是值得的。具有主键和IDENTITY的一个键。
因为递归CTE可以从主键上的索引中受益。

例如:

IF OBJECT_ID('tempdb..#tmpScores') IS NOT NULL DROP TABLE #tmpScores;
CREATE TABLE #tmpScores ( 
  ID int identity(1,1) primary key, 
  UserID int, 
  Score int,
  RN int,
  ReverseRN int
);

insert into #tmpScores 
(UserId, Score, RN, ReverseRN)
select UserId, Score,
row_number() over (partition by UserID order by RatingID) as RN,
row_number() over (partition by UserID order by RatingID desc) as reverseRN
from Scores
order by UserId, RatingID;

;with RCTE as
(
  select ID, UserId, Score, RN, reverseRN, iif(Score>=0,Score,0) as SpecialCummSum
  from #tmpScores 
  where RN = 1

  union all

  select t.ID, t.UserId, t.Score, t.RN, t.reverseRN, 
   case when r.SpecialCummSum + t.Score >= 0 then r.SpecialCummSum + t.Score else 0 end
  from RCTE r
  join #tmpScores t 
     on t.ID = r.ID + 1 AND t.UserId = r.UserId
     -- Using ID since it has an index and is sequencial without gaps
)
select UserId, SpecialCummSum as Score
from RCTE
where reverseRN = 1
order by UserId;

答案 1 :(得分:0)

IF OBJECT_ID('tempdb..#retyy') IS NOT NULL
    DROP TABLE #retyy

CREATE TABLE #retyy (
    ident INT identity(1, 1),
    [UserID] INT,
    [Score] INT
    )
    insert into #retyy([UserID], [Score])
select * from (VALUES
    (1, 2),
    (1, 3),
    (1, -8),
    (1, 6),
    (2, 3))a([UserID], [Score])


   select [UserID],score from(SELECT [UserID],
        iif(score < 0, 0, score + isnull((
                    iif(lag(score) OVER (
                            PARTITION BY [UserID] ORDER BY ident
                            ) < 0, 0, lag(score) OVER (
                            PARTITION BY [UserID] ORDER BY ident
                            ))
                    ), 0)) score
,row_number() over (partition by [UserID] order by ident desc) rn
    FROM #retyy) a where rn=1

link for the results

答案 2 :(得分:0)

要添加到@ Smart003答案中,要获得问题中所需的实际输出,只需应用过滤以仅选择每个UserId运行总计中的最后一行:

declare @t table(RatingID int,UserID int,Score int)
insert into @t values
 (1,1,2 ) 
,(2,1,3 ) 
,(3,1,-8) 
,(4,1,6 )
,(5,2,3 )
;


select UserId
        ,Score
from(
    select RatingID
        ,UserID
        ,case when score < 0
                then 0
                else score+isnull(case when lag(score) over (partition by UserID order by RatingID) < 0
                                        then 0
                                        else lag(score) over (partition by [UserID] order by RatingID)
                                        end
                                ,0)
                end as Score
        ,row_number() over (partition by UserId order by RatingID desc) as rn
    from @t
) a
where rn = 1;

输出:

+--------+-------+
| UserId | Score |
+--------+-------+
|      1 |     6 |
|      2 |     3 |
+--------+-------+