选择运行总计,直到达到特定 SUM
Posted
技术标签:
【中文标题】选择运行总计,直到达到特定 SUM【英文标题】:Select running total until specific SUM is reached 【发布时间】:2013-01-08 15:20:16 【问题描述】:我正在尝试从下表变量中选择前 n 个 rowid 值,这将使我接近 200,000 的 sum(itemcount) 而不会超过该阈值。如果我手动查看这个,我会选择前 3 行。除非没有基于纯集的方式,否则我不想使用游标。
什么是基于集合的好方法来获取所有 rowid 值“总和而/直到”我达到 200,000 的运行总数?
我在http://www.1keydata.com/sql/sql-running-totals.html 上查看了“运行总计”,但这似乎不会奏效,因为实际表有大约 50 万行。
这是我迄今为止尝试过的:
declare @agestuff table ( rowid int primary key , itemcount int , itemage datetime )
insert into @agestuff values ( 1 , 175000 , '2013-01-24 17:21:40' )
insert into @agestuff values ( 2 , 300 , '2013-01-24 17:22:11' )
insert into @agestuff values ( 3 , 10000 , '2013-01-24 17:22:11' )
insert into @agestuff values ( 4 , 19000 , '2013-01-24 17:22:19' )
insert into @agestuff values ( 5 , 16000 , '2013-01-24 17:22:22' )
insert into @agestuff values ( 6 , 400 , '2013-01-24 17:23:06' )
insert into @agestuff values ( 7 , 25000 , '2013-01-24 17:23:06' )
select sum(itemcount) from @agestuff -- 245700 which is too many
select sum(itemcount) from @agestuff
where rowid in (1,2,3) -- 185300 which gets me as close as possible
使用 SQL Server 2008。如果需要,我将切换到 2012。
【问题讨论】:
【参考方案1】:窗口函数 - 仅限 SQL Server 2012
DECLARE @point INT = 200000;
;WITH x(rowid, ic, r, s) AS
(
SELECT
rowid, itemcount, ROW_NUMBER() OVER (ORDER BY itemage, rowid),
SUM(itemcount) OVER (ORDER BY [itemage], rowid RANGE UNBOUNDED PRECEDING)
FROM @agestuff
)
SELECT x.rowid, x.ic, x.s
FROM x WHERE x.s <= @point
ORDER BY x.rowid;
结果:
rowid ic sum
----- ------ ------
1 175000 175000
2 300 175300
3 10000 185300
SQL fiddle demo
如果由于某种原因您不能使用 SQL Server 2012,那么在 SQL Server 2008 上,您可以使用几个替代方案:
古怪的更新
请注意,此行为未记录在案,也不保证以正确的顺序计算您的运行总计。因此,请自行承担使用风险。
DECLARE @st TABLE
(
rowid INT PRIMARY KEY,
itemcount INT,
s INT
);
DECLARE @RunningTotal INT = 0;
INSERT @st(rowid, itemcount, s)
SELECT rowid, itemcount, 0
FROM @agestuff
ORDER BY rowid;
UPDATE @st
SET @RunningTotal = s = @RunningTotal + itemcount
FROM @st;
SELECT rowid, itemcount, s
FROM @st
WHERE s < @point
ORDER BY rowid;
光标
DECLARE @st TABLE
(
rowid INT PRIMARY KEY, itemcount INT, s INT
);
DECLARE
@rowid INT, @itemcount INT, @RunningTotal INT = 0;
DECLARE c CURSOR LOCAL FAST_FORWARD
FOR SELECT rowid, itemcount
FROM @agestuff ORDER BY rowid;
OPEN c;
FETCH c INTO @rowid, @itemcount;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @RunningTotal = @RunningTotal + @itemcount;
IF @RunningTotal > @point
BREAK;
INSERT @st(rowid, itemcount, s)
SELECT @rowid, @itemcount, @RunningTotal;
FETCH c INTO @rowid, @itemcount;
END
CLOSE c;
DEALLOCATE c;
SELECT rowid, itemcount, s
FROM @st
ORDER BY rowid;
我只选择了两个备选方案,因为其他备选方案更不受欢迎(主要是从性能角度来看)。您可以在以下博客文章中看到它们,其中包含有关它们如何执行的一些背景信息以及有关潜在问题的更多信息。不要把自己画到一个角落,因为你坚持认为光标不好 - 有时,就像在这种情况下,它们可能是最有效的支持和可靠的选择:
http://www.sqlperformance.com/2012/07/t-sql-queries/running-totals
【讨论】:
以上是关于选择运行总计,直到达到特定 SUM的主要内容,如果未能解决你的问题,请参考以下文章