簡體   English   中英

在不影響現有記錄的情況下將新的XML數據導入MySQL表

[英]Import new XML data into MySQL table without affecting existing records

我有一個很大的(2.7mb)XML文件,其結構如下:

<?xml version="1.0"?>

<Destinations>

  <Destination>
    <DestinationId>W4R1FG</DestinationId>
    <Country>Pakistan</Country>
    <City>Karachi</City>
    <State>Sindh</State>
  </Destination>

  <Destination>
    <DestinationId>D2C2FV</DestinationId>
    <Country>Turkey</Country>
    <City>Istanbul</City>
    <State>Istanbul</State>
  </Destination>

  <Destination>
    <DestinationId>5TFV3E</DestinationId>
    <Country>Canada</Country>
    <City>Toronto</City>
    <State>Ontario</State>
  </Destination>  

  ... ... ...

</Destinations>

和一個MySQL表“目的地”是這樣的:

+---+--------------+----------+---------+----------+
|id |DestinationId |Country   |City     |State     |
+---+--------------+----------+---------+----------+
|1  |W4R1FG        |Pakistan  |Karachi  |Sindh     |
+---+--------------+----------+---------+----------+
|2  |D2C2FV        |Turkey    |Istanbul |Istanbul  |
+---+--------------+----------+---------+----------+
|3  |5TFV3E        |Canada    |Toronto  |Ontario   |
+---+--------------+----------+---------+----------+
|.  |......        |......    |.......  |.......   |
+---+--------------+----------+---------+----------+

現在,我要處理我的XML並檢查MySQL表中的每個目標記錄。 我只需要將DestinationId與每個記錄進行比較,並檢查它是否存在於我的數據庫表中。 如果確實存在,則保留該記錄並繼續,如果不存在,則執行INSERT查詢以將該記錄插入該表中。

我首先嘗試使用PHP foreach循環機制來完成此任務,但是由於數據如此之大,因此導致了嚴重的性能和速度問題。 然后我想出了一個MySQL Procedure方法,例如:

DELIMITER $$

USE `destinations`$$

DROP PROCEDURE IF EXISTS `p_import_destinations`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `p_import_destinations`(
    p_xml                     TEXT
)
BEGIN
    DECLARE v_row_index INT UNSIGNED DEFAULT 0;
    DECLARE v_row_count INT UNSIGNED;
    DECLARE v_xpath_row VARCHAR(255);

    -- calculate the number of row elements.
    SET v_row_count := extractValue(p_xml,'count(/Destinations/Destination)');

    -- loop through all the row elements
    WHILE v_row_index < v_row_count DO        
        SET v_row_index := v_row_index + 1;
        SET v_xpath_row := CONCAT('/Destinations/Destination[',v_row_index,']');

    INSERT IGNORE INTO destinations VALUES (
        NULL,
        extractValue(p_xml,CONCAT(v_xpath_row, '/child::DestinationId')),
        extractValue(p_xml,CONCAT(v_xpath_row, '/child::Country')),
        extractValue(p_xml,CONCAT(v_xpath_row, '/child::City')),
        extractValue(p_xml,CONCAT(v_xpath_row, '/child::State'))
    );


    END WHILE;

END$$  

DELIMITER ;

查詢以調用此過程:

SET @xml := LOAD_FILE('C:/Users/Muhammad Ali/Desktop/dest.xml'); 
CALL p_import_destinations(@xml);

這非常完美,但是我仍然不確定這種方法的可擴展性,性能和速度。 並且此過程中使用的IGNORE子句跳過重復記錄,但累積自動遞增鍵值。 就像它正在檢查id 3306行一樣,如果該記錄是重復記錄,則不會將其插入到表中(這是一件好事),但是會使用自動遞增鍵3307 ,並且下次插入NON-復制記錄將在3308將其插入。 這似乎不好。

滿足這種要求的任何其他方法將不勝感激。 如果我可以繼續使用該解決方案,請指導我。 如果沒有,為什么?

請記住,我正在處理大量數據。

這非常完美,但是我仍然不確定這種方法的可擴展性,性能和速度。

衡量速度,測試其縮放比例。 那你確定。 再次詢問是否找到對您造成傷害的問題,但會使性能/可伸縮性問題更加具體。 這樣的部分很可能已經進行了問答。 如果不是在此處的Stackoverflow上,而是在DBA站點上: https : //dba.stackexchange.com/

並且此過程中使用的IGNORE子句會跳過重復的記錄,但會累積自動遞增鍵值

類似地。 如果這些差距對您來說是個問題,那么這通常表明您的數據庫設計存在缺陷,因為這些差距通常是沒有意義的(比較: 如何填充自動增量字段中的“漏洞”? )。

但是,這並不意味着其他人也不會遇到這個問題。 您可以找到很多相關的材料,還“竅門”如何使用特定版本的數據庫服務器來防止這種情況。 但老實說,我不會在意差距。 合同是,標識列具有唯一值。 就這樣。

在任何情況下,無論是性能還是ID方面:為什么不將處理分開? 首先從XML導入到導入表中,然后可以輕松地從該導入表中刪除不想導入的每一行,然后可以根據需要插入目標表中。

使用下面描述的另一種邏輯解決了這個問題。

DELIMITER $$

USE `test`$$

DROP PROCEDURE IF EXISTS `import_destinations_xml`$$

CREATE DEFINER=`root`@`localhost` PROCEDURE `import_destinations_xml`(
    path VARCHAR(255), 
    node VARCHAR(255)
)

BEGIN
    DECLARE xml_content TEXT;
    DECLARE v_row_index INT UNSIGNED DEFAULT 0;   
    DECLARE v_row_count INT UNSIGNED;  
    DECLARE v_xpath_row VARCHAR(255); 

    -- set xml content.
    SET xml_content = LOAD_FILE(path);

    -- calculate the number of row elements.   
    SET v_row_count  = extractValue(xml_content, CONCAT('count(', node, ')')); 

    -- create a temporary destinations table
    DROP TABLE IF EXISTS `destinations_temp`;
    CREATE TABLE `destinations_temp` (
      `id` INT(11) NOT NULL AUTO_INCREMENT,
      `DestinationId` VARCHAR(32) DEFAULT NULL,
      `Country` VARCHAR(255) DEFAULT NULL,
      `City` VARCHAR(255) DEFAULT NULL,
      `State` VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;  

    -- loop through all the row elements    
    WHILE v_row_index < v_row_count DO                
        SET v_row_index = v_row_index + 1;        
        SET v_xpath_row = CONCAT(node, '[', v_row_index, ']');
        INSERT INTO destinations_temp VALUES (
            NULL,
            extractValue(xml_content, CONCAT(v_xpath_row, '/child::DestinationId')),
            extractValue(xml_content, CONCAT(v_xpath_row, '/child::Country')),
            extractValue(xml_content, CONCAT(v_xpath_row, '/child::City')),
            extractValue(xml_content, CONCAT(v_xpath_row, '/child::State'))
        );
    END WHILE;

    -- delete existing records from temporary destinations table
    DELETE FROM destinations_temp WHERE DestinationId IN (SELECT DestinationId FROM destinations);

    -- insert remaining (unmatched) records from temporary destinations table to destinations table
    INSERT INTO destinations (DestinationId, Country, City, State) 
    SELECT DestinationId, Country, City, State 
    FROM destinations_temp;

    -- creating a log file    
    SELECT  *
    INTO OUTFILE 'C:/Users/Muhammad Ali/Desktop/Destination_Import_Procedure/log/destinations_log.csv'
    FIELDS TERMINATED BY ','
    LINES TERMINATED BY '\r\n'
    FROM `destinations_temp`;

    -- removing temporary destinations table
    DROP TABLE destinations_temp;

END$$

DELIMITER ;

查詢以調用此過程。

CALL import_destinations_xml('C:\Users\Muhammad Ali\Desktop\Destination_Import_Procedure\dest.xml', '/Destinations/Destination');

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM