【发布时间】:2012-04-09 17:41:34
【问题描述】:
我需要从 Oracle 数据库中随机选择行。
例如:假设一个有 100 行的表,我如何从整个 100 行中随机返回 20 条记录。
【问题讨论】:
我需要从 Oracle 数据库中随机选择行。
例如:假设一个有 100 行的表,我如何从整个 100 行中随机返回 20 条记录。
【问题讨论】:
SELECT *
FROM (
SELECT *
FROM table
ORDER BY DBMS_RANDOM.RANDOM)
WHERE rownum < 21;
【讨论】:
SAMPLE() 不能保证准确地为您提供 20 行,但可能是合适的(并且可能比完整查询 + 大表随机排序的性能要好得多):
SELECT *
FROM table SAMPLE(20);
注意:这里的20 是一个大概的百分比,而不是所需的行数。在这种情况下,由于您有 100 行,因此要获得大约 20 行,您需要 20% 的样本。
【讨论】:
SELECT * FROM table SAMPLE(10) WHERE ROWNUM <= 20;
这样更高效,因为它不需要对表格进行排序。
【讨论】:
SELECT column FROM
( SELECT column, dbms_random.value FROM table ORDER BY 2 )
where rownum <= 20;
【讨论】:
要随机选择 20 行,我认为您最好选择其中随机排序的很多行并选择该集合的前 20 行。
类似:
Select *
from (select *
from table
order by dbms_random.value) -- you can also use DBMS_RANDOM.RANDOM
where rownum < 21;
最好用于小表,以避免选择大块数据而丢弃大部分数据。
【讨论】:
总结一下,介绍了两种方式
1) using order by DBMS_RANDOM.VALUE clause
2) using sample([%]) function
第一种方式在 'CORRECTNESS' 中具有优势,这意味着如果结果确实存在,您将永远不会失败,而在第二种方式中,即使有满足查询条件的案例,您也可能得不到结果,因为在采样期间信息减少了.
第二种方式在“高效”方面具有优势,这意味着您将更快地获得结果并减轻数据库的负载。 DBA 向我发出警告,我使用第一种方式的查询会给数据库带来负载
您可以根据自己的兴趣选择两种方式中的一种!
【讨论】:
在大表的情况下,按 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
)
);
【讨论】:
以下是如何从每组中随机抽取一个样本:
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;
我不确定它的效率如何,但是如果您有很多类别和子类别,这似乎可以很好地完成工作。
【讨论】: