2011-04-05 81 views
0

我在XML文件中有大約30K條記錄,並且此文件一直都在更新。使用PHP將大型xml文件導入/更新到MySQL中

我想插入,如果存在更新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')"); 
    } 
} 
+0

你需要的'/ /'在'XPath'中?它也會使事情變得緩慢...... – Wrikken 2011-04-05 21:30:49

+0

-1,SQL注入。準備好的陳述可能會使這個速度明顯加快。 – 2011-04-05 22:20:17

回答

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

+0

嗨,沒有productid不是索引,它在xml中唯一,但表中有另一個索引,因爲我不能使用MySql REPLACE或DUPLICATE KEY,或者我可以嗎? – Vaidas 2011-04-05 21:36:57

+0

是的,你可以使用。你爲什麼不製作一個獨特的索引?它會加快你的查詢。此外,在運行大量INSERT語句並將其啓用之前禁用索引是一種很好的技術,否則mysql將不得不在每個INSERT語句中更新索引。 – Raisen 2011-04-05 22:06:12

0

首先我建議刷一些MySQL。第二關,通過使用您的 productid字段主鍵,你可以使用一個叫做一個更高級的SQL語句: insert ... on duplicate key update ...

It's gonna halve your database lookups for the first part,因爲你插入/更新前做的一個額外的測試。

其次,XML可能不是您的跨平臺文件的最佳解決方案。你使用這個的任何特定原因?

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,但是從我谷歌似乎可以有不被任何記錄的速度差。

0

在一個事務30K更新語句應該在合理的時間內完成(等待用戶)。也許自動提交是?另外,如果你不介意被mysql特定的話,那麼REPLACE會在一個語句中執行INSERT/UPDATE。或者您可以執行INSERT ... ON DUPLICATE KEY UPDATE。特別是,這會消除「if(mysql_num_rows(mysql_query(」Select productid from test where productid ='$ testproductid'「)))」。

+0

嗨,沒有productid不是索引,它在xml中唯一,但表中有另一個索引,因爲我不能使用MySql REPLACE或DUPLICATE KEY,或者我可以嗎?如何自動提交從php – Vaidas 2011-04-05 21:45:17

+0

如果'productid'是唯一的,則應該在表中聲明它爲'UNIQUE'。然後你可以使用'ON DUPLICATE KEY UPDATE'。 – cantlin 2011-04-05 22:03:43

0

另外,如果你不介意被mysql特定的話,那麼在一個語句中有REPLACE這樣做INSERT/UPDATE。或者你可以做INSERT ... ON DUPLICATE KEY UPDATE。特別是,這消除了if(mysql_num_rows(mysql_query("Select productid from test where productid ='$testproductid'")))

在一個事務中的30k更新語句應該在合理的時間內完成(對於等待用戶)。也許自動提交是?

+0

REPLACE是一個語句中的組合DELETE/INSERT,它爲插入的行創建一個新的自動增量值。 – Sven 2012-10-19 23:05:04

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();