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

使用PHP将大型xml文件导入/更新到MySQL,php,mysql,xml,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->

我在一个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')");
    }
}

首先,我建议刷一些。第二种方法是在你的键盘上使用主键 productid字段中,可以使用更高级的sql语句,称为: 插入在重复密钥更新时

,因为您在插入/更新之前要做一个额外的测试


其次,XML可能不是跨平台文件的最佳解决方案。有什么特别的原因吗?

为什么两个查询一个就足够了

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

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

您也可以尝试代替,但从我的谷歌搜索结果来看,似乎没有任何记录在案的速度差异。

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

if(mysql_num_rows(mysql_query("Select productid from test where productid ='$testproductid'")))
如果mysql\u查询失败怎么办?改为这样做:

$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语句中插入多条记录。见此:

看看REPLACE命令。这将取代SELECT/UPDATE/INSERT条件,但这可能不是性能的重大改进


一个事务中的30k update语句应在等待用户的合理时间内完成。也许自动提交开启了


另外,如果您不介意特定于mysql,那么有一个REPLACE,它在一条语句中插入/更新。或者你可以插入。。。在重复密钥更新时。特别是,这不需要使用ifmysql\u num\u rowsmysql\u querySelect productid from test,其中productid='$testproductid'。

另外,如果您不介意特定于mysql,还有一个REPLACE,它在一条语句中插入/更新。或者你可以插入。。。在重复密钥更新时。特别是,这消除了ifmysql_num_rowsmysql_querySelect productid from test,其中productid='$testproductid'


一个事务中的30k update语句应在等待用户的合理时间内完成。“自动提交”可能处于启用状态?

按块加载大型文件的脚本 它将加载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();

您需要XPath中的//吗?这也会使事情变得缓慢…-1,SQL注入。准备好的语句可能会大大加快速度。您好,no productid不是索引,它在xml中是唯一的,但表有另一个索引,因为我不能使用MySql替换或复制键,或者我可以吗?是的,您可以使用。为什么不将productid作为唯一索引?它将大大加快您的查询速度。另外,在运行大量INSERT语句之前禁用索引并重新启用它们也是一种很好的技术,否则mysql将不得不在每个INSERT语句中更新索引。嗨,no productid不是索引,它在xml中是唯一的,但表中有另一个索引,因为我不能使用mysql替换或复制键,或者我可以吗?phpIf productid的自动提交如何是唯一的,您应该在表中声明它是唯一的。然后,您可以在重复密钥更新中使用。REPLACE是一条语句中的DELETE/INSERT组合,它为插入的行创建一个新的自动增量值。