6

我正在尝试从下表变量中选择前 n 个 rowid 值,这将使我在不超过该阈值的情况下接近 200,000 的 sum(itemcount)。如果我手动查看这个,我只会选择前 3 行。除非没有基于纯集的方式,否则我不想使用游标。

什么是基于集合的好方法来获取所有 rowid 值“总和而/直到”我达到 200,000 的运行总数?

我查看了http://www.1keydata.com/sql/sql-running-totals.html上的“运行总计”,但这似乎不会奏效,因为真正的表有大约 500k 行。

这是我到目前为止所尝试的:

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。

4

1 回答 1

15

窗口函数 - 仅限 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小提琴演示

如果由于某种原因您不能使用 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

于 2013-01-24T19:14:58.367 回答