Как оптимизировать скрипт wp_query для csv?

Я сделал этот скрипт, в котором я запускаю WP_Query(), который получает около 2000 записей и продолжает расти. Я хочу иметь возможность записывать эти результаты в файл CSV. У меня это работает, когда для posts_per_page установлено значение 300 или меньше записей.

Если я изменю posts_per_page на -1, я получу эту ошибку:

Fatal error: Allowed memory size of 134217728 bytes exhausted

Мне интересно, есть ли способ отправить 300 записей за раз в CSV-файл за раз, пока их больше не будет, а затем запустить файл для загрузки?

Или, может быть, передать каждую строку в файл CSV? Как лучше всего управлять памятью при выполнении fopencsv?

Вот что у меня есть сейчас:

$export_query = array(
    'post_type' => 'videorepot',
    'posts_per_page' => -1,
    'status' => 'published',
    'meta_query' => array(
        array(
            'key'     => 'agree',
            'value'   => 1,
            'compare' => '=',
        ),
        array(
            'key'     => 'opt-in',
            'value'   => 1,
            'compare' => '=',
        ),
        array(
            'key'     => 'status',
            'value'   => 'Video Ready',
            'compare' => '=',
        ),
    )
);

// Posts query
$wp_query = new WP_Query( $export_query );
if ( $wp_query->have_posts() ) :
    $list = array( 'Email,Photo1,Photo2,Photo3,VideoURL' );
    while ( $wp_query->have_posts() ) : $wp_query->the_post();

        $postID = get_the_ID();

        $user_email = get_post_meta($postID, 'user_email', true);
        $photo1 = get_post_meta($postID, 'photo1', true);
        $photo2 = get_post_meta($postID, 'photo2', true);
        $photo3 = get_post_meta($postID, 'photo3', true);
        $videourl = get_post_meta($postID, 'video_file', true);

        $list[] = $user_email.','.$photo1.','.$photo2.','.$photo3.','.$videourl;

    endwhile;
endif;


// Output file stream
$output_filename = 'export_' . strftime( '%Y-%m-%d' )  . '.csv';
$output_handle = @fopen( $output_filename, 'w' );

header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
header( 'Content-Description: File Transfer' );
header( 'Content-type: text/csv' );
header( 'Content-Disposition: attachment; filename=' . $output_filename );
header( 'Expires: 0' );
header( 'Pragma: public' );

foreach ( $list as $line ) {

    // Add row to file
    fputcsv( $output_handle, explode(',', $line), ',', '"');

}

// Close output file stream
fclose( $output_handle );

// We're done!
exit;

Обновление №1. Для некоторого тестирования я удалил часть скрипта, которая записывает элементы в CSV-файл, и поэтому я обнаружил, что в моем запросе действительно есть что-то, что сводит его с ума. В моем пользовательском типе сообщений может быть 3000 записей. Почему это может привести к тому, что мой запрос исчерпает память?

Обновление №2

Я решил пойти другим путем, потому что у меня все еще возникают проблемы с памятью при использовании wp_query. Итак, я написал свой запрос mySQL — вы знаете, я собрал его из других примеров. Этот запрос выполняется очень быстро, поэтому я думаю, что мне с ним повезет больше.

У меня новый вопрос, связанный с meta_key/meta_values. В настоящее время я использую SELECT * в своем запросе, но я не хочу выбирать все. Мне нужно всего около 5 полей из базы данных. Некоторые из этих полей, которые мне нужны, это meta_key/meta_value. Есть ли способ указать эти мета_ключи, чтобы при экспорте таблицы я мог просто получить 5 столбцов с результатами?

Мой запрос теперь выглядит так:

$values = mysql_query("SELECT * FROM wp_posts 
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) WHERE 1=1 
AND ( ( post_date >= '2014-11-03 00:00:00' AND post_date <= '2014-12-31 23:59:59' ) ) 
AND wp_posts.post_type = 'videorepot' 
AND (wp_posts.post_status = 'publish') 
AND ( (wp_postmeta.meta_key = 'agree' AND CAST(wp_postmeta.meta_value AS CHAR) = '1') 
AND (mt1.meta_key = 'opt-in' AND CAST(mt1.meta_value AS CHAR) = '1') 
AND (mt2.meta_key = 'status' AND CAST(mt2.meta_value AS CHAR) = 'Video Ready') ) 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC 
LIMIT 0,20");

На данный момент я устанавливаю ограничение на 20, но я протестировал его с ограничением, установленным на 0,9999, и он работает очень быстро в phpmyadmin. Приведенный выше запрос дает около 1282 результатов, если я не устанавливаю ограничение.

Обновление №3

Я опубликовал решение своего ответа. Спасибо за помощь Дэвид.


person Robbiegod    schedule 10.12.2014    source источник
comment
увеличьте php-память... в качестве альтернативы добавьте метаданные сообщения, чтобы обозначить, что вы загрузили, и установите максимальное количество результатов на 200 или около того (исключая загруженные)   -  person David    schedule 11.12.2014
comment
Я бы предпочел не просто увеличивать память php. Мне интересно узнать правильную технику PHP для управления памятью в этой ситуации. Я попробую это в настоящее время, хотя. Также согласно ошибке мне разрешено 134 МБ памяти, я думаю, что это довольно много. Дело в том, что я хочу экспортировать полную базу данных каждый раз, когда щелкают ссылку, поэтому нет необходимости устанавливать элемент как загружаемый. Тем не менее спасибо за ваш комментарий.   -  person Robbiegod    schedule 11.12.2014
comment
правда, но у вас нет 134 МБ для работы ... но отключите запрос wp после вашего цикла и используйте while вместо foreach (foreach снова создает еще одну копию, поэтому у вас фактически есть 3 копии на ходу), а затем отключите массив $ list когда вы перебираете его.   -  person David    schedule 11.12.2014
comment
Ok. Я добавил wp_reset_query() сразу после цикла для сброса wp_query. Меня немного смущает использование while вместо foreach, потому что я загружаю результаты запроса в массив, а затем использую foreach для добавления каждого элемента в CSV. Я могу попробовать удалить foreach и переместить fputcsv в цикл While. Может ты это имеешь в виду? Я уверен, что где-то есть функция unset($list), я поищу ее. Спасибо.   -  person Robbiegod    schedule 11.12.2014
comment
foreach создает копию данных, а выполняет цикл по данным без копии. На самом деле вы все равно можете использовать цикл wp while, который сохраняет цикл дважды. Надеюсь, вам не нужно будет сбрасывать значения на каждой итерации, просто сбрасывайте данные публикации в конце цикла.   -  person David    schedule 11.12.2014
comment
хм, я думаю, что сейчас проблема с запросом или созданием исходного массива, потому что я закомментировал биты, которые записывают данные в файл CSV, а затем я пытаюсь просто вывести массив, который я сделал, и я получаю ошибку предела памяти еще. Я собираюсь возиться с этим в первую очередь.   -  person Robbiegod    schedule 11.12.2014


Ответы (2)


Я пришел к хорошему решению, я думаю. Это работает для меня, и мне не нужно было увеличивать память php или что-то в этом роде.

Вот окончательный, неотредактированный сценарий. Имейте в виду, что запрос mysql, который я использую, использует диапазон дат и 3 мета_ключа для получения подмножества данных из нашей базы данных. Я получаю около 1290 результатов, записанных в файл CSV. (Спасибо Дэвиду за помощь.)

$path = $_SERVER['DOCUMENT_ROOT'];

include_once $path . '/wp-load.php';

// SET UP DB VARS
$host = DB_HOST;
$user = DB_USER;
$pass = DB_PASSWORD;
$db = DB_NAME;


// CONNECT TO DB
$link = mysql_connect($host, $user, $pass) or die("Can not connect." . mysql_error());
mysql_select_db($db) or die("Can not connect.");

$mycolums = array( 'Email,Photo1,Photo2,Photo3,VideoURL' );
foreach ( $mycolums as $column ) {
    $csv_output = $column.", ";
}
$csv_output .= "\n";

$values = mysql_query("SELECT * FROM wp_posts 
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) WHERE 1=1 
AND ( ( post_date >= '2014-11-03 00:00:00' AND post_date <= '2014-12-31 23:59:59' ) ) 
AND wp_posts.post_type = 'videorepot' 
AND (wp_posts.post_status = 'publish') 
AND ( (wp_postmeta.meta_key = 'agree' AND CAST(wp_postmeta.meta_value AS CHAR) = '1') 
AND (mt1.meta_key = 'opt-in' AND CAST(mt1.meta_value AS CHAR) = '1') 
AND (mt2.meta_key = 'status' AND CAST(mt2.meta_value AS CHAR) = 'Video Ready') ) 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC 
LIMIT 0,9999");
while ($rowr = mysql_fetch_row($values)) {

        $postID = $rowr[0];         
        $user_email = get_post_meta($postID, 'email_address', true);
        $photo1 = get_post_meta($postID, 'photo_1', true);
        $photo2 = get_post_meta($postID, 'photo_2', true);
        $photo3 = get_post_meta($postID, 'photo_3', true);
        $videourl = get_post_meta($postID, 'the_video', true);

        // $csv_output .= $rowr['']."; ";
        $csv_output .= $user_email.', '.$photo1.', '.$photo2.', '.$photo3.', '.$videourl.',';
        $csv_output .= "\n";

}

$output_filename = "export_".date("Y-m-d_H-i",time()).".csv";
header("Content-type: application/vnd.ms-excel");
header("Content-disposition: csv" . date("Y-m-d_H-i",time()) . ".csv");
header( "Content-disposition: filename=".$output_filename);
print_r($csv_output);
exit;

Я надеюсь, что это поможет кому-то еще, кто имеет дело с проблемами памяти.

person Robbiegod    schedule 11.12.2014

Я также получал сообщение «Пытался выделить больше памяти, фатальная ошибка», но мое решение заключалось в том, что внутри цикла while я пропустил эту строку:

$wp_query->the_post();

Может быть полезно, если кто-то еще сталкивается с той же проблемой.

person vats    schedule 25.05.2015