首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何通过RAND()函数优化MySQL的顺序?

如何通过RAND()函数优化MySQL的顺序?
EN

Stack Overflow用户
提问于 2009-08-07 12:55:24
回答 9查看 69.3K关注 0票数 91

我想优化我的查询,以便查看mysql-slow.log

我的大多数慢速查询都包含ORDER BY RAND()。我无法找到真正的解决办法来解决这个问题。在MySQLPerformanceBlog有一个可能的解决方案,但我认为这还不够。对于优化不佳(或经常更新,用户管理)的表,它不能工作,或者我需要运行两个或多个查询才能选择我的PHP-generated随机行。

这个问题有什么解决办法吗?

一个虚拟的例子:

代码语言:javascript
运行
复制
SELECT  accomodation.ac_id,
        accomodation.ac_status,
        accomodation.ac_name,
        accomodation.ac_status,
        accomodation.ac_images
FROM    accomodation, accomodation_category
WHERE   accomodation.ac_status != 'draft'
        AND accomodation.ac_category = accomodation_category.acat_id
        AND accomodation_category.acat_slug != 'vendeglatohely'
        AND ac_images != 'b:0;'
ORDER BY
        RAND()
LIMIT 1
EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2009-08-07 13:01:53

试试这个:

代码语言:javascript
运行
复制
SELECT  *
FROM    (
        SELECT  @cnt := COUNT(*) + 1,
                @lim := 10
        FROM    t_random
        ) vars
STRAIGHT_JOIN
        (
        SELECT  r.*,
                @lim := @lim - 1
        FROM    t_random r
        WHERE   (@cnt := @cnt - 1)
                AND RAND(20090301) < @lim / @cnt
        ) i

这在MyISAM上尤其有效(因为COUNT(*)是即时的),但即使在InnoDB中,10的效率也比ORDER BY RAND()高出一倍。

这里的主要思想是,我们不进行排序,而是保留两个变量,并计算要在当前步骤中选择的行的running probability

有关更多细节,请参阅我博客中的这篇文章:

  • 选择随机行

更新:

如果您只需要选择一个随机记录,请尝试如下:

代码语言:javascript
运行
复制
SELECT  aco.*
FROM    (
        SELECT  minid + FLOOR((maxid - minid) * RAND()) AS randid
        FROM    (
                SELECT  MAX(ac_id) AS maxid, MIN(ac_id) AS minid
                FROM    accomodation
                ) q
        ) q2
JOIN    accomodation aco
ON      aco.ac_id =
        COALESCE
        (
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_id > randid
                AND ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        ),
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        )
        )

这假设您的ac_id的分布或多或少是均匀的。

票数 67
EN

Stack Overflow用户

发布于 2009-08-08 23:31:04

这取决于你需要的随机性。你联系的解决方案很管用,海事组织。除非您在ID字段中有很大的空白,否则仍然是随机的。

但是,您应该能够在一个查询中使用以下方法(用于选择单个值):

代码语言:javascript
运行
复制
SELECT [fields] FROM [table] WHERE id >= FLOOR(RAND()*MAX(id)) LIMIT 1

其他解决办法:

  • 向表中添加一个名为random的永久浮点数字段,并将其填充为随机数。然后,您可以在PHP中生成一个随机数并执行"SELECT ... WHERE rnd > $random"
  • 获取整个in列表并将其缓存在文本文件中。读取文件并从中选择一个随机ID。
  • 将查询结果缓存为HTML,并将其保存几个小时。
票数 12
EN

Stack Overflow用户

发布于 2009-08-07 21:00:59

我是这样做的:

代码语言:javascript
运行
复制
SET @r := (SELECT ROUND(RAND() * (SELECT COUNT(*)
  FROM    accomodation a
  JOIN    accomodation_category c
    ON (a.ac_category = c.acat_id)
  WHERE   a.ac_status != 'draft'
        AND c.acat_slug != 'vendeglatohely'
        AND a.ac_images != 'b:0;';

SET @sql := CONCAT('
  SELECT  a.ac_id,
        a.ac_status,
        a.ac_name,
        a.ac_status,
        a.ac_images
  FROM    accomodation a
  JOIN    accomodation_category c
    ON (a.ac_category = c.acat_id)
  WHERE   a.ac_status != ''draft''
        AND c.acat_slug != ''vendeglatohely''
        AND a.ac_images != ''b:0;''
  LIMIT ', @r, ', 1');

PREPARE stmt1 FROM @sql;

EXECUTE stmt1;
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1244555

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档