如何最好地“洗牌”数据库记录表?

6
假设我有一个带有许多记录的表格,我想要将其随机呈现给用户。我还想让用户能够翻页,因此必须保留某种排序方式,至少在一段时间内是这样。
应用程序基本上仅使用 AJAX,并且对已访问页面使用缓存,因此即使我始终提供随机结果,当用户尝试返回时,他将获得上一页,因为它将从本地缓存加载。
问题是,如果我只返回随机结果,可能会有一些重复项。每个页面包含6个结果,因此为了防止这种情况,我需要做类似于 WHERE id NOT IN(1,2,3,4...) 的操作,在其中放置所有先前加载的ID。
那种解决方案的巨大弊端在于,服务器端无法缓存任何内容,因为每个用户都将请求不同的数据。
另一种解决方案可能是为记录创建另一列来对其进行排序,并且每 插入时间单位 就对其进行洗牌。问题在于,我需要对表中的每个记录设置一个序列外的随机数,这将需要与记录数量相同的查询次数。
如果相关的话,我正在使用 Rails 和 MySQL。

1
以用户身份浏览随机数据对我来说没有太多意义。如果你把它称为“再来6篇随机文章!”并且只接受可能的重复,似乎这基本上就是你所要求的,而且不需要额外的努力。 - jdl
那么对于这些答案有什么回应呢?我很想知道是否有任何建议的解决方案适用于您。 - noodl
4个回答

7

试试这个:

mysql> create table t (i int);
mysql> insert into t values (1),(2),(3),(4),(5),(6);
mysql> select * from t order by rand(123) limit 2 offset 0;
+------+
| i    |
+------+
|    6 | 
|    4 | 
+------+
mysql> select * from t order by rand(123) limit 2 offset 2;
+------+
| i    |
+------+
|    2 | 
|    3 | 
+------+
mysql> select * from t order by rand(123) limit 2 offset 4;
+------+
| i    |
+------+
|    5 | 
|    1 | 
+------+

请注意rand()函数有一个种子值(123)。同时请注意,如果您重复执行最后三个查询,每次都会得到相同的结果。

ORDER BY RAND() 是最糟糕的做法,因为它需要 mySQL 复制整个表,给每一行添加一个 RAND() 值,最后进行排序。如果数据表只有6行,那么就没问题,但是对于更多的行数来说,这种方法效率极低。预计你的 mySQL 服务器会变得更慢和超载。 - Sebastian

3
如果随机结果是“面向所有人”而不是任何特定用户,则可以像这样做:(这是针对Postgres的,也适用于其他数据库)
update mytable set sortorder = random() * 100000000;

select * from mytable order by sortorder, primarykeyid;

由于随机数可能会重复,通过按照primarykeyid进行二次排序可以增加排序的稳定性。

然后您可以随时执行此操作以刷新缓存。例如,将页面的绝对过期时间设置为每分钟。然后,每分钟重新更新排序顺序并正常提供页面。

如果在刷新窗口内收到请求,则有可能出现不同的页面获取相同的结果。还有一个问题是,当用户点击“返回”时,他们可能无法得到之前的页面(因为已经刷新了)。

这种方法的有效性取决于呈现随机数据的动机以及数据量等因素。但是,如果对缓存友好性很重要,则这是一种好的方法。它也是无状态的(不需要会话信息)。


2
我会做以下事情(假设有一个按顺序排列的数字主键):
  1. 生成一个随机数并将其存储在用户的会话中
  2. 当用户浏览数据时,查询总行数
  3. 使用会话中存储的数字作为种子,在每个请求中生成相同的“随机”ID顺序
  4. 浏览ID并仅从数据库检索与这些ID匹配的记录。

0
SET @rownum := (SELECT COUNT(1) FROM tbl_allindia_mapping);
SET @row := (SELECT CEIL((RAND() * @rownum)));
SELECT @row;
SELECT * FROM tbl_allindia_mapping WHERE id = @row;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接