2015-07-12 61 views
1

我有项目在php + mysql(超过2 000 000行)。请查看这个php代码。如何解决与mysql随机错误

<?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随机函数选择请求,谢谢

+0

。 。您正在使用'limit'而没有'order by',所以任意行都被返回。我真的不明白代码应该做什么。 –

+0

需要从数据库中随机获得40个元素 – Vahagn

+0

刚刚更新了我的答案。 –

回答

1

实际上,即使表有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子句复杂化。索引可能是您提高性能所需的关键。

+0

http://www.webtrenches.com/post.cfm/avoid-rand-in-mysql - 与2+万条记录,'ORDER BY RAND()'似乎是一个坏主意。 –

+0

对我来说是有趣的你的答案有-1 VODE向下 – Shafizadeh

+0

@ScottArciszewski。 。 。关键是查询不会对所有数据进行排序。它需要几百行并对其进行排序。对于这类问题,排序几百行通常很好。您显然误解了查询逻辑。 –

2

根据您的代码使用$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():这是一种偏见和可预测的随机数发生器只有4十亿可能的种子。如果你想要更好的效果,look into random_int()(仅PHP 7,但我工作了PHP 5项目的兼容层上。更多信息请参阅链接的答案。)

+1

。 。你对我的回答的陈述是不正确的。运行多个查询通常是一个坏主意。在这种情况下,对于20行,取决于与数据库结构和数据库连接有关的各种因素,它可能会或可能不会有效。 –