1

我正在尝试从数据库中提取具有图片的随机文章。

SELECT FLOOR(MAX(id) * RAND()) FROM `table` WHERE `picture` IS NOT NULL

我的表有 33 MB 大,有 1,006,394 篇文章,但只有 816 篇带图片。我的问题是这个查询需要 0.4640 sek

我需要这个更快。欢迎任何想法。

PS 1. 当然我有一个关于 id 的索引。2.图片字段没有索引。我应该加一个吗?3. 产品名称是唯一的,也是产品编号,但那是不可能的。

测试结果。

@cHao的解决方案在我使用它来选择一个带有图片的随机条目时会更快。(然后是 0.1 秒。但如果我尝试做相反的事情,选择没有图片的随机文章,它会更慢。2 .. 3 秒。

@Kickstart的解决方案在尝试查找带图片的条目时会慢一些,但在尝试查找没有图片的条目时速度几乎相同。平均 0,149 秒。

@bob-kruithof的解决方案对我不起作用。当试图找到一个有图片的条目时,它会选择一个没有图片的条目。

和@ganesh-bora,是的,你是对的,在我的情况下,速度差异约为 5..15 倍。

我要感谢大家的帮助,我决定使用@Kickstart。

4

4 回答 4

1

您需要获取具有匹配记录的值范围,然后在该范围内找到匹配的记录。

像这样的东西: -

SELECT r1.id
FROM `table` AS r1 
INNER JOIN (
    SELECT RAND( ) * ( MAX( id ) - MIN( id ) ) + MIN( id ) AS id
    FROM `table`
    WHERE `picture` IS NOT NULL
) AS r2
ON r1.id >= r2.id
WHERE `picture` IS NOT NULL
ORDER BY r1.id ASC
LIMIT 1

但是,为了提高效率,您需要在它正在检查的字段上建立一个索引(即,picture在您的示例中)

只是解释这是如何工作的。

子选择从表中找到一个随机 id,该 id 介于图片记录的最小和最大 id 之间。这个随机 id 可能适用于图片,也可能不适用。

该子选择的结果 id 与主表连接,但使用 >= 和 WHERE 子句指定记录是图片记录。因此,它加入了所有 id 大于或等于随机 id 的图片记录。最高的随机 id 将是具有最高 id 的图片记录,因此它总是会找到一条记录(如果有任何图片记录)。然后使用 ORDER BY / LIMIT 带回该单个 id。

请注意,这有一个明显的缺陷,但大多数时候它是无关紧要的。检索到的记录可能不是完全随机的。具有最低 id 的图片不太可能被返回(只有当 RAND() 返回正好为 0 时才会返回),但如果这很重要,这很容易通过舍入生成的随机 id 来修复。另一个缺陷是,如果 id 不是在整个 id 范围内模糊均匀分布,那么一些将比其他更频繁地返回。例如,以前 1000 个 id 是图片的情况为例,然后直到最后(第 3300 万个)记录为止。随机 id 可以是这 3300 万个中的任何一个,但除非它小于或等于 1000,否则它将是将返回的第 3300 万个记录。

于 2013-05-09T09:00:11.393 回答
1

您可以尝试在每一行附加一个随机数,然后按此排序。编号最小的行将位于顶部。

SELECT `table`.`id`, RAND() as `order`
FROM `table`
WHERE `picture` IS NOT NULL
ORDER BY `order`
LIMIT 1;

这当然比仅仅用 魔法来设置一个 ID 慢RAND(),但是 (1) 它总是会给你一个有效的 ID(只要表中有一个非空图片字段的记录,无论如何),和 (2 ) WTF 比率相当低;大多数人都知道这里发生了什么。:)当可供选择的项目数量相对较少(大约 1%)时,它的性能可以与具有良好索引表的Kickstart 解决方案相媲美。绝对不要试图从这样一张巨大的桌子中进行选择;WHERE首先在某些索引字段上使用子句对其进行限制。

性能方面,如果您有一个长时间运行的应用程序(即:不是 PHP;我说的是 Java、.net 等,即使在请求之间应用程序仍然存在),您可能会尝试保留所有项目 ID 的列表使用图片,从该列表中选择一个随机 ID,然后加载文章。如果你愿意,你也可以在 PHP 中做到这一点。当您每次都必须查询所有 ID 时,它可能效果不佳,但如果您可以在 APC 或其他东西中缓存 ID 列表,它可能会非常有用。

于 2013-05-09T08:46:13.650 回答
0

为了提高性能,您可以首先在图片列上添加索引,以便在执行查询时将 814 条记录排序在顶部,然后您可以触发查询。

于 2013-05-09T08:54:11.897 回答
0

其他人是如何解决问题的?

我建议查看这篇文章,了解在 mysql 中选择随机行的不同可能方法。

文章中的修改示例

SELECT name
FROM random JOIN
    ( SELECT CEIL( RAND() * (
        SELECT MAX( id ) FROM random WHERE picture IS NOT NULL
    ) ) AS id ) AS r2 USING ( id );

这可能适用于您的情况。

效率

  • 正如用户 Kickstart 所提到的:您在列上有索引picture吗?这可能会帮助您更快地获得结果。
  • 你的桌子优化了吗?
于 2013-05-09T08:58:00.957 回答