不久前,我需要从Oracle数据库中的表中获取随机行。我发现的最广泛的解决方案是:
SELECT * FROM
( SELECT * FROM tabela WHERE warunek
ORDER BY dbms_random.value )
WHERE rownum = 1
但是,这对于大型表来说是非常高的性能,因为它首先以随机顺序对表进行排序,然后获取第一行。
今天,我的一位同事提出了另一种建议:
SELECT * FROM (
SELECT * FROM MAIN_PRODUCT
WHERE ROWNUM <= CAST((SELECT COUNT(*) FROM MAIN_PRODUCT)*dbms_random.value AS INTEGER)
ORDER BY ROWNUM DESC
) WHERE ROWNUM = 1;
它的运行速度更快,并且似乎返回随机值,但是真的吗?有人可以深入了解它是否真的是随机的,是否按预期方式运行?我真的很好奇为什么在看的时候没有在其他任何地方找到这种方法,如果它确实是随机的并且在性能上有更好的表现,为什么它不更广泛?
答案 0 :(得分:2)
(可能)这是获得结果的最简单查询。
但是COUNT()
会进行表扫描,我怀疑您会得到一个不这样做的查询。
Ps。此查询假定未删除的记录。
查询
SELECT *
FROM
MAIN_PRODUCT
WHERE
ROWNUM = FLOOR(
(dbms_random.value * (SELECT COUNT(*) FROM MAIN_PRODUCT)) + 1
)
FLOOR(
(dbms_random.value * (SELECT COUNT(*) FROM MAIN_PRODUCT)) + 1
)
将生成一个介于1到表的最大数量之间的数字,请参见demo。刷新时如何工作。
Oracle12c +查询
SELECT *
FROM
MAIN_PRODUCT
WHERE
ROWNUM <= FLOOR(
(dbms_random.value * (SELECT COUNT(*) FROM MAIN_PRODUCT)) + 1
)
ORDER BY
ROWNUM DESC
FETCH FIRST ROW ONLY
答案 1 :(得分:1)
提高性能的关键是减轻ORDER BY
的负担。
如果知道符合条件的行数,则可以在排序之前进行过滤。例如,以下内容占据了大约1%的行:
SELECT *
FROM (SELECT *
FROM tabela
WHERE warunek AND dbms_random.value < 0.01
ORDER BY dbms_random.value
)
WHERE rownum = 1 ;
一种变化是计算匹配值的数量。然后随机选择一个较小的样本。以下获取约100个匹配的行,然后对它们进行排序以进行随机选择:
SELECT a.*
FROM (SELECT *
FROM (SELECT a.*, COUNT(*) OVER () as cnt
FROM tabela a
WHERE warunek
) a
WHERE dbms_random.value < 100 / cnt
ORDER BY dbms_random.value
) a
WHERE rownum = 1 ;
答案 2 :(得分:1)
您拥有的第二个代码
SELECT * FROM (
SELECT * FROM MAIN_PRODUCT
WHERE ROWNUM <= CAST((SELECT COUNT(*) FROM MAIN_PRODUCT)*dbms_random.value AS INTEGER)
ORDER BY ROWNUM DESC
) WHERE ROWNUM = 1;
非常好,除了它将获得后续元素。 dbms_random.value
返回一个0到1之间的实数。将其乘以行数将为您提供一个真正的随机数,这里的瓶颈是对行数进行计数,而不是为每行生成一个随机值。 / p>
证明
考虑
0 <= x <1
号码。如果将它乘以n,我们得到
0 <= n * x
这正是您要加载单个元素的需求。之所以不普及,是因为在许多情况下,仅数千条记录就不会引起性能问题。
编辑
如果您需要k个记录,而不仅是第一个,那么仍然有些困难,但是仍然可以解决。该算法将是这样的(我没有安装Oracle来测试它,所以我仅描述该算法):
randomize(n, k)
randomized <- empty_set
while (k > 0) do
newValue <- random(n)
n <- n - 1
k <- k - 1
//find out how many elements are lower than newValue
//increase newValue with that amount
//find out if newValue became larger than some values which were larger than new value
//increase newValue with that amount
//repeat until there is no need to increase newValue
while end
randomize end
如果您从n个中随机分配了k个元素,那么您将可以在过滤器中使用这些值。