$wpdb->从XML文件批量插入?

时间:2019-06-03 作者:click here

事实上我被困在这上面了!

XML文件约140.000行,文件大小约9.0 MBLOAD XML INFILE \'file_name\' 不允许(共享主机!)供应商提供一个包含数量和sku的XML文件。XML的格式如下:

<item>
<LITM>0000001</LITM>
<STQU>1<STQU>
</item>

<item>
<LITM>0000002</LITM>
<STQU>4<STQU>
</item>
以下函数可以工作,但正如您所想,它的速度很慢!

function load_xml2mysql($filename,$type) {
    global $wpdb;

    $file = XML_DIR.$filename;
    $xml = new XMLReader();
    $xml->open($file);

    $charset = $wpdb->get_charset_collate();
    $table = $wpdb->prefix.\'supplier_stock\';

    ignore_user_abort();

    while($xml->read() && $xml->name != \'item\'){ ; }

    while($xml->name == \'item\')
    {
        $element = new SimpleXMLElement($xml->readOuterXML());

        $query = $wpdb->prepare("INSERT INTO {$table} (sku,stock) VALUES (%d,%d) ON DUPLICATE KEY UPDATE stock = %d", strval($element->LITM),strval($element->STQU),strval($element->STQU));
        $wpdb->query( $query );
        $wpdb->flush();

        $xml->next(\'item\');
        unset($element);
    }
}

我已经读了很多关于批/批量插入的书,关于这方面的基准非常清楚。使用my函数查询140000行平均需要40秒。Stock表是三个XML文件中最小的一个,试想一下,插入总文件大小约为145.0 MB的主要产品数据可能会给我带来一些麻烦?

关于如何改进或批量插入有何想法?

3 个回复
SO网友:phatskat

您可以尝试通过使用Generator:

function load_xml2mysql($filename,$type) {
    global $wpdb;

    $charset = $wpdb->get_charset_collate();
    $table = $wpdb->prefix.\'supplier_stock\';

    ignore_user_abort();

    $query_template = "INSERT INTO {$table} (sku,stock) VALUES (%d,%d) ON DUPLICATE KEY UPDATE stock = %d";
    $wpdb->query( \'SET autocommit = 0\' );
    foreach ( get_xml_element( $filename ) as $element ) {
        $query = $wpdb->prepare(
            $query_template,
            strval($element->LITM),
            strval($element->STQU),
            strval($element->STQU)
        );

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

function get_xml_element( $filename ) {
    $file = XML_DIR.$filename;
    $xml = new XMLReader();
    $xml->open($file);

    // I\'m not clear on what exactly this is doing, do you need it?
    // edit: this moves us to the first <item> node 
    while($xml->read() && $xml->name != \'item\'){ ; }

    while ( $xml->name === \'item\' ) {
        yield new SimpleXMLElement( $xml->readOuterXML() );
        $xml->next(\'item\');
    }
}
生成器往往比只做一个循环更有效率,在这种情况下,它可能就是您所需要的。

我们也在做SET autocommit=0 在运行查询之前,这应该会提高性能,因为您告诉MySQL在调用之前不要提交更改COMMIT 循环之后。

SO网友:click here

在面临内存耗尽和查询耗时的问题后,我尝试了另一种批量导入方法。这个想法并不新鲜,但为了展示一种可能的方式,我想分享我的解决方案。请记住,我不是PHP/MySQL方面的专业人员,所以也许有一种更有效或更好的方法可以以不同的方式或更好的性能完成此任务

由于@kuchenundkakao向我提出了一些新想法,使用临时表导入整个XML文件,并在第二次查询中处理其中的数据,因此使用$wpdb实现了大容量插入的主要功能。总之,bulk\\u insert函数没有检查或更新任何现有数据,表得到truncated 处理完成后每24小时一次。

它是在PHP类中使用的,所以如果您试图复制和粘贴,请注意-检查语法!

// Table, including your prefix!
$table = $wpdb->prefix.\'table_name\';

$rows = array(
           array(\'price\' => \'12.00\',\'vat\' => \'7.7\'),
           array(\'price\' => \'230.00\', \'vat\' => \'7.7\')
            );
我的内容bulk_insert 作用

    function bulk_insert($table, $rows) {
        global $wpdb;

            /* Extract column list from first row of data! */ 
            $columns = array_keys($rows[0]);
            asort($columns);
            $columnList = \'`\' . implode(\'`, `\', $columns) . \'`\';

            /* Start building SQL, initialise data and placeholder arrays */
            $sql = "INSERT INTO `$table` ($columnList) VALUES\\n";
            $placeholders = array();
            $data = array();

            /* Build placeholders for each row, and add values to data array */
            foreach ($rows as $row) {
                ksort($row);
                $rowPlaceholders = array();
                    foreach ($row as $key => $value) {
                        $data[] = $value;

                        /* differentiate values and set placeholders */
                        if (is_numeric($value)) {
                                $rowPlaceholders[] = is_float($value) ? \'%f\' : \'%d\';
                        } else {
                                $rowPlaceholders[] = \'%s\';
                        }
                    }
                $placeholders[] = \'(\' . implode(\', \', $rowPlaceholders) . \')\';
            }

            /* Stitching all rows together */
            $sql .= implode(",\\n", $placeholders);
            // Run the query.  Returning number of affected rows for this chunk
            return $wpdb->query($wpdb->prepare($sql, $data));
    }
对XML数据进行分块(Chunking XML Data)

您可以将多少数据推送到bulk_insert() 而且很大程度上取决于您的主机/服务器,因此我通过在发送到之前限制数据量,使其变得灵活且易于调整bulk_insert().

What is this function doing?它使用XMLReader和SimpleXMLElement逐行解析XML文档,而不是解析整个文档(大部分以耗尽内存结束)。在给定金额后,由$limit 中的XML元素$array[] 到达时,函数将此XML文件块作为数组发送到bulk_insert() -> 将整个数组标记到数据库中。

我正在将文件名传递给load_price_xml2mysql 出于灵活性目的,如myfile.xml

function load_price_xml2mysql($filename) {
        global $wpdb;

    /* get xml file */

      $file = WP_UPLOAD_DIR.$filename;
      $xml = new XMLReader();
      $xml->open($file);

    /* set your limit here, define the limit in your plugin or just add $limit = 1000 */
        $limit = MYSQL_PRICE_INSERTLIMIT;

        $array = array();
        $table = $wpdb->prefix.\'temp_api_price\';

       /* counting entries, so we have to set zero before while */
        $i = 0;
        while($xml->read() && $xml->name != \'item\'){ ; }
        while ( $xml->name === \'item\' ) {
            ++$i;

            $element = new SimpleXMLElement( $xml->readOuterXML() );
                $array[] = array(
                                 \'sku\' => intval($element->SKU),
                                 \'buying\' => floatval($element->price->BUYING),
                                 \'buying_ex_vat\' => floatval($element->price->BUYING_EX),
                                 \'vat\' => floatval($element->price->VAT),
                                 \'selling\' => floatval($element->price->SELLING)
                                );

            /* start chunking the while routine by $limit
             passing the array to bulk_insert() if the limits gets reached. */

            if (count($array) == $limit) {
            $this->bulk_insert($table, $array);
            unset($array);
            }
                unset($element);
                $xml->next(\'item\');
      }
        /* don\'t miss the last chunk, if it didn\'t reach the given limit, send it now! */
        $this->bulk_insert($table, $array);
       /* return the total amount of entries */
        return $i;
    }

结论你必须找到你的工作$limit, 仅供参考:

product_item.xml
 - 140MB
 - 9.1 Million rows in document
 - 142.000 items 
$limit = 5000;
$runtime = 43.93240232324 [average]
运行时=包括解析(&N);正在插入MySQL数据库。

每排6500行是最大工作限制,所以我决定多留一点空间,然后去$limit = 5000 并且仍然达到相同的平均运行时间。可以肯定的是,大部分时间都需要解析XML文档,也许我会对每个函数分别进行基准测试和跟踪微时间。

非常感谢@phatskat和@kuchenandkakao把我推向了正确的方向。

SO网友:HU ist Sebastian

首先:不要在每次插入后冲洗。我没有对此进行测试,但如果导入所有数据,然后进行刷新,应该会更快。

第二:你到底想完成什么?是一次性导入,还是定期更新数据库?如果您需要选项a),那么请等待。如果需要,可以增加PHP的最大运行时间。

如果您想定期更新数据库,那么您可能应该采用另一种方法。例如,首先读取XML文件并将其转换为数组/将其放入临时数据库表中,检查是否必须添加或更新文章,然后在第三步中,通过添加新文章并更新要更新的文章来完成。

快乐的编码!

相关推荐

需要MySQL查询或WP-CLI命令来更新快捷代码中的旧URL

我客户的网站已从http 到https, 我已经使用phpMyAdmin中的查询更新了大多数URL,但我无法更新短代码URL。我已使用以下查询替换中的URLpost_content, 但它对短码URL没有影响:UPDATE `wp_posts` SET post_content = REPLACE(post_content, \'src=\"http://example.com\', \'src=\"https://example.com\') WHERE post_content