PHP, как запустить SQL запрос по одной части за раз? - PullRequest
1 голос
/ 15 декабря 2009

У меня есть таблица с примерно 1 миллионом строк. Я делаю простую программу, которая печатает одно поле из каждой строки. Однако, когда я начал использовать mysql_pconnect и mysql_query, запрос занимал много времени, я предполагал, что запрос должен завершиться, прежде чем я смогу распечатать даже первую строку. Есть ли способ обрабатывать данные постепенно?

- Edited - Я не собираюсь извлекать небольшой набор данных, я ищу способ обработки данных порцией (скажем, выборка 10 строк, печать 10 строк, выборка 10 строк, печать 10 строк и т. Д. И т. Д.) чем ждать запроса, чтобы получить 1 миллион строк (кто знает, как долго), а затем начать печать.

Ответы [ 8 ]

3 голосов
/ 15 декабря 2009

Печать миллиона полей займет некоторое время. Извлечение миллиона записей займет некоторое время. Время складывается.

Вы профилировали свой код? Я не уверен, что использование лимита имело бы столь существенное значение в этом случае.

Делая что-то подобное

while ($row = mysql_fetch_object($res)) {
   echo $row->field."\n";
}

выводит одну запись за раз. Он не ожидает возврата всего набора результатов.

Если вы имеете дело с браузером, вам понадобится что-то большее.

как этот

ob_start();
$i = 0;
while ($row = mysql_fetch_object($res)) {
   echo $row->field."\n";
   if (($i++ % 1000) == 0) {
       ob_flush();
   }
}
ob_end_flush();
2 голосов
/ 15 декабря 2009

Вы действительно хотите напечатать миллион полей?

Обычное решение - использовать в своем веб-приложении какой-либо вид нумерации выходных данных, показывающий только часть результата. В запросах SELECT вы можете использовать ключевое слово LIMIT для возврата только части данных. На самом деле это базовый SQL-материал. Пример:

SELECT * FROM table WHERE (some conditions) LIMIT 40,20

показывает 20 записей, начиная с 40-го (возможно, ошибки с моей стороны возможны).

Возможно, потребуется использовать ORDER BY вместе с LIMIT, чтобы предотвратить случайное изменение порядка между вашими запросами.

1 голос
/ 15 декабря 2009

Это обычно необходимо для нумерации страниц. Вы можете использовать ключевое слово limit в вашем запросе выбора. Искать лимит здесь :

Предложение LIMIT может использоваться для ограничения количества строк, возвращаемых оператором SELECT. LIMIT принимает один или два числовых аргумента, которые оба должны быть неотрицательными целочисленными константами (кроме случаев использования подготовленных операторов).

С двумя аргументами первый аргумент задает смещение первой строки, которую нужно вернуть, а второй - максимальное количество строк, которые нужно вернуть. Смещение начальной строки 0 (не 1):

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

Чтобы извлечь все строки от определенного смещения до конца набора результатов, вы можете использовать некоторое большое число для второго параметра. Этот оператор извлекает все строки от 96-й строки до последней:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

С одним аргументом значение указывает количество строк, возвращаемых с начала набора результатов:

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

Другими словами, LIMIT row_count эквивалентно LIMIT 0, row_count.

0 голосов
/ 15 декабря 2009

Используйте mysql_unbuffered_query() или, если используете PDO, убедитесь, что PDO::MYSQL_ATTR_USE_BUFFERED_QUERY равно false.

Также см. этот похожий вопрос .

Редактировать: и, как говорили другие, вы можете захотеть объединить это с очисткой буфера вывода после каждой партии обработки, в зависимости от ваших обстоятельств.

0 голосов
/ 15 декабря 2009

Звучит так, как будто вы выходите за пределы разных размеров буфера на сервере mysql ... Некоторые методы, которые вы могли бы сделать, это указать поле, которое вы хотите в операторе SQL, чтобы уменьшить этот размер буфера, или поэкспериментировать с различные настройки администратора.

ИЛИ, вы можете использовать нумерацию страниц , как , но вывести все на одной странице ...

(псевдокод)

 function q($part) {
      $off = $part*SIZE_OF_PARTITIONS;
      $size = SIZE_OF_PARTITIONS;

      return( execute_and_return_sql('SELECT `field` FROM `table` LIMIT $off, $size'));
    }

    $ii = 0;

    while ($elements = q($ii)) {
      print_fields($elements);
      $ii++;
    }
0 голосов
/ 15 декабря 2009

Пример использования базового драйвера mysql.

define( 'CHUNK_SIZE', 500 );

$result = mysql_query( 'select count(*) as num from `table`' );
$row = mysql_fetch_assoc( $result );

$totalRecords = (int)$row['num'];

$offsets = ceil( $totalRecords / CHUNK_SIZE );

for ( $i = 0; $i < $offsets; $i++ )
{
  $result = mysql_query( "select * from `table` limit " . CHUNK_SIZE . " offset " . ( $i * CHUNK_SIZE ) );
  while ( $row = mysql_fetch_assoc( $result ) )
  {
    // your per-row operations here
  }
  unset( $result, $row );
}

Это будет выполнять итерацию по всему объему строки, но делать это только 500 строк за раз, чтобы уменьшить использование памяти.

0 голосов
/ 15 декабря 2009

Вот как я делаю что-то подобное в Oracle. Я не уверен, как это перешло бы:

declare
my_counter integer := 0;
begin
for cur in (
select id from table
) loop
  begin
    -- do whatever your trying to do
    update table set name = 'steve' where id = cur.id;
    my_counter := my_counter + 1;
    if my_counter > 500 then
      my_counter := 0;
      commit;
    end if;
    end;
  end loop;
  commit;
end;
0 голосов
/ 15 декабря 2009

Вы можете использовать Mysqli :: use_result

в сочетании с flush для вывода набора данных в браузер. Я знаю, что flush может использоваться для вывода данных в браузер в инкрементном состоянии, поскольку я использовал его прежде для этого, однако я не уверен, является ли mysqli :: use_result правильной функцией для получения неполных наборов результатов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...