データベーステーブルからのランダムレコード(T-SQL)


85

SQLサーバーテーブルからランダムレコードを取得する簡潔な方法はありますか?

ユニットテストデータをランダム化したいので、テーブルからランダムIDを選択する簡単な方法を探しています。英語では、selectは「テーブルから1つのIDを選択します。このIDは、テーブル内の最小IDとテーブル内の最大IDの間の乱数です。」

クエリを実行し、null値をテストしてから、nullの場合は再実行する必要がないので、それを実行する方法を見つけることはできません。

アイデア?


ここにいくつかのメソッドがありますbrettb.com/SQL_Help_Random_Numbers.asp–
メッシュ

2
このアプローチを採用してもよろしいですか?単体テストのデータはランダムであってはなりません。実際、単体テストを何度実行しても、同じ結果が得られることが保証されているはずです。ランダムなデータがあると、単体テストのこの基本原則に違反する可能性があります。
抑制

@Meshからの上記のリンクはアクティブではなくなりました。
RobertSievers18年

回答:


145

SQLサーバーテーブルからランダムレコードを取得する簡潔な方法はありますか?

はい

SELECT TOP 1 * FROM table ORDER BY NEWID()

説明

NEWID()行ごとにAが生成され、テーブルはそれによってソートされます。最初のレコードが返されます(つまり、「最も低い」GUIDを持つレコード)。

ノート

  1. バージョン4以降、GUIDは疑似乱数として生成されます。

    バージョン4のUUIDは、真の乱数または疑似乱数からUUIDを生成するためのものです。

    アルゴリズムは次のとおりです。

    • clock_seq_hi_and_reservedの最上位2ビット(ビット6と7)をそれぞれ0と1に設定します。
    • time_hi_and_versionフィールドの最上位4ビット(ビット12から15)をセクション4.1.3の4ビットバージョン番号に設定します。
    • 他のすべてのビットをランダムに(または疑似ランダムに)選択された値に設定します。

    UUID(Universally Unique IDentifier)URN名前空間-RFC 4122

  2. 代替案SELECT TOP 1 * FROM table ORDER BY RAND()は、人が考えるようには機能しません。RAND()クエリごとに1つの値を返すため、すべての行が同じ値を共有します。

  3. GUID値は疑似ランダムですが、より要求の厳しいアプリケーションには、より優れたPRNGが必要になります。

  4. 通常のパフォーマンスは、約1,000,000行で10秒未満です。もちろん、システムによって異なります。インデックスをヒットすることは不可能であるため、パフォーマンスが比較的制限されることに注意してください。


まさに私が探していたもの。作っているよりもシンプルだと感じました。
ジェレミー

1
NEWIDが疑似乱数値を生成すると想定しています。順次値が生成される可能性は十分にあります。NEWIDは一意の値を生成するだけです。ただし、RANDは疑似乱数値を生成します。
スキズ2008年

1,671,145行のインデックスの多いテーブルで実行していますが、戻るまでに7秒かかります。テーブルもかなり最適です-それは事実上私たちのデータベースの中心なので、世話をします。
トムリッター

@ÂviewAnew。インデックスにヒットしない(そしてヒットできない)選択で160万行7秒は悪くありません。
sklivvz 2008年

7
@ Skizz、randはそのようには機能しません。SELECTの前にSINGLEランダム値が生成されます。したがって、「SELECT TOP 10 RAND()...」を試してみると、常に同じ値が得られます
Sklivvz 2008年

27

より大きなテーブルではTABLESAMPLE、これを使用してテーブル全体をスキャンしないようにすることもできます。

SELECT  TOP 1 *
FROM YourTable
TABLESAMPLE (1000 ROWS)
ORDER BY NEWID()

ORDER BY NEWIDデータページに最初に表示される行を返すことを避けるために、は引き続き必要です。

使用する数は、テーブルのサイズと定義に合わせて慎重に選択する必要があり、行が返されない場合は再試行ロジックを検討することをお勧めします。この背後にある数学と、この手法が小さなテーブルに適していない理由について、ここ説明します。


これはMicrosoftのWebサイトで見つかりました。次のいずれかの条件が当てはまる場合、TABLESAMPLEを使用して、大きなテーブルからサンプルをすばやく返すことができます。サンプルは、個々の行のレベルで真にランダムなサンプルである必要はありません。テーブルの個々のページの行は、同じページの他の行と相関していません。
Mark Entingh 2017

1
@ MarkEntingh-この場合TOP 1、同じページの行が相関しているかどうかは関係ありません。あなたはそれらのうちの1つだけを選んでいます。
マーティンスミス

9

また、メソッドを試して、MIN(Id)とMAX(Id)の間のランダムなIDを取得してから、

SELECT TOP 1 * FROM table WHERE Id >= @yourrandomid

常に1行になります。


2
-1、これは、最小値と最大値の間に欠落しているIDがない場合にのみ機能します。1つが削除されると、同じIDがランダム関数によって生成され、ゼロレコードが返されます。
ニールN

6
@Neil、実際にはそうではありません-IDが欠落している場合、乱数よりも大きいIDを持つ最初の行が取得されます。ここでの問題は、各行が出てくる確率が一定ではないことです。しかし、ほとんどの場合、これで十分です。
sklivvz 2011年

1
+1。十分に良い異なる値をヒットするはずの単体テストの場合-実際のランダムを必要とする場合、これは別のものです。しかし、OPのコンテキストでは、それで十分なはずです。
TomTom 2012年

7

大きなデータを選択したい場合、私が知っている最善の方法は次のとおりです。

SELECT * FROM Table1
WHERE (ABS(CAST(
    (BINARY_CHECKSUM
    (keycol1, NEWID())) as int))
    % 100) < 10

出典:MSDN


よくわかりませんが、選択プロセスでNEWID()を使用することの欠点があるため、真に乱数を生成するためにNEWID()ではなくRAND()を使用する方が良いと思います。
QMaster 2016

パーセントベースではなく正確なレコード数でこの方法を使用してみましたが、選択範囲を拡大し、TOP nで制限しましたが、何か提案はありますか?
QMaster 2016

このシナリオで別の問題が見つかりました。groupbyを使用すると、ランダムに選択された行の順序が常に同じになるため、小さなテーブルでは@skilvvzアプローチが最も適切であるように見えます。
QMaster 2016

0

私は自分が試した方法を改善しようとしていて、この投稿に出くわしました。古いことに気づきましたが、この方法は記載されていません。テストデータを作成して適用しています。これは、@ st(2文字の状態)で呼び出されたSPの「アドレス」のメソッドを示しています。

Create Table ##TmpAddress (id Int Identity(1,1), street VarChar(50), city VarChar(50), st VarChar(2), zip VarChar(5))
Insert Into ##TmpAddress(street, city, st, zip)
Select street, city, st, zip 
From tbl_Address (NOLOCK)
Where st = @st


-- unseeded RAND() will return the same number when called in rapid succession so
-- here, I seed it with a guaranteed different number each time. @@ROWCOUNT is the count from the most recent table operation.

Set @csr = Ceiling(RAND(convert(varbinary, newid())) * @@ROWCOUNT)

Select street, city, st, Right(('00000' + ltrim(zip)),5) As zip
From ##tmpAddress (NOLOCK)
Where id = @csr

0

個々の行のランダムサンプルが本当に必要な場合は、TABLESAMPLEを使用する代わりに、クエリを変更して行をランダムに除外します。たとえば、次のクエリはNEWID関数を使用して、Sales.SalesOrderDetailテーブルの行の約1パーセントを返します。

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)

SalesOrderID列はCHECKSUM式に含まれているため、NEWID()は行ごとに1回評価して、行ごとにサンプリングを実行します。式CAST(CHECKSUM(NEWID()、SalesOrderID)&0x7fffffff AS float / CAST(0x7fffffff AS int)は、0から1までのランダムなfloat値に評価されます。」

ソース:http//technet.microsoft.com/en-us/library/ms189108(v = sql.105).aspx

これについては、以下でさらに説明します。

これはどのように作動しますか?WHERE句を分割して説明しましょう。

CHECKSUM関数は、リスト内の項目のチェックサムを計算しています。NEWID()は新しいランダムGUIDを返す関数であるため、SalesOrderIDが必要かどうかについては議論の余地があります。したがって、ランダムな数値に定数を掛けると、いずれの場合もランダムになります。実際、SalesOrderIDを除外しても違いはないようです。あなたが熱心な統計学者であり、これを含めることを正当化できる場合は、以下のコメントセクションを使用して、私が間違っている理由を教えてください!

CHECKSUM関数はVARBINARYを返します。バイナリの(111111111 ...)に相当する0x7fffffffを使用してビット単位のAND演算を実行すると、0と1のランダムな文字列を効果的に表す10進値が生成されます。係数0x7fffffffで除算すると、この10進数が0〜1の数値に効果的に正規化されます。次に、各行が最終結果セットに含める価値があるかどうかを判断するために、1 / xのしきい値(この場合は0.01)が使用されます。 xは、サンプルとして取得するデータのパーセンテージです。

ソース:https//www.mssqltips.com/sqlservertip/3157/different-ways-to-get-random-data-for-sql-server-data-sampling

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.