Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  ...  /  LOAD XML Statement

15.2.10 加载 XML 语句

LOAD XML
    [LOW_PRIORITY | CONCURRENT] [LOCAL]
    INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE [db_name.]tbl_name
    [CHARACTER SET charset_name]
    [ROWS IDENTIFIED BY '<tagname>']
    [IGNORE number {LINES | ROWS}]
    [(field_name_or_user_var
        [, field_name_or_user_var] ...)]
    [SET col_name={expr | DEFAULT}
        [, col_name={expr | DEFAULT}] ...]

LOAD XML 语句从 XML 文件中读取数据到表中。必须将file_name指定为文字字符串。可选的ROWS IDENTIFIED BY子句中的tagname也必须指定为文字字符串,并且必须被角括号(<>)包围。

LOAD XML 语句是 mysql 客户端在 XML 输出模式下运行的补充(即使用--xml选项启动客户端)。要将表中的数据写入到 XML 文件中,可以使用 mysql 客户端,带有--xml-e选项,从系统 shell 中执行,如下所示:

$> mysql --xml -e 'SELECT * FROM mydb.mytable' > file.xml

要将文件读回表中,请使用 LOAD XML 语句。默认情况下,<row>元素被认为是数据库表行的等价物;这可以通过ROWS IDENTIFIED BY子句进行更改。

该语句支持三个不同的XML格式:

  • 将列名作为属性,将列值作为属性值:

    <row column1="value1" column2="value2" .../>
  • 将列名作为标签,将列值作为这些标签的内容:

    <row>
      <column1>value1</column1>
      <column2>value2</column2>
    </row>
  • 将列名作为<field>标签的name属性,列值为这些标签的内容:

    <row>
      <field name='column1'>value1</field>
      <field name='column2'>value2</field>
    </row>

    这是其他MySQL工具,如mysqldump使用的格式。

所有三个格式都可以在同一个XML文件中使用;导入程序自动检测每行的格式并正确解释。标签是根据标签或属性名称和列名进行匹配的。

以下子句对LOAD XMLLOAD DATA的工作方式相同:

  • LOW_ PRIORITYCONCURRENT

  • LOCAL

  • REPLACEIGNORE

  • CHARACTER SET

  • SET

请参阅第15.2.9节,“LOAD DATA Statement”,了解这些子句的更多信息。

(《field_名或用户变量,…)是一个由一个或多个逗号分隔的XML字段或用户变量组成的列表。用于此目的的用户变量名称必须与XML文件中的字段名称匹配,并且以@开头。你可以使用字段名来选择仅需的字段。用户变量可以用来存储对应字段值,以便后续重复使用。

IGNORE 《number》 LINESIGNORE 《number》 ROWS子句会跳过XML文件中的前《number》行。它类似于LOAD DATA语句的IGNORE … LINES子句。

假设我们有一个名为《person》的表,创建方法如下:

USE test;

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

假设这个表当前是空的。

现在假设我们有一个简单的XML文件《person.xml》,其内容如下:

<list>
  <person person_id="1" fname="Kapek" lname="Sainnouine"/>
  <person person_id="2" fname="Sajon" lname="Rondela"/>
  <person person_id="3"><fname>Likame</fname><lname>Örrtmons</lname></person>
  <person person_id="4"><fname>Slar</fname><lname>Manlanth</lname></person>
  <person><field name="person_id">5</field><field name="fname">Stoma</field>
    <field name="lname">Milu</field></person>
  <person><field name="person_id">6</field><field name="fname">Nirtam</field>
    <field name="lname">Sklöd</field></person>
  <person person_id="7"><fname>Sungam</fname><lname>Dulbåd</lname></person>
  <person person_id="8" fname="Sraref" lname="Encmelt"/>
</list>

在这个示例文件中,每种可接受的XML格式都被讨论过了。

要将《person.xml》中的数据导入到《person》表中,可以使用以下语句:

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

Query OK, 8 rows affected (0.00 sec)
Records: 8  Deleted: 0  Skipped: 0  Warnings: 0

在这里,我们假设《person.xml》位于MySQL数据目录。如果文件找不到,会出现以下错误结果:

ERROR 2 (HY000): File '/person.xml' not found (Errcode: 2)

"ROWS IDENTIFIED BY '<person>'" 子句意味着 XML 文件中的每个 "<person>" 元素都被视为等同于要将数据导入到的表中的一行。在这种情况下,这是 "test" 数据库中的 "person" 表。

从服务器的响应可以看出,有 8 行被导入到 "test.person" 表中。这可以通过一个简单的 SELECT 语句进行验证:

mysql> SELECT * FROM person;
+-----------+--------+------------+---------------------+
| person_id | fname  | lname      | created             |
+-----------+--------+------------+---------------------+
|         1 | Kapek  | Sainnouine | 2007-07-13 16:18:47 |
|         2 | Sajon  | Rondela    | 2007-07-13 16:18:47 |
|         3 | Likame | Örrtmons   | 2007-07-13 16:18:47 |
|         4 | Slar   | Manlanth   | 2007-07-13 16:18:47 |
|         5 | Stoma  | Nilu       | 2007-07-13 16:18:47 |
|         6 | Nirtam | Sklöd      | 2007-07-13 16:18:47 |
|         7 | Sungam | Dulbåd     | 2007-07-13 16:18:47 |
|         8 | Sreraf | Encmelt    | 2007-07-13 16:18:47 |
+-----------+--------+------------+---------------------+
8 rows in set (0.00 sec)

如本节前面所述,这表明 3 种允许的 XML 格式中的任何一种或所有格式都可能出现在单个文件中,并可以使用 LOAD XML 读取。

刚才展示的导入操作的反操作,即将 MySQL 表数据转储到 XML 文件中,可以使用系统 shell 中的 mysql 客户端完成,如下所示:

$> mysql --xml -e "SELECT * FROM test.person" > person-dump.xml
$> cat person-dump.xml
<?xml version="1.0"?>

<resultset statement="SELECT * FROM test.person" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <row>
	<field name="person_id">1</field>
	<field name="fname">Kapek</field>
	<field name="lname">Sainnouine</field>
  </row>

  <row>
	<field name="person_id">2</field>
	<field name="fname">Sajon</field>
	<field name="lname">Rondela</field>
  </row>

  <row>
	<field name="person_id">3</field>
	<field name="fname">Likema</field>
	<field name="lname">Örrtmons</field>
  </row>

  <row>
	<field name="person_id">4</field>
	<field name="fname">Slar</field>
	<field name="lname">Manlanth</field>
  </row>

  <row>
	<field name="person_id">5</field>
	<field name="fname">Stoma</field>
	<field name="lname">Nilu</field>
  </row>

  <row>
	<field name="person_id">6</field>
	<field name="fname">Nirtam</field>
	<field name="lname">Sklöd</field>
  </row>

  <row>
	<field name="person_id">7</field>
	<field name="fname">Sungam</field>
	<field name="lname">Dulbåd</field>
  </row>

  <row>
	<field name="person_id">8</field>
	<field name="fname">Sreraf</field>
	<field name="lname">Encmelt</field>
  </row>
</resultset>
Note

The --xml option causes the mysql client to use XML formatting for its output; the -e option causes the client to execute the SQL statement immediately following the option. See Section 6.5.1, “mysql — The MySQL Command-Line Client”.

您可以通过创建 person 表的副本,并将dump文件导入到新表中,来验证 dump 是否有效,如下所示:

mysql> USE test;
mysql> CREATE TABLE person2 LIKE person;
Query OK, 0 rows affected (0.00 sec)

mysql> LOAD XML LOCAL INFILE 'person-dump.xml'
    ->   INTO TABLE person2;
Query OK, 8 rows affected (0.01 sec)
Records: 8  Deleted: 0  Skipped: 0  Warnings: 0

mysql> SELECT * FROM person2;
+-----------+--------+------------+---------------------+
| person_id | fname  | lname      | created             |
+-----------+--------+------------+---------------------+
|         1 | Kapek  | Sainnouine | 2007-07-13 16:18:47 |
|         2 | Sajon  | Rondela    | 2007-07-13 16:18:47 |
|         3 | Likema | Örrtmons   | 2007-07-13 16:18:47 |
|         4 | Slar   | Manlanth   | 2007-07-13 16:18:47 |
|         5 | Stoma  | Nilu       | 2007-07-13 16:18:47 |
|         6 | Nirtam | Sklöd      | 2007-07-13 16:18:47 |
|         7 | Sungam | Dulbåd     | 2007-07-13 16:18:47 |
|         8 | Sreraf | Encmelt    | 2007-07-13 16:18:47 |
+-----------+--------+------------+---------------------+
8 rows in set (0.00 sec)

XML 文件中的每个字段不需要与对应表中的列相匹配。没有相应列的字段将被忽略。你可以通过首先清空 person2 表,然后删除 created 列,接着使用相同的LOAD XML 语句,如下所示:

mysql> TRUNCATE person2;
Query OK, 8 rows affected (0.26 sec)

mysql> ALTER TABLE person2 DROP COLUMN created;
Query OK, 0 rows affected (0.52 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHOW CREATE TABLE person2\G
*************************** 1. row ***************************
       Table: person2
Create Table: CREATE TABLE `person2` (
  `person_id` int NOT NULL,
  `fname` varchar(40) DEFAULT NULL,
  `lname` varchar(40) DEFAULT NULL,
  PRIMARY KEY (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql> LOAD XML LOCAL INFILE 'person-dump.xml'
    ->   INTO TABLE person2;
Query OK, 8 rows affected (0.01 sec)
Records: 8  Deleted: 0  Skipped: 0  Warnings: 0

mysql> SELECT * FROM person2;
+-----------+--------+------------+
| person_id | fname  | lname      |
+-----------+--------+------------+
|         1 | Kapek  | Sainnouine |
|         2 | Sajon  | Rondela    |
|         3 | Likema | Örrtmons   |
|         4 | Slar   | Manlanth   |
|         5 | Stoma  | Nilu       |
|         6 | Nirtam | Sklöd      |
|         7 | Sungam | Dulbåd     |
|         8 | Sreraf | Encmelt    |
+-----------+--------+------------+
8 rows in set (0.00 sec)

XML 文件中的每行字段顺序对 LOAD XML 的操作没有影响;字段顺序可以在每行之间变化,并且不需要与对应表中的列顺序相同。

如前所述,您可以使用一个 (field_名或_user_var, ...) 列表,其中包含一个或多个 XML 字段(以选择要加载的字段)或用户变量(以存储对应字段值以便后续使用)。用户变量可以在您想要将 XML 文件中的数据插入到列名不匹配的表列时特别有用。为了了解如何工作,我们首先创建一个名为 individual 的表,该表结构与 person 表相同,但列名不同:

mysql> CREATE TABLE individual (
    ->     individual_id INT NOT NULL PRIMARY KEY,
    ->     name1 VARCHAR(40) NULL,
    ->     name2 VARCHAR(40) NULL,
    ->     made TIMESTAMP
    -> );
Query OK, 0 rows affected (0.42 sec)

在这种情况下,您不能简单地将 XML 文件直接加载到表中,因为字段和列名不匹配:

mysql> LOAD XML INFILE '../bin/person-dump.xml' INTO TABLE test.individual;
ERROR 1263 (22004): Column set to default value; NULL supplied to NOT NULL column 'individual_id' at row 1

这是因为 MySQL 服务器寻找与目标表的列名匹配的字段名称。你可以通过将字段值选择到用户变量,然后使用 SET 将目标表的列设置为这些变量的值,可以在单个语句中执行,如下所示:

mysql> LOAD XML INFILE '../bin/person-dump.xml'
    ->     INTO TABLE test.individual (@person_id, @fname, @lname, @created)
    ->     SET individual_id=@person_id, name1=@fname, name2=@lname, made=@created;
Query OK, 8 rows affected (0.05 sec)
Records: 8  Deleted: 0  Skipped: 0  Warnings: 0

mysql> SELECT * FROM individual;
+---------------+--------+------------+---------------------+
| individual_id | name1  | name2      | made                |
+---------------+--------+------------+---------------------+
|             1 | Kapek  | Sainnouine | 2007-07-13 16:18:47 |
|             2 | Sajon  | Rondela    | 2007-07-13 16:18:47 |
|             3 | Likema | Örrtmons   | 2007-07-13 16:18:47 |
|             4 | Slar   | Manlanth   | 2007-07-13 16:18:47 |
|             5 | Stoma  | Nilu       | 2007-07-13 16:18:47 |
|             6 | Nirtam | Sklöd      | 2007-07-13 16:18:47 |
|             7 | Sungam | Dulbåd     | 2007-07-13 16:18:47 |
|             8 | Srraf  | Encmelt    | 2007-07-13 16:18:47 |
+---------------+--------+------------+---------------------+
8 rows in set (0.00 sec)

用户变量的名称必须与 XML 文件中的相应字段名称匹配,添加必要的 @ 前缀以指示它们是变量。用户变量不需要按顺序列出或分配与相应字段相同。

使用 ROWS IDENTIFIED BY '<tagname>' 子句,可以将同一个 XML 文件中的数据导入到具有不同定义的数据库表中。例如,假设您有一个名为 address.xml 的文件,它包含以下 XML:

<?xml version="1.0"?>

<list>
  <person person_id="1">
    <fname>Robert</fname>
    <lname>Jones</lname>
    <address address_id="1" street="Mill Creek Road" zip="45365" city="Sidney"/>
    <address address_id="2" street="Main Street" zip="28681" city="Taylorsville"/>
  </person>

  <person person_id="2">
    <fname>Mary</fname>
    <lname>Smith</lname>
    <address address_id="3" street="River Road" zip="80239" city="Denver"/>
    <!-- <address address_id="4" street="North Street" zip="37920" city="Knoxville"/> -->
  </person>

</list>

你可以再次使用之前在本节中定义的 test.person 表,清除表中的所有记录,然后显示其结构,如下所示:

mysql< TRUNCATE person;
Query OK, 0 rows affected (0.04 sec)

mysql< SHOW CREATE TABLE person\G
*************************** 1. row ***************************
       Table: person
Create Table: CREATE TABLE `person` (
  `person_id` int(11) NOT NULL,
  `fname` varchar(40) DEFAULT NULL,
  `lname` varchar(40) DEFAULT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

现在,在 test 数据库中创建一个名为 address 的表,使用以下 CREATE TABLE 语句:

CREATE TABLE address (
    address_id INT NOT NULL PRIMARY KEY,
    person_id INT NULL,
    street VARCHAR(40) NULL,
    zip INT NULL,
    city VARCHAR(40) NULL,
    created TIMESTAMP
);

将 XML 文件中的数据导入到 person 表中,执行以下 LOAD XML 语句,它指定行将由 <person> 元素指定,如下所示:

mysql> LOAD XML LOCAL INFILE 'address.xml'
    ->   INTO TABLE person
    ->   ROWS IDENTIFIED BY '<person>';
Query OK, 2 rows affected (0.00 sec)
Records: 2  Deleted: 0  Skipped: 0  Warnings: 0

您可以使用SELECT语句验证记录是否已经导入:

mysql> SELECT * FROM person;
+-----------+--------+-------+---------------------+
| person_id | fname  | lname | created             |
+-----------+--------+-------+---------------------+
|         1 | Robert | Jones | 2007-07-24 17:37:06 |
|         2 | Mary   | Smith | 2007-07-24 17:37:06 |
+-----------+--------+-------+---------------------+
2 rows in set (0.00 sec)

由于 XML 文件中的<address>元素没有对应的列在person表中,因此它们被跳过。

要将<address>元素中的数据导入到address表中,请使用以下LOAD XML语句:

mysql> LOAD XML LOCAL INFILE 'address.xml'
    ->   INTO TABLE address
    ->   ROWS IDENTIFIED BY '<address>';
Query OK, 3 rows affected (0.00 sec)
Records: 3  Deleted: 0  Skipped: 0  Warnings: 0

您可以看到数据已经使用SELECT语句导入,例如:

mysql> SELECT * FROM address;
+------------+-----------+-----------------+-------+--------------+---------------------+
| address_id | person_id | street          | zip   | city         | created             |
+------------+-----------+-----------------+-------+--------------+---------------------+
|          1 |         1 | Mill Creek Road | 45365 | Sidney       | 2007-07-24 17:37:37 |
|          2 |         1 | Main Street     | 28681 | Taylorsville | 2007-07-24 17:37:37 |
|          3 |         2 | River Road      | 80239 | Denver       | 2007-07-24 17:37:37 |
+------------+-----------+-----------------+-------+--------------+---------------------+
3 rows in set (0.00 sec)

XML 注释中包含的<address>元素中的数据不被导入。然而,因为address表中存在person_id列,所以每个<address>元素的父<person>元素中的person_id属性值将被导入到address表中。

安全考虑. LOAD DATA 语句类似,MySQL 服务器在客户端主机和服务器主机之间传输 XML 文件的过程由 MySQL 服务器控制。在理论上,可以构建一个被修补过的服务器,使得客户程序将服务器选择的文件,而不是客户在LOAD XML 语句中指定的文件传输。这样一个服务器可以访问客户主机上的任何文件,以便读取客户用户对该文件具有读权限的文件。

在 Web 环境中,通常情况下,客户端从 Web 服务器连接到 MySQL。能够对 MySQL 服务器执行任意命令的用户可以使用LOAD XML LOCAL 读取 Web 服务器进程具有读权限的任何文件。在这个环境中,客户端对 MySQL 服务器实际上是 Web 服务器,而不是远程程序被用户连接到 Web 服务器运行的程序。

您可以通过使用--local-infile=0--local-infile=OFF 选项来禁用客户端加载 XML 文件。这个选项也可以在启动mysql 客户端时使用,以禁用LOAD XML 语句的执行期限内。

为了防止客户端从服务器加载XML文件,不授予对应的MySQL用户账户FILE权限,或者如果客户端用户账户已经拥有该权限,则撤销该权限。

Important

撤销FILE权限(或不授予该权限)将使得用户只能执行LOAD XML语句(同时也包括LOAD_FILE()函数),但它防止用户执行LOAD XML LOCAL语句。要禁止该语句,您必须使用--local-infile=OFF启动服务器或客户端。

换言之,FILE权限只影响客户端是否可以在服务器上读取文件,而对客户端是否可以读取本地文件系统没有任何影响。