Oracleデータベースからランダムにレコードを取得するにはどうすればよいですか?


82

OracleDBからランダムに行を選択する必要があります。

例:100行のテーブルを想定します。100行全体からそれらのレコードのうち20個をランダムに返す方法を教えてください。

回答:


111
SELECT *
FROM   (
    SELECT *
    FROM   table
    ORDER BY DBMS_RANDOM.RANDOM)
WHERE  rownum < 21;

1
私を殴ってください。ただし、これにより、テーブルから最初の20行のみが選択され、ランダムに並べ替えられます。
Nishant Sharma 2012年

10
これは大きなテーブルでは非常に重い操作であることに注意する必要があります。これは、最初に各行に乱数を割り当て、次にこの値で並べ替えてから、そこからいくつかのレコードを取得するためです。
Roeland Van Heddegem 2015

11
@NishantSharma、行は、ランダム化され、その後制限-あなたのコメントが正しくありません。
サイモンMᶜKenzie

5
このアプローチは
非常に

1
@JonBetts、サンプルははるかに高速でリソース効率が高いと思います:stackoverflow.com/a/9920431/156787
Evan Kroske 2018年

50

SAMPLE()は、正確に20行を提供することは保証されていませんが、適切である可能性があります(また、完全なクエリ+大きなテーブルのランダムソートよりも大幅にパフォーマンスが向上する可能性があります)。

SELECT *
FROM   table SAMPLE(20);

注:これ20はおおよそのパーセンテージであり、必要な行数ではありません。この場合、100行あるので、約20行を取得するには、20%のサンプルを要求します。


1
サンプルは高速ですが、あまりランダムではないようです。表の先頭/先頭に向かうレコードが優先される傾向があります。
craigrs84 2014

1
これは、テーブル全体を通過する前にクエリを停止すると発生します。
ジェフリーケンプ

2
申し訳ありませんが、間違えました。投稿は問題なく、結果は均等に分散されます。「whererownum <= 20」をsample(20)と組み合わせて追加すると、データのランダム性が低下し始めます。
craigrs84 2014

13
SELECT * FROM table SAMPLE(10) WHERE ROWNUM <= 20;

これは、テーブルを並べ替える必要がないため、より効率的です。


7
20行後にサンプルを停止すると、結果はランダムではなくなります(表の前にある行は、後の行よりもはるかに頻繁に返されます)。また、これは20行を返すことが保証されていません。
ジェフリーケンプ2015年

10
SELECT column FROM
( SELECT column, dbms_random.value FROM table ORDER BY 2 )
where rownum <= 20;

4

20行をランダムに選択するには、ランダムに並べられた多くの行を選択し、そのセットの最初の20行を選択する方がよいと思います。

何かのようなもの:

Select *
  from (select *
          from table
         order by dbms_random.value) -- you can also use DBMS_RANDOM.RANDOM
 where rownum < 21;

ほとんどのデータを破棄するためだけに大きなデータチャンクを選択することを避けるために、小さなテーブルに最適です。


3

要約すると、2つの方法が導入されました

1) using order by DBMS_RANDOM.VALUE clause
2) using sample([%]) function

最初の方法は「CORRECTNESS」に利点があります。つまり、実際に存在する場合でも結果の取得に失敗することはありません。一方、2番目の方法では、サンプリング中に情報が減少するため、クエリ条件を満たす場合でも結果が得られない可能性があります。

2番目の方法は、「効率的」という利点があります。つまり、結果が早く得られ、データベースへの負荷が軽くなります。DBAから、最初の方法を使用したクエリでデータベースに負荷がかかるという警告が表示されました。

あなたの興味に応じて2つの方法のいずれかを選択できます!


1

巨大なテーブルの場合、dbms_random.valueによるソートを使用した標準的な方法は効果的ではありません。これは、テーブル全体をスキャンする必要があり、dbms_random.valueは非常に遅い関数であり、コンテキストスイッチが必要なためです。このような場合、3つの追加の方法があります。


1:使用sample句:

例えば:

select *
from s1 sample block(1)
order by dbms_random.value
fetch first 1 rows only

つまり、すべてのブロックの1%を取得してから、ランダムに並べ替えて1行だけを返します。


2:正規分布の列にインデックス/主キーがある場合、最小値と最大値を取得し、この範囲のランダム値を取得し、ランダムに生成された値以上の値を持つ最初の行を取得できます。

例:

--big table with 1 mln rows with primary key on ID with normal distribution:
Create table s1(id primary key,padding) as 
   select level, rpad('x',100,'x')
   from dual 
   connect by level<=1e6;

select *
from s1 
where id>=(select 
              dbms_random.value(
                 (select min(id) from s1),
                 (select max(id) from s1) 
              )
           from dual)
order by id
fetch first 1 rows only;

3:ランダムなテーブルブロックを取得し、ROWIDを生成し、このROWIDによってテーブルから行を取得します

select * 
from s1
where rowid = (
   select
      DBMS_ROWID.ROWID_CREATE (
         1, 
         objd,
         file#,
         block#,
         1) 
   from    
      (
      select/*+ rule */ file#,block#,objd
      from v$bh b
      where b.objd in (select o.data_object_id from user_objects o where object_name='S1' /* table_name */)
      order by dbms_random.value
      fetch first 1 rows only
      )
);

0

各グループからランダムなサンプルを選択する方法は次のとおりです。

SELECT GROUPING_COLUMN, 
       MIN (COLUMN_NAME) KEEP (DENSE_RANK FIRST ORDER BY DBMS_RANDOM.VALUE) 
         AS RANDOM_SAMPLE
FROM TABLE_NAME
GROUP BY GROUPING_COLUMN
ORDER BY GROUPING_COLUMN;

それがどれほど効率的かはわかりませんが、カテゴリとサブカテゴリがたくさんある場合は、これでうまくいくようです。


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