5

100,000,000 行の大きなテーブルがあります。テーブルから n 行ごとに選択したいと思います。私の最初の本能は、次のようなものを使用することです。

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

800 行の均等なスプレッドを取得する (id はクラスター化インデックスです)

この手法は小さなデータ セットでは問題なく機能しますが、私の大きなテーブルではクエリに 2.5 分かかります。これは、モジュラス操作がすべての行に適用されるためだと思います。行スキップのより最適な方法はありますか?

4

4 に答える 4

2

クエリは、ID が連続していることを前提としています (そして、おそらく、これを認識していなければそうではありません...)。とにかく、ID を自分で生成する必要があります。

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

非常に多くのIDがあるため、すべてのIDを送信するにはTVPが必要になる場合があります。または、サーバー上の T-SQL、SQLCLR 関数、または数値テーブルで ID を生成します。

この手法を使用すると、インデックス シークを実行でき、生成できる最速の手法になります。可能な最小量のデータを読み取ります。

Modulo は SARGable ではありません。Microsoft が希望すれば、SQL Server はこれをサポートできますが、これは風変わりな使用例です。modulo SARGable を作成することは決してありませんし、すべきではありません。

于 2013-09-03T19:22:37.277 に答える
1

がインデックスにある場合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 を取得することができます。

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);

実際の乱数ジェネレーターのコードはhereから来ました。

編集:

SQL Server の癖により、シナリオ内であっても連続していない ID を取得できます。この受け入れられた答えは原因を説明しています。つまり、ID 値は一度に 1 つずつ割り当てられるのではなく、グループで割り当てられます。サーバーが失敗する可能性があり、未使用の値でさえスキップされます。

ランダム サンプリングを実行する理由の 1 つは、この問題を回避するためです。おそらく、上記の状況はほとんどのシステムでかなりまれです。ランダム サンプリングを使用して、たとえば 900 個の ID を生成できます。これらから、サンプルに実際に使用できる 800 個を見つけることができるはずです。

于 2013-09-03T20:02:00.553 に答える
0
 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)
于 2013-09-03T21:00:30.220 に答える