繁体   English   中英

SQL Server模数运算符跳到大表上的第n行

[英]SQL server modulus operator to skip to every n'th row on a large table

我有一个有100,000,000行的大表。 我想从表中选择第n行。 我的第一个本能是使用这样的东西:

SELECT id,name FROM table WHERE id%125000=0

检索均匀的800行(id是聚簇索引)

该技术在较小的数据集上效果很好,但是对于我的较大表,查询需要2.5分钟。 我认为这是因为模运算适用于每一行。 有没有更优化的行跳过方法?

时间不是在进行模运算本身,而是在为您实际想要的每一行(即表扫描或聚簇索引扫描)读取124,999条不必要的行。

乍看起来,加快查询速度的唯一方法似乎是不合逻辑的:仅在该列([ID])上添加一个额外的非聚集索引。 此外,您可能必须添加索引提示以强制其使用该索引。 最后,虽然对于125,000+的模数,它可能实际上并没有使其更快(尽管它永远不会真正快)。


如果您的ID不一定是连续的(任何删除的行都会在很大程度上造成这种情况),并且确实确实需要按ID顺序精确地使用每个模数行 ,那么您仍然可以使用上述方法,但是您必须为ID重新排序在查询中使用ROW_NUMBER() OVER(ORDER BY ID)进行模运算。

您的查询假设这些ID是连续的(并且可能在您没有意识到这一点的情况下才并非如此...)。 无论如何,您应该自己生成ID:

select *
from T
where ID in (0, 250000*1, 250000*2, ...)

也许您需要一个TVP来发送所有ID,因为数量太多了。 或者,您在T-SQL或SQLCLR函数或数字表中的服务器上生成ID。

此技术使您可以执行索引查找,并且将是可能产生的最快速度。 它读取的数据量最少。

模数不可保存。 如果Microsoft愿意,SQL Server可以支持它,但这是一个奇特的用例。 他们永远不会使模可SARGable,也不应这样做。

如果id在索引中,那么我正在考虑以下方面:

with ids as (
      select 1 as id
      union all
      select id + 125000
      from ids
      where id <= 100000000
  )
select ids.id,
       (select name from table t where t.id = ids.id) as name
from ids
option (MAXRECURSION 1000);

我认为此公式将使用表上的索引。

编辑:

在考虑这种方法时,您实际上可以使用它来获取表中的实际随机ID,而不仅仅是均匀分布的ID:

with ids as (
      select 1 as cnt,
             ABS(CONVERT(BIGINT,CONVERT(BINARY(8), NEWID()))) % 100000000 as id
      union all
      select cnt + 1, ABS(CONVERT(BIGINT,CONVERT(BINARY(8), NEWID()))) % 100000000
      from ids
      where cnt < 800
  )

select ids.id,
       (select name from table t where t.id = ids.id) as name
from ids
option (MAXRECURSION 1000);

实际随机数生成器的代码来自此处

编辑:

由于SQL Server中的古怪之处,即使在您的方案中,您仍然可以获得非连续的ID。 这个被接受的答案解释了原因。 简而言之,身份值不是一次分配,而是成组分配。 服务器可能会失败,甚至未使用的值也会被跳过。

我想要进行随机抽样的一个原因是为了避免这一问题。 据推测,在大多数系统上上述情况很少见。 您可以使用随机抽样生成900个ID。 从这些中,您应该能够找到实际可用于样本的800。

 DECLARE @i int, @max int, @query VARCHAR(1000)
 SET @i = 0
 SET @max = (SELECT max(id)/125000 FROM Table1)
 SET @query = 'SELECT id, name FROM Table1 WHERE id in ('
 WHILE @i <= @max
 BEGIN
     IF @i > 0 SET @query = @query + ','
     SET @query = @query + CAST(@i*125000 as varchar(12))
     SET @i = @i + 1
 END
 SET @query = @query + ')'
 EXEC(@query)

编辑:

为了避免在非连续ID情况下出现任何“漏洞”,您可以尝试执行以下操作:

DECLARE @i int, @start int, @id int, @max int, @query VARCHAR(1000)
SET @i = 0
SET @max = (SELECT max(id)/125000 FROM Table1)
SET @query = 'SELECT id, name FROM Table1 WHERE id in ('
WHILE @i <= @max
BEGIN
    SET @start = @i*125000
    SET @id = (SELECT TOP 1 id FROM Table1 WHERE id >= @start ORDER BY id ASC)
    IF @i > 0 SET @query = @query + ','
    SET @query = @query + CAST(@id as VARCHAR(12))
    SET @i = @i + 1
END
SET @query = @query + ')'
EXEC(@query)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM