2015-07-12 2 views
1

У меня есть проект в php + mysql (более 2 000 000 строк). Пожалуйста, просмотрите этот PHP-код.Как исправить ошибку с mysql random

<?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); положения, пожалуйста, помогите, как я могу сделать выбор запроса с тузда случайной функцией, спасибо

+0

. , Вы используете 'limit' без' order by', поэтому произвольные строки возвращаются в любом случае. Я действительно не понимаю, что должен делать код. –

+0

нужно получить случайные 40 элементов из базы данных – Vahagn

+0

Только что обновил мой ответ. –

ответ

0

Это лучше, во-первых составить свой SQL запрос (в виде строки в PHP) один раз, а затем просто выполните его один раз.

Или вы могли бы использовать этот способ для выбора значения, если это соответствует вашему случаю: Select n random rows from SQL Server table

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-down – Shafizadeh

+0

@ScottArciszewski. , , Дело в том, что запрос НЕ сортирует все данные. Это занимает несколько сотен строк и сортировок. Сортировка нескольких сотен строк, как правило, прекрасна, поскольку это проблема его типа. Вы явно не понимаете логику запроса. –

2

В зависимости от того, что делает ваш код с помощью $folder, вы можете быть уязвимы для SQL injection.

Для лучшей безопасности перейдите к PDO или MySQLi и using prepared statements. Я написал библиотеку под названием EasyDB, чтобы разработчики могли лучше применять методы обеспечения безопасности.

Быстро, в здравом уме, и эффективный способ выбора N различных случайных элементов из базы данных выглядит следующим образом:

  1. Получить количество строк, которые соответствуют вашему условию (т.е. WHERE folder = ?).
  2. Создайте случайное число от 0 до этого числа.
  3. Выберите строку с заданным смещением, как и вы.
  4. Сохраните идентификатор ранее сгенерированной строки в постоянно растущем списке, чтобы исключить из результатов и уменьшить количество строк.

Пример, который использует 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 строк это может быть или не быть эффективным, в зависимости от различных факторов, связанных со структурой базы данных и подключениями к базе данных. –

Смежные вопросы