2013-09-03 8 views
5

私は100,000,000行の大きなテーブルを持っています。テーブルからn行目ごとに選択したいと思います。この技術は、小さなデータセット上の罰金​​が、私の大きなテーブルで動作します大きなテーブルのn番目の行にスキップするSQLサーバーモジュラス演算子

(idは、クラスタ化インデックスである)でも800行の広がりを取得するために

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

:私の最初の本能は、このようなものを使用することですクエリには2.5分かかります。これは、モジュラス演算がすべての行に適用されるためです。列スキップのより最適な方法はありますか?

+1

これを行う目的は何ですか:

非連続的なIDの状況で任意の「穴」を避けるために、あなたはこのような何かを試すことができますか?結果のランダムなサブセットを取得しようとしていますか? –

答えて

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

私は、この製剤は、テーブルの上にインデックスを使用すると思います。

EDIT:

私はこのアプローチを考えると、あなたは実際には、テーブルではなく、単に等間隔のもの、実際のランダム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から来ました。

EDIT:

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

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

+0

私はこの答えを正しいとマークしていますが、他の最も優れた貢献者にとって公平であるためには、私のユースケースの詳細を質問に追加するべきです。 テーブルはデータロガーから生成されます。テーブルが静的なままであるので、id値は連続していることが保証されています。 Gordonの回答が私のアプリケーションに合っている理由は、データの概要グラフを作成しようとしているため、ランダムなサンプルを使用すると十分です。 ちなみに、理論的には同じ結果が得られるはずのTABLESAMPLEを試しましたが、少なくともSQL Server 2012では本当にランダムな選択肢が表示されません。 – Brian

2

時間はモジュラス演算自体には入っていませんが、実際に必要なすべての行(つまり、テーブルスキャンまたはクラスタ化インデックススキャン)で124,999行を読み込むだけです。

このようなクエリをスピードアップする唯一の方法は、最初は不合理であると思われるものです。その列([ID])に余分な非クラスタ化インデックスを追加します。さらに、インデックスヒントを追加して、そのインデックスを強制的に使用する必要があります。そして最終的には、それは実際にはそれを速くすることはできませんが、125,000+のモジュラスでは、(それは決して本当に速くないでしょう)。


あなたのIDが(任意の削除された行はかなり原因この意志)は必ずしも連続していないとあなたが本当に正確すべての剰余行を必要がある場合は、IDの順によって、あなたはまだ上記のアプローチを使用することができますが、クエリでROW_NUMBER() OVER(ORDER BY ID)を使用してモジュロ演算のIDを並べ替える必要があります。

+0

フィルタリングされたインデックスがモジュロ演算子をサポートしていれば、またはモジュロを計算した計算カラムを使用できれば素晴らしいでしょう。 –

+0

@AaronBertrandもちろん、追加の列を手動で設定して同じ効果を実現することもできます。 – RBarryYoung

2

あなたの質問は、IDが連続していることを前提としています(おそらく、あなたはこれを認識していないわけではありません)。とにかく、あなたは自分のIDを生成する必要があります:

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

はたぶん、あなたは非常に多く存在するため、すべてのIDを送信するTVPを必要としています。または、T-SQLまたはSQLCLR関数または数値表でサーバー上のIDを生成します。

この手法を使用すると、インデックスシークを実行できます。それは可能な最小量のデータを読み込みます。

モジュロはSARGableではありません。マイクロソフトがそれを望むなら、SQL Serverはこれをサポートすることができますが、これはエキゾチックなユースケースです。彼らはSARGableを法とすることは決してありません。

+0

パフォーマンスの影響についてはわかりませんが、IN(...)を使用する代わりにIN(...)を使用する代わりに、( として125000を選択してください。 ユニオンすべて 選択番号+ 125000番号<(125000 * 800) ) が:-) ...誰かが非連続IDの問題を言及するつもりだった場合、私は思っていたNumberRange オプション(MAXRECURSION 800) ' –

+0

SELECT * FROM NumberRange – RBarryYoung

+0

から 問題は、すべてのID値が入力されるわけではないため、多くの行が失われる可能性があるということです。 –

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) 

EDIT:

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) 
関連する問題