简体   繁体   English

如何用mysql随机修复错误

[英]how to fix error with mysql random

I have project in php + mysql (over 2 000 000 rows). 我有php + mysql项目(超过2 000 000行)。 Please view this php code. 请查看此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>';
                }
            }
            ?>

This code work very slowly in $start = rand(1,19980); 这段代码在$start = rand(1,19980);得非常慢$start = rand(1,19980); position, Please help how I can make select request with mysql random function, thank you 位置,请帮忙我如何用mysql随机函数做出选择请求,谢谢

Depending on what your code is doing with $folder , you may be vulnerable to SQL injection . 根据您的代码使用$folder ,您可能容易受到SQL注入的攻击。

For better security, consider moving to PDO or MySQLi and using prepared statements . 为了更好的安全性,请考虑转移到PDO或MySQLi并使用准备语句 I wrote a library called EasyDB to make it easier for developers to adopt better security practices. 我编写了一个名为EasyDB的库,以便开发人员更容易采用更好的安全实践。

The fast, sane, and efficient way to select N distinct random elements from a database is as follows: 从数据库中选择N个不同随机元素的快速,合理和有效的方法如下:

  1. Get the number of rows that match your condition (ie WHERE folder = ? ). 获取符合条件的行数(即WHERE folder = ? )。
  2. Generate a random number between 0 and this number. 生成0到此数字之间的随机数。
  3. Select a row with a given offset like you did. 像您一样选择具有给定偏移量的行。
  4. Store the ID of the previously generated row in an ever-growing list to exclude from the results, and decrement the number of rows. 将先前生成的行的ID存储在不断增长的列表中以从结果中排除,并减少行数。

An example that uses EasyDB is as follows: 使用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 suggests using ORDER BY RAND() , which in general is a bad idea and can make your queries very slow. Gordon的回答建议使用ORDER BY RAND() ,这通常是一个坏主意 ,可以使你的查询非常慢。 Furthermore, although he says that you shouldn't need to worry about there being less than 40 rows (presumably, because of the probability involved), this will fail in edge cases. 此外,虽然他说你不应该担心少于40行(可能是因为涉及的概率),但在边缘情况下这失败。

A quick note about mt_rand() : It's a biased and predictable random number generator with only 4 billion possible seeds. 关于mt_rand()快速说明:它是一个有偏见且可预测的随机数生成器,只有40亿种可能的种子。 If you want better results, look into random_int() (PHP 7 only, but I'm working on a compatibility layer for PHP 5 projects. See the linked answer for more information.) 如果你想要更好的结果,请查看random_int() (仅限PHP 7,但我正在为PHP 5项目开发兼容层。有关详细信息,请参阅链接的答案。)

Actually, even though the table has 2+ million rows, I'm guessing that a given folder has many fewer. 实际上,即使表有200多万行,我猜测给定的文件夹有更少的行。 Hence, this should be reasonable with an index on photo_gen(folder) : 因此,对于photo_gen(folder)的索引,这应该是合理的:

SELECT *
FROM photo_gen 
WHERE folder = '$folder'
ORDER BY rand()
LIMIT 40;

If a folder can still have tens or hundreds of thousands of examples, I would suggest a slight variation: 如果一个文件夹仍然可以有数十或数十万个例子,我建议稍微改变一下:

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;

The WHERE expression should get about 500 rows (subject to the vagaries of sample variation). WHERE表达式应该大约有500行(受样本变化的影响)。 There is a really high confidence that there will be at least 40 (you don't need to worry about it). 有一个非常高的信心,至少有40个(你不必担心它)。 The final sort should be fast. 最后的排序应该很快。

There are definitely other methods, but they are complicated by the where clause. 肯定有其他方法,但它们由where子句复杂化。 The index is probably the key thing you need for improved performance. 索引可能是提高性能所需的关键。

It's better to firstly compose your SQL query (as a string in PHP) once and then just execute it once. 最好首先将您的SQL查询(在PHP中作为字符串)编写一次,然后只执行一次。

Or you could use this way to select values if it fits your case: Select n random rows from SQL Server table 或者您可以使用这种方式选择适合您情况的值: 从SQL Server表中选择n个随机行

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM