随机改进MySQL

时间:2014-02-17 13:12:54

标签: mysql sql

我有这个问题:

$sql = "SELECT 
    catalogs_values.name as word, catalogs.name as catalog
FROM
    catalogs_values
        INNER JOIN
    catalogs ON catalogs_values.catalog_id = catalogs.id
WHERE
    catalogs_values.id NOT IN (SELECT 
            valueid
        FROM
            monitor
        WHERE
            userid = $user_id)
        AND catalogs_values.checked = 0
ORDER BY RAND()
LIMIT 1";

在我的表中,我有大约100万条记录,我的查询非常慢。你能建议一些改进吗?

5 个答案:

答案 0 :(得分:1)

通过Mysql的RAND排序总是很慢,我用一种非常快速的方法对它进行排序:

  1. 从表中选择最小和最大ID
  2. 运行php rand(min_Id,max_Id)
  3. 制作一个循环,直到找到一个真正存在的id,相信我,如果id是一个唯一的键,它真的很快,因为它应该
  4. 一旦你确定ID存在退出循环,就会有你的随机ID

    $SQL = " SELECT MAX( id ) as x FROM table ";
    $query = mysql_query($SQL);
    $x = mysql_fetch_assoc($query);
    $max = $x['x'];
    $ok = false;
    
    while($ok == false){
    
        $id = rand(1, $max);
        $SQL = "SELECT id FROM table WHERE id = ".$id." LIMIT 1";
    
        $query = mysql_query($SQL);
        $record = mysql_fetch_assoc($query);
    
        if((int)$record['id'] > 0){
            $ok = true;
        }
    
    }
    
    //your ID is: $record['id'];
    

答案 1 :(得分:1)

MediaWiki(想想维基百科的random article page)通过为每一行random value分配一个,将其添加到索引,然后使用索引选择来实现这一点:

SELECT * from `some_table` where `my_rand_column` >= RAND() LIMIT 1;

答案 2 :(得分:0)

尝试将not in替换为left outer joinnot exists

SELECT cv.name as word, c.name as catalog
FROM catalogs_values cv INNER JOIN
     catalogs c
     ON cv.catalog_id = c.id LEFT JOIN
     monitor m
     on cv.id = m.valueid and userid=$user_id
WHERE m.valueid is null and cv.checked = 0
ORDER BY RAND()
LIMIT 1;

这可能会解决性能问题。

如果不是,您可能需要另一种方法来获取随机行。一种简洁的方法是选择随机行的子集,然后只选择一个:

select word, catalog
from (SELECT cv.name as word, c.name as catalog
      FROM catalogs_values cv INNER JOIN
           catalogs c
           ON cv.catalog_id = c.id LEFT JOIN
           monitor m
           on cv.id = m.valueid and userid=$user_id
      WHERE m.valueid is null and cv.checked = 0 and rand() < 0.001
     ) t
ORDER BY RAND()
LIMIT 1;

内部查询选择大约一千行中的一行(该比例可能需要根据与其他各种条件匹配的行数而变化)。然后将此简化集传递给order by rand()方法,以便只选择一个。

答案 3 :(得分:0)

你可以采取这样的随机数:

$whoToTake = rand(1, 1000000);

或者如果您希望它是动态的,请进行简单的计数

select count(id) from table;

$max = THE_RESULT_OF_THE_QUERY
$whoToTake = rand(1, $max);

现在做一个简单的查询

select * 
from table
limit 1 offset {$whoToTake}

这会快得多

答案 4 :(得分:0)

这是关于freenode IRC的#mysql的一个反复出现的问题。

看看Jan Kneschke的this blog post

它概述了如何优化ORDER BY RAND() LIMIT 1,这样您就不必首先排序所有行,然后抛弃所有内容,除了您要保留的那一行。

Jan在没有完全剥夺他的博客文章的情况下更详细地解释了这一点。