如何用mysql随机修复错误

时间:2015-07-12 13:32:56

标签: php mysql random

我在php + mysql中有项目(超过2 000 000行)。请查看此PHP代码。

<?php 
            for($i=0;$i<20;$i++)
            {
                $start = rand(1,19980);
                $select_images_url_q = "SELECT * FROM photo_gen WHERE folder='$folder' LIMIT $start,2 ";
                $result_select = (mysql_query($select_images_url_q));
                while($row = mysql_fetch_array($result_select))
                    {
                    echo '<li class="col-lg-2 col-md-3 col-sm-3 col-xs-4" style="height:150px">
                                      <img class="img-responsive" src="http://static.gif.plus/'.$folder.'/'.$row['code'].'_s.gif">
                                </li>';
                }
            }
            ?>

这段代码在$start = rand(1,19980);位置工作得非常慢,请帮助我如何使用mysql随机函数进行选择请求,谢谢

3 个答案:

答案 0 :(得分:2)

根据您的代码使用$folder执行的操作,您可能容易受到SQL injection的攻击。<​​/ p>

为了更好的安全性,请考虑转移到PDO或MySQLi和using prepared statements。我编写了一个名为EasyDB的库,以便开发人员更容易采用更好的安全实践。

从数据库中选择N个不同随机元素的快速,合理和有效的方法如下:

  1. 获取符合条件的行数(即WHERE folder = ?)。
  2. 生成0到此数字之间的随机数。
  3. 像您一样选择具有给定偏移量的行。
  4. 将以前生成的行的ID存储在不断增长的列表中,以从结果中排除,并减少行数。
  5. 使用EasyDB的示例如下:

    // Connect to the database here:
    $db = \ParagonIE\EasyDB\Factory::create(
        'mysql;host=localhost;dbname=something',
        'username',
        'putastrongpasswordhere'
    ); 
    
    // Maintain an array of previous record IDs in $exclude
    $exclude = array();
    $count = $db->single('SELECT count(id) FROM photo_gen WHERE folder = ?', $folder);
    
    // Select _up to_ 40 values. If we have less than 40 in the folder, stop
    // when we've run out of photos to load:
    $max = $count < 40 ? $count : 40;
    
    // The loop:
    for ($i = 0; $i < $max; ++$i) {
        // The maximum value will decrease each iteration, which makes
        // sense given that we are excluding one more result each time
        $r = mt_rand(0, ($count - $i - 1));
    
        // Dynamic query
        $qs = "SELECT * FROM photo_gen WHERE folder = ?";
    
        // We add AND id NOT IN (2,6,7,19, ...) to prevent duplicates:
        if ($i > 0) {
            $qs .= " AND id NOT IN (" . implode(', ', $exclude) . ")";
        }
        $qs .= "ORDER BY id ASC LIMIT ".$r.", 1";
    
        $row = $db->row($qs, $folder);
    
        /**
         * Now you can operate on $row here. Feel free to copy the
         * contents of your while($row=...) loop in place of this comment.
         */
    
        // Prevent duplicates
        $exclude []= (int) $row['id'];
    }
    

    Gordon's answer建议使用ORDER BY RAND()in general is a bad idea并且可以使您的查询速度非常慢。此外,尽管他说您不必担心少于40行(可能是因为涉及的概率),但在边缘情况下失败。

    关于mt_rand()的快速说明:这是一个有偏见且可预测的随机数生成器,只有40亿种可能的种子。如果你想要更好的结果,look into random_int()(仅限PHP 7,但我正在为PHP 5项目开发兼容层。有关详细信息,请参阅链接的答案。)

答案 1 :(得分:1)

实际上,即使该表有200多万行,我也猜测给定的文件夹少了很多。因此,对于photo_gen(folder)

的索引,这应该是合理的
SELECT *
FROM photo_gen 
WHERE folder = '$folder'
ORDER BY rand()
LIMIT 40;

如果一个文件夹仍然可以包含数十或数十万个示例,我建议稍微改变一下:

SELECT pg.**
FROM photo_gen pg cross join
     (select count(*) cnt from photo_gen where folder = $folder) as cnt
WHERE folder = '$folder' and
      rand() < 500 / cnt
ORDER BY rand()
LIMIT 40;

WHERE表达式应该大约有500行(受样本变异的影响)。有一个非常高的信心,至少有40个(你不必担心它)。最后的排序应该很快。

肯定有其他方法,但它们因where子句而变得复杂。索引可能是提高性能所需的关键因素。

答案 2 :(得分:0)

最好先将你的SQL查询(在PHP中作为一个字符串)编写一次,然后再执行一次。

或者您可以使用这种方式选择适合您情况的值:Select n random rows from SQL Server table