我有一张分数如下的表
.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中还有其他替代方法吗?
先谢谢了。
答案 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
答案 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 |
+--------+-------+