使用PHP将大型xml文件导入/更新到MySQL中

时间:2011-04-05 21:19:51

标签: php mysql xml

我在XML文件中有大约30K条记录,而且这个文件一直在更新。

我正在尝试插入并且如果存在则更新MySQL数据库。

这是我想要使用的代码,但运行速度非常慢,是否有人有任何提高其性能的想法?

// getting xml file
$dom = new DOMDocument();
$dom->load('products.xml');

// getting xml nodes using xpath
$xpath = new DOMXPath($dom);
$productid = $xpath->query('//NewDataSet/Product/ProductId');
$price = $xpath->query('//NewDataSet/Product/Price');

// Reading all nodes and if mach found in db update price, else insert as new record**
for($i=0;$i<$allNodes->length;$i++){
    $testproductid = $productid->item($i)->nodeValue;
    $testprice = $price->item($i)->nodeValue;
    if(mysql_num_rows(mysql_query("Select productid from test where productid ='$testproductid'"))){
        mysql_query("UPDATE test SET price = '$testprice' WHERE productid = '$testproductid'");
    }else{
        mysql_query("INSERT INTO test (price, productid) VALUES ('$testprice','$testproductid')");
    }
}

6 个答案:

答案 0 :(得分:0)

首先,我建议刷一些MySQL。第二关,通过使用您的主键 productid字段,可以使用更高级的sql语句调用: insert ... on duplicate key update ...

It's gonna halve your database lookups for the first part,因为您在插入/更新之前正在进行一次额外的测试。

其次,XML可能不是您的跨平台文件的最佳解决方案。你使用这个的任何特殊原因?

答案 1 :(得分:0)

为什么两个查询哪个就足够了?

$sql = "INSERT INTO test (price, productid) " .
       "VALUES ('$testprice','$testproductid') " .
       "ON DUPLICATE KEY UPDATE";

if(!$query = mysql_query($sql))
   trigger_error(mysql_error());

您也可以尝试使用SimpleXML来代替DOMDocument,但是我可以通过Google获得任何记录的速度差异。

答案 2 :(得分:0)

首先,这一行可能导致不良行为:

if(mysql_num_rows(mysql_query("Select productid from test where productid ='$testproductid'")))

如果mysql_query()失败会怎么样?做一些类似的事情:

$res = mysql_query("Select productid from test where productid ='$testproductid'");
if ($res) {
... CODE HERE ...
}

productid 是一个索引吗?此外,您可以将查询表示为:

Select productid from test where productid ='$testproductid' LIMIT 1

在这种情况下,MySQL不会查找更多记录。此外,尝试在单个INSERT语句中插入多个记录。见:

http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html

查看REPLACE命令。这将取代SELECT / UPDATE / INSERT条件,但它可能不会对性能有很大的改进。

http://dev.mysql.com/doc/refman/5.0/en/replace.html

答案 3 :(得分:0)

一个事务中的30k更新语句应该在合理的时间内完成(对于等待用户)。也许是自动提交?

另外,如果您不介意特定于mysql,则REPLACE会在一个语句中执行INSERT / UPDATE。或者您可以执行INSERT ... ON DUPLICATE KEY UPDATE。特别是,这取消了“if(mysql_num_rows(mysql_query(”select productid from test where productid ='$ testproductid'))))“。

答案 4 :(得分:0)

此外,如果您不介意特定于mysql,则REPLACE在一个语句中执行INSERT / UPDATE。或者你可以做INSERT ... ON DUPLICATE KEY UPDATE。特别是,这取消了if(mysql_num_rows(mysql_query("Select productid from test where productid ='$testproductid'")))

一个事务中的30k更新语句应该在合理的时间内完成(对于等待用户)。也许是自动提交?

答案 5 :(得分:0)

通过块加载大文件的脚本 它将加载xml文件,一次读取给定数量的条目,然后将它们加载到数据库中。

$lot =5000;
$tempFiledir = '.';
$tempFile = 'temp.xml';
$table = 'mytable';
$db_username= 'root';
$db_password = 'mysql';

// count element 
    print( "    Computing items...");
    $xml_reader = new XMLReader;
    $xml_reader->open($xml_file);
    while ($xml_reader->read() && $xml_reader->name != $node_name);
    $totalItems =0;
    while ($xml_reader->name == $node_name) {
        $xml_reader->next($node_name);
        $totalItems++;
    }
    $xml_reader->close();

    print( "\r    $totalItems items found.                     ");


//Truncat the table to load into 
$xmlload_cmd = sprintf ("$mysql_exe -u%s -p%s $database_temp -e \"TRUNCATE TABLE `%s`;\" ", $db_username, $db_password, $table);
system($xmlload_cmd);                           

// move the pointer to the first item
$xml_reader = new XMLReader;
$xml_reader->open($xml_file);
while ($xml_reader->read() && $xml_reader->name != $node_name);


// load by chunks
$index = 0;
while ($xml_reader->name == $node_name){

    $tempFileXMLOutput = fopen( "$tempFiledir\\$tempFile", "w") or die("Unable to open file!");
    fwrite($tempFileXMLOutput,'<?xml version="1.0"?>');

    $index0=$index;
    do {    
        // remove self closign tags from the rendred xml output and store it in the temp file
        $data = preg_replace('/\<(\w+)\s*\/\s*\>/i', '<$1></$1>', $xml_reader->readOuterXML());
        fwrite($tempFileXMLOutput, "\n\t$data");    

        // move the pointer to the next item
        $xml_reader->next($node_name);
        $index++;
    }
    while ($xml_reader->name == $node_name && ($index % $lot != 0) );

    // close the temp file
    fclose($tempFileXMLOutput);

    echo sprintf("\r    Processing items from %6s to %6s [%3.0f%%]", $index0, $index, $index/$totalItems*100);

    // run the LOAD XML comand on the temp xml file
    $load_cmd = sprintf("LOAD XML LOCAL INFILE '%s' INTO TABLE `%s` ROWS IDENTIFIED BY '<Data>'", addslashes("$tempFiledir\\$tempFile"), $table);               

    $xmlload_cmd = sprintf ("$mysql_exe -u%s -p%s $database_temp -e \"$load_cmd\" ", $db_username, $db_password);
    system($xmlload_cmd);   

    // remove the temp file
    @unlink ( "$tempFiledir\\$tempFile");
}

$xml_reader->close();