好奇心问题 - 每当我看到通过SQL选择的随机行时,我都会看到它:
SELECT * FROM table_name ORDER BY RANDOM() LIMIT 1;
哪个效果很好,但是O(n)
n
是表的大小。 (这不是我遇到的真正的性能问题,数据库速度很快,这是一个学术问题)。
有没有办法在任何SQL语言中实现O(1)
随机选择,就像在其他语言中一样? (例如,在1和n之间生成一个随机数r
,然后选择第r
行。
答案 0 :(得分:1)
您的问题是基于数据库查询以某种方式O(n)
的假设 - 它们不是(否则JOIN
将非常昂贵)。
SQL是基于关系代数的RDBMs系统中存在的表/行存储的抽象,它没有引用或声明w.r.t.运行时复杂性,这很好,因为它意味着实现数据库系统可以利用索引,内存存储,特殊情况处理程序和其他优化来提供具有不同运行时性能的正确结果。这也意味着SQL不公开实现细节,例如物理行号,块地址,B树节点等。当您编写查询时,您不应该关注这些事情,重要的是代数正确性你的查询。
因此,一个特别聪明的DBMS可能会有一个ORDER BY RANDOM() LIMIT 1
的特殊情况处理程序,它将在O(1)
时间内运行。
...但是,似乎MySQL没有针对这种情况进行优化,因此需要更好的方法。
试试这个:
SELECT @n := COUNT(*) FROM table_name
SET @offset := ROUND( RAND() * @n )
SELECT * FROM table_name ORDER BY clustered_indexed_column LIMIT 1 OFFSET @offset
其中clustered_indexex_column
是一列可能是您的主键,或者是一个非常便宜的列,可以执行ORDER BY
。假设索引设置正确,此查询应按O( log(n) )
或更好的顺序运行。
答案 1 :(得分:0)
http://logicalread.solarwinds.com/oracle-11g-hash-indexes-mc02/#.WBE0hBmEbqA
答案 2 :(得分:0)
O(n)并不一定意味着快速。以下应该做你想要的:
SELECT t.*
FROM (SELECT t.*
FROM table_name t CROSS JOIN
(SELECT COUNT(*) as cnt FROM table_name) x
WHERE RANDOM() <= 100.0 / cnt
) t
ORDER BY RANDOM()
LIMIT 1;
如果没有索引,则处理为:
cnt
是全表扫描,即O(n)。where
子句进行过滤会产生大约100行,与整个表的大小无关。ORDER BY
是“常数”(诚然,如果n是100或更小,那么这个“常数”更像是O(n log(n))这取决于一个非常不幸的随机数条连一条匹配都找不到的事实。我认为不太可能排除这种可能性。