【问题标题】:LOAD XML LOCAL INFILE with Inconsistent Column Names使用不一致的列名加载 XML LOCAL INFILE
【发布时间】:2011-12-20 22:32:10
【问题描述】:

MySQL 有一个很好的语句:LOAD XML LOCAL INFILE

例如,如果你有这个表:

 CREATE TABLE person (
    person_id INT NOT NULL PRIMARY KEY,
    fname VARCHAR(40) NULL,
    lname VARCHAR(40) NULL
 );

以及名为 person.xml 的以下 XML 文件:

<list>
      <person>
          <person_id>1</person_id>
          <fname>Mikael</fname>
          <lname>Ronström</lname>
      </person>
      <person>
          <person_id>2</person_id>
          <fname>Lars</fname>
          <lname>Thalmann</lname>
      </person>
</list>

你可以这样做:

LOAD XML LOCAL INFILE 'person.xml'
INTO TABLE person
ROWS IDENTIFIED BY '<person>';

我的问题是,如果 XML 文件中的列名与表中的不同,该怎么办?例如:

<list>
      <person>
          <PersonId>1</PersonId>
          <FirstName>Mikael</FirstName>
          <LastName>Ronström</LastName>
      </person>
      <person>
          <PersonId>2</PersonId>
          <FirstName>Lars</FirstName>
          <LastName>Thalmann</LastName>
      </person>
</list>

如何在不操作 XML 文件的情况下使用 MySQL 语句完成相同的操作?我到处搜索,但找不到答案。

【问题讨论】:

    标签: mysql xml


    【解决方案1】:

    XML 文件中与物理列名不对应的字段将被忽略。并且在 XML 中没有对应字段的表中的列被设置为 NULL。

    我要做的是按照@Kolink 的建议加载到临时表中,但要添加额外的列。在从 XML 加载数据时添加 SET 子句。

    CREATE TEMP TABLE person_xml LIKE person;
    
    ALTER TABLE person_xml 
      ADD COLUMN FirstName VARCHAR(40),
      ADD COLUMN LastName  VARCHAR(40),
      ADD COLUMN PersonId  INT;
    
    LOAD XML LOCAL INFILE 'person.xml' INTO TABLE person_xml
      SET person_id = PersonId, fname = FirstName, lname = LastName;
    
    SELECT * FROM person_xml;
    +-----------+--------+-------------+-----------+-------------+----------+
    | person_id | fname  | lname       | FirstName | LastName    | PersonId |
    +-----------+--------+-------------+-----------+-------------+----------+
    |         1 | Mikael | Ronström    | Mikael    | Ronström    |        1 |
    |         2 | Lars   | Thalmann    | Lars      | Thalmann    |        2 |
    +-----------+--------+-------------+-----------+-------------+----------+
    

    然后复制到真实表,选择列的子集。

    INSERT INTO person SELECT person_id, fname, lname FROM person_xml;
    

    或者,删除多余的列并使用SELECT *

    ALTER TABLE person_xml 
      DROP COLUMN PersonId, 
      DROP COLUMN FirstName, 
      DROP COLUMN LastName;
    
    INSERT INTO person SELECT * FROM person_xml;
    
    SELECT * FROM person;
    
    +-----------+--------+-------------+
    | person_id | fname  | lname       |
    +-----------+--------+-------------+
    |         1 | Mikael | Ronström    |
    |         2 | Lars   | Thalmann    |
    +-----------+--------+-------------+
    

    【讨论】:

    • 谢谢。一个新的问题出现了。显然,此语句不喜欢 形式的空元素。它需要单独的打开和关闭标签。我不确定是否有办法解决这个问题。
    • 这些是我自己不生成的巨大文件,它们必须每 15 分钟加载到 MySQL 中。操纵它们效率太低了。否则,我会跳过这整个语句,并通过 Java 使用 SAX 来解析它们并将数据插入 MySQL。
    • 如你所愿。我只是说我会使用 XSLT。它比任何编码语言(如过程语言)更快地开发和执行针对 XML 的转换。
    • 我想我可能会接受您的建议,首先使用 XSLT 转换文件,然后在转换后的文件上使用 LOAD XML 语句。
    • 再次,我面临的问题是这些 XML 文件太大而无法加载到内存中。 XSLT 转换通常首先将 XML 加载到内存中。
    【解决方案2】:

    mysql 表架构:organization_type(id, name)

    组织类型.xml:

    <NewDataSet>
        <row>
          <ItemID>1</ItemID>
          <ItemCreatedBy>53</ItemCreatedBy>
          <ItemCreatedWhen>2014-03-10T22:53:43.947+10:00</ItemCreatedWhen>
          <ItemModifiedBy>53</ItemModifiedBy>
          <ItemModifiedWhen>2014-03-10T22:53:43.99+10:00</ItemModifiedWhen>
          <ItemOrder>1</ItemOrder>
          <ItemGUID>e2ad051f-b7ea-4feb-b91e-f558f6f632a0</ItemGUID>
          <Name>Company Type 1</Name>
        </row>
    

    mysql 导入查询将如下所示:

    LOAD XML INFILE '/var/lib/mysql-files/organizationtype.xml'
    INTO TABLE organization_type (@ItemID, @Name) 
    SET id=@ItemID, name=@Name
    

    【讨论】:

      【解决方案3】:

      以下是我可以使用的选项:

      选项 1:创建一个具有不同字段名称的临时表(如其他答案所建议的那样)。这将是一个令人满意的方法。然而,当我尝试它时,出现了一个新问题:LOAD XML 语句由于某种原因不接受最小化格式的空元素(例如&lt;person /&gt;)。因此,该语句失败,因为我需要加载的 XML 文件有时具有该格式的空元素。

      选项 2:在运行 LOAD XML 语句之前使用 XSLT 转换 XML 文件以更改元素名称并修改空元素格式。这是不可行的,因为 XML 文件非常大,并且 XSLT 处理引擎在处理之前将整个 XML 加载到内存中。

      选项 3:完全绕过 LOAD XML 语句并使用 SAX 解析器来解析 XML 文件,并使用 JDBC 和预准备语句将记录直接插入数据库。尽管原始 JDBC 和准备好的语句通常很有效,但事实证明这太慢了。比 LOAD XML 语句慢得多。

      选项 4:使用 LOAD DATA 语句而不是 LOAD XML 语句,并使用与该语句关联的可选子句以满足我的需要(例如,由分隔的行等)。这本来可以奏效,但容易出错且不稳定。

      选项 5:使用快速只进解析器解析文件并同时读/写 XML 元素,并生成一个新的 XML 文件,该文件具有 LOAD XML 语句所需格式的修改名称。

      我最终使用了选项 5。我使用 Java Streaming API for XML (StAX) 来读取 XML 文件和生成修改后的 XML 文件,然后从 Web 应用程序内部通过 JDBC 运行 LOAD XML LOCAL INFILE。它工作得很好,而且速度非常快。

      【讨论】:

        【解决方案4】:

        使用旧的 LOAD DATA INFILE 有点老套但可行的解决方案:

        LOAD DATA LOCAL INFILE '/tmp/xml/loaded.xml'
        INTO TABLE person
        CHARACTER SET binary
        LINES STARTING BY '<person>' TERMINATED BY '</person>'
        (@person)
        SET
          person_id = ExtractValue(@person:=CONVERT(@person using utf8), 'PersonId'),
          fname = ExtractValue(@person, 'FirstName'),
          lname = ExtractValue(@person, 'LastName')
        ;
        

        附:如果数据包含逗号,您可能需要额外使用字段分隔符。

        【讨论】:

        • 这可能容易出错。太hacky了。不过感谢您的回答。
        • 谢谢。与LOAD XML 相比,速度优势是一个数量级
        • 这比我在本地测试数据库上的 3+gb 文件上的 LOAD XML 慢。它在 109 分钟后也失去了连接。 (mysql 8.0.18)
        【解决方案5】:

        您可以使用 XML 文件中的列名创建一个临时表(尽管这必须在 create temporary table 查询中手动完成),将 XML 文件加载到该表中,然后将 insert into person select * from tmp_table_name 加载。

        【讨论】:

        • 这是一个很好的解决方法。但是,正如我对 Bill Karwin 的回答所评论的那样,LOAD XML 语句还有另一个问题。它不接受最小化的空标签,例如 。你能想出解决这个问题的办法吗?
        • 我从未与LOAD XML 合作过,所以我只是对这个答案进行了有根据的猜测。所以不,不幸的是我不知道这个问题的解决方案,除了用另一种语言(例如 PHP)加载 XML 文件,解析它,然后将结果查询发送到数据库。
        • 仅供参考,加载 标记失败是一个错误,现已修复:在 MySQL 5.5.46 之前,LOAD XML 不处理 正确。 (错误 #67542、错误 #16171518)。来自here
        猜你喜欢
        • 2012-06-20
        • 2013-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-12
        • 1970-01-01
        • 2015-05-16
        相关资源
        最近更新 更多