【问题标题】:how to fix error with mysql random如何修复mysql随机错误
【发布时间】:2015-07-12 13:32:55
【问题描述】:

我在 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 随机函数发出选择请求,谢谢

【问题讨论】:

  • 。 .您正在使用 limit 而没有 order by,因此无论如何都会返回任意行。我真的不明白代码应该做什么。
  • 需要从 DB 中随机获取 40 个元素
  • 刚刚更新了我的答案。

标签: php mysql random


【解决方案1】:

根据您的代码对$folder 的作用,您可能容易受到SQL injection 的攻击。

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

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

  1. 获取符合条件的行数(即WHERE folder = ?)。
  2. 生成一个介于 0 和该数字之间的随机数。
  3. 像您一样选择具有给定偏移量的行。
  4. 将先前生成的行的 ID 存储在不断增长的列表中以从结果中排除,并减少行数。

一个使用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 项目开发兼容层。有关更多信息,请参阅链接的答案。)

【讨论】:

  • 。 .您对我的回答的陈述完全不正确。运行多个查询通常是个坏主意。在这种情况下,对于 20 行,它可能有效也可能无效,具体取决于与数据库结构和数据库连接相关的各种因素。
【解决方案2】:

实际上,即使表有 2+ 百万行,我猜给定文件夹的行数要少得多。因此,这对于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 子句复杂化了。索引可能是您提高性能所需的关键。

【讨论】:

  • webtrenches.com/post.cfm/avoid-rand-in-mysql - 拥有 2+ 百万条记录,ORDER BY RAND() 似乎是个坏主意。
  • 这对我来说很有趣,你的回答是 -1 否决
  • @ScottArciszewski 。 . .关键是查询不会对所有数据进行排序。它需要几百行并对其进行排序。对于他的问题类型,对几百行进行排序通常很好。您显然误解了查询逻辑。
  • 您对我发布的解决方案有何看法?我是 PHP 人,而不是 MySQL DBA,所以可能存在我不知道的细微差别。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 2014-06-24
    • 1970-01-01
    • 1970-01-01
    • 2023-01-09
    • 2021-12-27
    相关资源
    最近更新 更多