我有一个要插入的csv文件,包含约1,500行和97列。完全导入大约需要2-3个小时,如果可以的话,我想改善这一点。当前,对于每一行,我正在为$ 97关联的每一行执行$ post_id = wp_insert_post,然后执行add_post_meta。这是非常低效的...

是否有更好的方法来解决这个问题,即可以获取post_id来保持post及其post_meta值之间的关系?

现在,我正在使用wamp的本地计算机上尝试此操作,但将使其在VPS上运行

评论

除了下面的WP技巧,还请按照此答案查看在MySQL中使用InnoDB并批量提交事务。

#1 楼

某个时候,我在使用自定义CSV导入时遇到了类似的问题,但是最后我对批量插入使用了一些自定义SQL。但是到那时我还没有看到这个答案:

为批量操作优化帖子的插入和删除?

使用wp_defer_term_counting()启用或禁用术语计数。

此外,如果您查看WordPress导入程序插件的源代码,则将在批量导入之前看到以下功能:

wp_defer_term_counting( true );
wp_defer_comment_counting( true );


,然后在批量导入之后插入:

wp_defer_term_counting( false );
wp_defer_comment_counting( false );


所以这可能是要尝试的东西;-)

将帖子作为草稿而不是发布导入,也会加快速度向上,因为跳过了为每个人寻找唯一子弹的缓慢过程。一个可以例如稍后以较小的步骤发布它们,但是请注意,这种方法需要以某种方式标记导入的帖子,因此我们不只是稍后发布任何草稿!

如果有例如要导入很多类似的帖子标题(相同的post_name),则由于循环查询迭代查找可用的子弹,wp_unique_post_slug()可能变慢。这可能会生成大量的数据库查询。

自WordPress 5.1起,可以使用pre_wp_unique_post_slug过滤器来避免对Slug进行循环迭代。请参阅核心故障单#21112。这是一个示例:

add_filter( 'pre_wp_unique_post_slug', 
    function( $override_slug, $slug, $post_id, $post_status, $post_type, $post_parent ) {
        // Set a unique slug value to shortcircuit the slug iteration loop.
        // $override_slug = ...

        return $override_slug;
    }, 10, 6
);


如果尝试例如$override_slug = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix",其中$suffix$post_id,那么我们会注意到,对于新帖子,$post_id始终为0。尽管有多种方法可以在PHP中生成唯一数字,例如uniqid( '', true )。但是请小心使用此过滤器,以确保您有独特的子弹。我们可以例如请确保随后在post_name上运行组计数查询。

另一个选择是使用WP-CLI以避免超时。参见例如我的答案发布为使用.csv文件创建20,000个帖子或页面?

然后,我们可以使用WP-CLI命令运行自定义PHP导入脚本import.php

wp eval-file import.php


也避免像当前的wp-admin那样导入大量的分层文章类型用户界面处理不好。参见例如自定义帖子类型-帖子列表-死亡白屏

这里是@otto的提示:

在批量插入之前,请明确禁用autocommit模式:

$wpdb->query( 'SET autocommit = 0;' );


大容量插入后,运行:

$wpdb->query( 'COMMIT;' );


我也认为做一些整理工作是个好主意,例如:

$wpdb->query( 'SET autocommit = 1;' );


我没有在MyISAM上测试过,但这应该在InnoDB上有效。

@kovshenin提到的这一技巧不会为MyISAM工作。

评论


除此之外,还可以使用查询功能在插入之前关闭自动提交,然后在插入之后手动提交。进行批量插入时,这可以大大加快数据库级别的操作。只需发送SET autocommit = 0;在插入之前,后面是COMMIT;之后。

–奥托
13年6月8日在20:27

有趣,谢谢!我到家时必须测试一下。

– Corey Rowell
13年6月8日在20:31

@Otto,感谢您的宝贵建议。因此我们可以执行$ wpdb-> query('SET autocommit = 0;');在插入之前,但是我们可以跳过$ wpdb-> query('START TRANSACTION;');吗?在这种情况下?我将查看MySQL手册以进一步了解它;-)欢呼。

–birgire
2013年6月8日在21:05



好点马克。如果这些只是插入而不是更新,则wp_suspend_cache_addition(true)应该有助于将内容不放入对象缓存中。 @birgire还提到他们没有使用MyISAM对此进行测试-不用担心,存储引擎不支持事务,因此设置自动提交或开始事务将产生零影响。

–kovshenin
16年8月2日,0:01

大提示@Otto。我的查询之前花费了38秒,现在花费了1秒。

–安纳布尔纳
17年8月22日在10:47

#2 楼

我必须添加此代码:

    remove_action('do_pings', 'do_all_pings', 10, 1);


请记住,这将跳过do_all_pings,它处理pingback,附件,引用和其他ping(链接:https:// developer.wordpress.org/reference/functions/do_all_pings/)。通过查看代码,我的理解是,删除此remove_action行后,仍将处理挂起的pingbacks / trackbacks /机箱,但我不确定。

更新:我还添加了

    define( 'WP_IMPORTING', true );


除此之外,我还使用:

    ini_set("memory_limit",-1);
    set_time_limit(0);
    ignore_user_abort(true);

    wp_defer_term_counting( true );
    wp_defer_comment_counting( true );
    $wpdb->query( 'SET autocommit = 0;' );

    /* Inserting 100,000 posts at a time
       including assigning a taxonomy term and adding meta keys
       (i.e. a `foreach` loop with each loop containing:
       `wp_insert_post`, `wp_set_object_terms`, `add_post_meta`.)
    */

    $wpdb->query( 'COMMIT;' );
    wp_defer_term_counting( false );
    wp_defer_comment_counting( false );


#3 楼

您将需要插入帖子以获取您的ID,但是$wpdb->postmeta表的结构非常简单。您可能会使用直接的INSERT INTO语句,例如来自MySQL文档的INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

以您的情况...

$ID = 1; // from your wp_insert_post
$values = '($ID,2,3),($ID,5,6),($ID,8,9)'; // build from your 97 columns; I'd use a loop of some kind
$wpdb->query("INSERT INTO {$wpdb->postmeta} (post_id,meta_key,meta_value) VALUES {$values}");


不会处理任何编码,序列化,转义,错误检查,重复或其他操作,但是我希望它会更快(尽管我没有尝试过)。

我不会做无需经过全面测试就可以在生产现场进行此操作,如果只需要执行一次或两次,则可以使用核心功能并在导入内容时花很长的午餐。

评论


认为我会吃一顿长饭,而不是将原始数据插入表中,并且没有必要重写Wordpress已经完成的工作。

– Corey Rowell
2013年6月8日18:27

这就是mysql注入的过程,所以请不要使用它。

–OneOfOne
15年3月9日在17:04

一切都是硬编码的@OneOfOne。没有用户提供的输入,注入就不会发生-根据定义是不会发生的。这就是“注射”的性质。 OP正在使用他控制下的代码从.csv文件中导入数据,该文件由他控制。第三方注入任何东西都是没有机会的。请注意上下文。

– s_ha_dum
2015年4月10日0:00,

从我+1,我需要添加20个海关字段值,这比“ add_post_meta”要快得多

–佐罗克斯
2015年10月5日12:48



您不能指望OP在导入之前彻底检查CSV文件,因此您应该将其视为用户输入,并且至少-> prepare()SQL语句。在您的方案中,如果CSV中的ID列包含诸如1,'foo','bar')之类的内容,将会发生什么?删除表wp_users; -?可能是坏事。

–kovshenin
16年8月1日在23:58

#4 楼

关于'SET autocommit = 0;'的重要说明在设置autocommit = 0之后,如果脚本停止执行(由于某些原因,例如exit,致命错误等),则所做的更改将被保存在DB中!

$wpdb->query( 'SET autocommit = 0;' );

update_option("something", "value");     

exit; //lets say, here happens error or anything...

$wpdb->query( 'COMMIT;' );


在这种情况下,update_option不会保存在数据库中!

因此,最好的建议是在COMMIT函数中注册shutdown作为预警(以防万一发生意外退出)。

register_shutdown_function( function(){     $GLOBALS['wpdb']->query( 'COMMIT;' );    } );