SQLでランダムな行をリクエストする方法は?


510

純粋なSQLでランダムな行(または可能な限り真にランダムな行に近い行)を要求するにはどうすればよいですか?


私はいつもsqlからのクエリ結果の後にphpでこれを実行していました...これはおそらくソリューションの制限1の付属物
CheeseConQueso


2
すべてのdbmsで実行される「純粋なSQL」ソリューションはないようです...それぞれのソリューションがあります。
Manu

回答:


735

この投稿を参照してください:データベーステーブルからランダムな行を選択するSQL。これは、MySQL、PostgreSQL、Microsoft SQL Server、IBM DB2、Oracleでこれを行うためのメソッドを通過します(以下はそのリンクからコピーされます)。

MySQLでランダムな行を選択します。

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

PostgreSQLでランダムな行を選択します。

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

Microsoft SQL Serverでランダムな行を選択します。

SELECT TOP 1 column FROM table
ORDER BY NEWID()

IBM DB2でランダムな行を選択する

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

Oracleでランダムなレコードを選択します。

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1

30
-1はorder by rand()、すべてのdb に依存している、または同等のものである場合:|。ここにも触れます
AD7six 2014年

20
10年前、一部の人は使用ORDER BY RAND()が間違っていると言いました...
trejder

ORDER BY NEWID()は、SQL Serverでは著しく遅いようです。私のクエリは次のようになります。C.CustomerId= CL.CustomerIdグループのCustomerLogin CL on C.CustomerId = CL.CustomerIdグループのC.CustomerId、CLのCustomer C内部結合LinkedAccount LAから上位1000 C.CustomerId、CL.LoginNameを選択します。 NEWID()によるcount(*)> 1の順序を持​​つLoginName「NEWID()による順序」行を削除すると、結果がはるかに速く返されます。
Ben Power

3
SQLiteの場合、RANDOM()関数を使用します。
スラム

10
これらのソリューションは拡張できません。彼らはあるO(n)n、テーブル内のレコードの数です。100万件のレコードがあるとします。100万件の乱数または一意のIDを本当に生成しますか?私はむしろそれを使用して、単一の乱数を持つCOUNT()新しいLIMIT式にそれを含めます。
Christian Hujer、2016年

174

Jeremiesのようなソリューション:

SELECT * FROM table ORDER BY RAND() LIMIT 1

機能しますが、すべてのテーブルのシーケンシャルスキャンが必要です(各行に関連付けられたランダムな値を計算する必要があるため、最小の値を決定できるため)。これは、中規模のテーブルでも非常に遅くなることがあります。私の推奨は、ある種のインデックス付き数値列(多くのテーブルはこれらを主キーとして持っている)を使用し、次のように書くことです。

SELECT * FROM table WHERE num_value >= RAND() * 
    ( SELECT MAX (num_value ) FROM table ) 
ORDER BY num_value LIMIT 1

これnum_valueは、インデックスが付けられている場合、テーブルサイズに関係なく、対数時間で機能します。注意点の1つ:これはnum_value、範囲内で均等に分散されていることを前提としています0..MAX(num_value)。データセットがこの仮定から大きく外れている場合、結果は歪んでいます(一部の行は他の行よりも頻繁に表示されます)。


8
2番目の提案はランダムではありません。ピックされる行を予測することはできませんが、賭ける必要がある場合は、2番目の行に賭けます。そして、あなたは最後の行に賭けることは決してないでしょう、それはあなたのnum_valueの分布やテーブルの大きさが何であれ、選ばれる可能性が低いです。
エティエンヌラシーン

1
通常、RAND()関数はそれほど高品質ではないことを知っていますが、それ以外の場合、選択がランダムにならない理由を詳しく説明できますか?
グレイパンサー

13
1つ目は、SQL ServerのWRONGです。RAND()関数は、行ごとではなく、クエリごとに1回だけ呼び出されます。したがって、常に最初の行が選択されます(試してください)。
ジェフウォーカーコードレンジャー

3
2つ目は、すべての行が考慮されていることも前提としています。削除された行を選択する可能性があります。
Sam Rueby

3
@ Sam.Rueby実際には、num_value> = RAND()...制限1により、既存の行が見つかるまで空の行がスキップされます。
ghord

62

これがどれほど効率的かわかりませんが、以前に使用したことがあります。

SELECT TOP 1 * FROM MyTable ORDER BY newid()

GUIDはかなりランダムであるため、順序付けはランダムな行を取得することを意味します。


1
私はMS SQLサーバーを使用しています。SELECTTOP 1 * FROM some_table_name ORDER BY NEWID()はうまくいきました。アドバイスをありがとうございます。

それはまったく同じことですORDER BY RAND() LIMIT 1
ケンブルーム

6
とを使用TOP 1してnewid()いるため、これもデータベース固有です。
グレイ

12
これは悪い考えです。このメソッドは、各列に個別にインデックスを付けない限り、インデックスを使用しません。1億レコードのテーブルでは、1つのレコードを取得するのに非常に長い時間がかかる場合があります。
2012

1
@Switchとどのようなソリューションを提案しますか?
Akmal Salikhov

31
ORDER BY NEWID()

取る 7.4 milliseconds

WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table)

取る0.0065 milliseconds

私は間違いなく後者の方法で行きます。


2
2番目のオプションでは、最後の行は選択されません。理由はわかりません-指摘するだけです。
Voldemort 2014年

7
@Voldemort:rand()浮動小数点数を返しnどこ0 < n < 1num_valueが整数であると仮定すると、の戻り値rand() * max(num_value)も整数に強制変換されるため、小数点以下は切り捨てられます。したがって、rand() * max(num_value)常により小さいmax(num_value)ため、最後の行は選択されません。
Ian Kemp

データが頻繁に削除されると効率が悪くなります。ギャップが見つかった場合は、クエリ全体を再実行する必要があります。
Loic Coenen

1
@IanKemp愚かな質問、それではなぜSELECT MAX(num_value)+ 1を使用しないのですか?rand(またはほとんどの場合はRANDOM)は[0,1)を返すため、値の全範囲を取得できます。また、そうです、そうです、クエリを修正する必要があります。
tekHedd 2018

13

どのサーバーを使用しているかは言いませんでした。古いバージョンのSQL Serverでは、これを使用できます。

select top 1 * from mytable order by newid()

SQL Server 2005以降では、を使用TABLESAMPLEして、反復可能なランダムサンプルを取得できます。

SELECT FirstName, LastName
FROM Contact 
TABLESAMPLE (1 ROWS) ;

9
MSDNでは、真にランダムな結果を得るには、newid()がtablesampleよりも優先されるとしています:msdn.microsoft.com/en-us/library/ms189108.aspx
Andrew Hedges

7
@Andrew Hedges:ORDER BY NEWID()は高すぎる
AndreiRînea10年

10

SQL Serverの場合

newid()/ order byは機能しますが、すべての行のIDを生成してソートする必要があるため、大きな結果セットの場合は非常に負荷がかかります。

TABLESAMPLE()はパフォーマンスの観点からは優れていますが、結果が集中します(ページのすべての行が返されます)。

パフォーマンスの高い真のランダムサンプルの場合、最善の方法は、行をランダムに除外することです。SQL Server Books Onlineの記事「TABLESAMPLEを使用して結果セットを制限する」で次のコードサンプルを見つけました。

個々の行のランダムなサンプルが本当に必要な場合は、クエリを変更して、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の間のランダムな浮動小数点値に評価されます。

1,000,000行のテーブルに対して実行すると、次のような結果になります。

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

TABLESAMPLEを使用して問題を回避できる場合は、最高のパフォーマンスが得られます。それ以外の場合は、newid()/ filterメソッドを使用します。結果セットが大きい場合は、newid()/ order byを最後の手段としてください。


4

可能であれば、ストアドステートメントを使用して、RND()の両方のインデックスの非効率性を回避し、レコード番号フィールドを作成します。

RandomRecord FROM "SELECT * FROM table LIMIT?、1";を準備します。
SET @ n = FLOOR(RAND()*(SELECT COUNT(*)FROM table));
EXECUTE RandomRecord USING @n;

このソリューションは、上記のwhere句で使用されているインデックス付きの数値が均等に分散されていない場合にも、ランダムな行を返す処理を行います。そのため、where id_value> = RAND()* MAX(id_value)を使用する場合とほぼ同じ(一定の)時間を要する場合でも、より良い方法です。
guido

私の知る限り、これは一定の時間で実行されず、線形時間で実行されます。最悪の場合、@ nはテーブルの行数に等しく、 "SELECT * FROM table LIMIT?、1"は最後の行に到達するまで@n-1行を評価します。
Andres Riofrio 2014

3

最善の方法は、その目的のためだけに新しい列にランダムな値を入れ、次のようなものを使用することです(疑似コード+ SQL):

randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")

これはMediaWikiコードで採用されているソリューションです。もちろん、小さい値に対してはある程度のバイアスがありますが、行がフェッチされない場合は、ランダムな値をゼロにラップすることで十分であることがわかりました。

newid()ソリューションでは、全行をスキャンして、各行に新しいGUIDを割り当てることができるため、パフォーマンスが大幅に低下する可能性があります。

関数が一度だけ評価され、すべての行に同じ「ランダムな」番号が割り当てられるため、rand()ソリューションはまったく機能しない可能性があります(つまり、MSSQLでは)。


1
結果が0のときにラップアラウンドすると、(「十分に良い」だけではなく)証明可能なランダムなサンプルが提供されます。このソリューションは、複数行のクエリにほぼ対応します(「パーティシャッフル」と考えてください)。問題は、結果が同じグループで繰り返し選択される傾向があることです。これを回避するには、使用したばかりの乱数を再配布する必要があります。結果から、randomNoを追跡してmax(randomness)に設定することで不正行為を行うことができますが、p(クエリ1の行i ANDクエリ2の行i)== 0であり、これは公平ではありません。いくつかの計算をさせてください。真に公正なスキームでお返しします。
09/10/29

3

SQL Server 2005および2008の場合、個々の行のランダムなサンプルが必要な場合(Books Onlineから):

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

3

推奨されていないためRAND()使用を意図しているため、単純に最大ID(= Max)を取得できます。

SELECT MAX(ID) FROM TABLE;

1..Max(= My_Generated_Random)の間のランダムを取得します

My_Generated_Random = rand_in_your_programming_lang_function(1..Max);

次に、このSQLを実行します。

SELECT ID FROM TABLE WHERE ID >= My_Generated_Random ORDER BY ID LIMIT 1

IDが選択した値と等しいかそれよりも高い行がないかチェックすることに注意してください。テーブルの行を探して、My_Generated_Random以下のIDを取得し、クエリを次のように変更することもできます。

SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1

生成されたランダムIDがテーブルに存在しない場合はどうなりますか?ユーザーに表示したくない削除済み行またはパッシブ行は、問題の原因になります。
-Ebleme

何もない。正確ではない、最も近いID番号を取得します。id = 1を削除することを検討している場合は、最小で1を交換してください。
Forsberg、

2

@cnuの回答に関する@BillKarwinのコメントで指摘されているように...

LIMITと組み合わせると、実際の行を直接順序付けるよりも、ランダムな順序でJOINを実行する方が(少なくともPostgreSQL 9.1では)はるかに優れていることがわかりました。例:

SELECT * FROM tbl_post AS t
JOIN ...
JOIN ( SELECT id, CAST(-2147483648 * RANDOM() AS integer) AS rand
       FROM tbl_post
       WHERE create_time >= 1349928000
     ) r ON r.id = t.id
WHERE create_time >= 1349928000 AND ...
ORDER BY r.rand
LIMIT 100

'r'が、結合されている複雑なクエリのすべての可能なキー値に対して 'rand'値を生成することを確認しますが、可能な場合は 'r'の行数を制限します。

整数としてのCASTは、整数および単精度浮動小数点型に対して特定のソート最適化を備えたPostgreSQL 9.2で特に役立ちます。


1

ここでのほとんどのソリューションは、ソートを回避することを目的としていますが、それでも、テーブルを順次スキャンする必要があります。

インデックススキャンに切り替えることで、順次スキャンを回避する方法もあります。ランダムな行のインデックス値がわかっている場合は、ほとんど瞬時に結果を得ることができます。問題は-インデックス値を推測する方法です。

次のソリューションはPostgreSQL 8.4で機能します。

explain analyze select * from cms_refs where rec_id in 
  (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
   from generate_series(1,10))
  limit 1;

上記の解決策では、範囲0 .. [idの最後の値]から10個のランダムなインデックス値を推測します。

数値10は任意です-応答時間に大きな影響を与えないため(驚くほど)、100または1000を使用できます。

また、1つの問題があります。IDがまばらな場合、見落とす可能性があります。解決策は、バックアップ計画を立てることです :)この場合、random()クエリによる純粋な古い順序です。結合されたIDは次のようになります。

explain analyze select * from cms_refs where rec_id in 
    (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
     from generate_series(1,10))
    union all (select * from cms_refs order by random() limit 1)
    limit 1;

ユニオン ALL句ではありません。この場合、最初の部分がデータを返す場合、2番目の部分は決して実行されません。


1

最近、Google経由で入手したので、後世のために、代替ソリューションを追加します。

別のアプローチは、TOPを2回、交互の順序で使用することです。TOPで変数を使用しているため、「純粋なSQL」かどうかはわかりませんが、SQL Server 2008で機能します。ランダムな単語が必要な場合に、辞書の単語のテーブルに対して使用する例を次に示します。

SELECT TOP 1
  word
FROM (
  SELECT TOP(@idx)
    word 
  FROM
    dbo.DictionaryAbridged WITH(NOLOCK)
  ORDER BY
    word DESC
) AS D
ORDER BY
  word ASC

もちろん、@ idxはランダムに生成された整数で、ターゲットテーブルで1からCOUNT(*)までの範囲です。列にインデックスが付けられている場合は、それからもメリットがあります。別の利点は、NEWID()が許可されていないため、関数で使用できることです。

最後に、上記のクエリは、同じテーブルに対するNEWID()タイプのクエリの実行時間の約1/10で実行されます。YYMV。


1

new id()関数を使用することもできます。

クエリを記述し、new id()関数の順序を使用するだけです。それはかなりランダムです。


1

MySQLがランダムなレコードを取得するために

 SELECT name
  FROM random AS r1 JOIN
       (SELECT (RAND() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1

詳細http://jan.kneschke.de/projects/mysql/order-by-rand/


答えの多くをテストした後、これが最良の答えだと思います。それは高速であるようで、毎回良い乱数を選びます。上記の@GreyPantherの2番目の提案に似ていますが、この回答はより多くの乱数を選択します。
ジェフベイカー

1

回答のこの変化をまだ十分に理解していませんでした。最初のシードを指定して、毎回同じ行のセットを選択する必要がある追加の制約がありました。

MS SQLの場合:

最小の例:

select top 10 percent *
from table_name
order by rand(checksum(*))

正規化された実行時間:1.00

NewId()の例:

select top 10 percent *
from table_name
order by newid()

正規化された実行時間:1.02

NewId()はに比べてわずかに遅いためrand(checksum(*))、大きなレコードセットに対しては使用しないほうがよい場合があります。

初期シードを使用した選択:

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by rand(checksum(*) % seed) /* any other math function here */

シードを指定して同じセットを選択する必要がある場合、これは機能するようです。


1

MSSQL(11.0.5569でテスト済み)で

SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10)

よりもかなり速い

SELECT TOP 100 * FROM employee ORDER BY NEWID()

1

SQL Serverでは、TABLESAMPLEをNEWID()と組み合わせて、非常に優れたランダム性を取得しながら、速度を維持できます。これは、本当に1行または少数の行が必要な場合に特に役立ちます。

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()

1

SQL Server 2012+では、OFFSET FETCHクエリを使用して単一のランダムな行に対してこれを行うことができます

select  * from MyTable ORDER BY id OFFSET n ROW FETCH NEXT 1 ROWS ONLY

ここで、idはID列であり、nは目的の行です-テーブルの0とcount()-1の間の乱数として計算されます(オフセット0は結局最初の行です)

これは、ORDER BY句で使用するインデックスがある限り、テーブルデータのホールで機能します。それはまた、ランダム性に非常に適しています-あなたが自分自身で渡すように働きかけますが、他の方法でのニグルは存在しません。さらに、パフォーマンスはかなり良好です。小さいデータセットで十分に対応できますが、数百万行に対して本格的なパフォーマンステストを試したことはありません。



0

CD-MaNに同意する必要があります。 "ORDER BY RAND()"を使用すると、小さなテーブルやSELECTを数回しか実行しない場合にうまく機能します。

また、 "num_value> = RAND()* ..."手法も使用しています。ランダムな結果が本当に必要な場合は、テーブルに特別な「ランダム」列を1日1回程度更新します。単一のUPDATEの実行にはしばらく時間がかかりますが(特にその列にインデックスが必要になるため)、選択が実行されるたびにすべての行に乱数を作成するよりもはるかに高速です。


0

TableSampleは実際には行のランダムなサンプルを返さないので注意してください。これは、行を構成する8KBページのランダムなサンプルを調べるようにクエリに指示します。次に、これらのページに含まれているデータに対してクエリが実行されます。これらのページでデータがどのようにグループ化されるか(挿入順序など)により、これは実際にはランダムなサンプルではないデータにつながる可能性があります。

参照:http : //www.mssqltips.com/tip.asp?tip=1308

このTableSampleのMSDNページには、実際にランダムなデータのサンプルを生成する方法の例が含まれています。

http://msdn.microsoft.com/en-us/library/ms189108.aspx


0

リストされているアイデアの多くはまだ順序付けを使用しているようです

ただし、一時テーブルを使用する場合は、ランダムなインデックスを割り当てて(多くのソリューションが提案しているように)、0から1の間の任意の数より大きい最初のインデックスを取得できます。

例(DB2の場合):

WITH TEMP AS (
SELECT COMLUMN, RAND() AS IDX FROM TABLE)
SELECT COLUMN FROM TABLE WHERE IDX > .5
FETCH FIRST 1 ROW ONLY

2
この解決策を検討した結果、ロジックに根本的な欠陥があることがわかりました。これは、テーブルの最初近くにある同じ小さなセットアップ値を一貫して返します。0と1の間に均等な分布があった場合、最初の行がその基準を満たす確率は50%であると想定しているためです。
DAVID、2011年


0

dbms_random.valueを使用する代わりにOracleのより良いソリューションがありますが、dbms_random.valueで行を並べ替えるにはフルスキャンが必要であり、大きなテーブルの場合は非常に遅くなります。

代わりにこれを使用してください:

SELECT *
FROM employee sample(1)
WHERE rownum=1

0

Firebirdの場合:

Select FIRST 1 column from table ORDER BY RAND()

0

SQL Server 2005以降では、num_valueが連続値ではない場合の@GreyPantherの回答を拡張します。これnum_valueは、データセットが均等に分散されておらず、数値ではなく一意の識別子である場合にも機能します。

WITH CTE_Table (SelRow, num_value) 
AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ID) AS SelRow, num_value FROM table
) 

SELECT * FROM table Where num_value = ( 
    SELECT TOP 1 num_value FROM CTE_Table  WHERE SelRow >= RAND() * (SELECT MAX(SelRow) FROM CTE_Table)
)

-1

SQLのランダム関数が役立ちます。また、1行のみに制限する場合は、最後に追加します。

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