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 作为在 XML 输出模式(用 --xml 选项启动客户端)下运行mysql 客户端的补充。要将表中的数据写入XML文件,可以在系统 shell 中使用 --xml 和 -e 选项调用 mysql 客户端,如下所示:
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>
这是 mysqldump 等其他 MySQL 工具使用的格式。 这三种格式都可以在同一个 XML 文件中使用;导入程序会自动检测每一行的格式并正确解析它。根据标签或属性名和列名匹配所有的标签。 在 MySQL 8.0.21 之前,LOAD XML 不支持源 XML 中的 CDATA 部分。 以下子句对 LOAD XML 的作用与对 LOAD DATA 的作用基本相同: ● LOW_PRIORITY 或 CONCURRENT ● LOCAL ● REPLACE or IGNORE ● CHARACTER SET ● SET (field_name_or_user_var, ...) 是逗号分隔的 XML 字段或用户变量的列表。这里的用户变量名称必须与XML文件中前缀为 @ 的字段名称匹配。可以使用字段名称仅选择所需的字段。可以使用用户变量来存储相应的字段值,以便以后重用。 IGNORE number LINES 或 IGNORE 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 进行读取。 与导入操作相反的是,可以从系统 shell 使用 mysql 客户端将 MySQL 表数据转储到XML文件中,如下所示:
shell> mysql --xml -e "SELECT * FROM test.person" > person-dump.xml
shell> 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>
注意 --xml 选项使 mysql 客户端对其输出使用 xml 格式;-e 选项使客户端执行在其后设置的 SQL 语句。 可以通过创建 person 表的副本并将转储文件导入新表来验证转储是否有效,如下所示:
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(11) NOT NULL,
`fname` varchar(40) DEFAULT NULL,
`lname` varchar(40) DEFAULT NULL,
PRIMARY KEY (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
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 的操作;字段顺序可能会因行而异,不需要与表中相应列的顺序相同。 如前所述,可以使用一个或多个 XML 字段(仅用于选择所需的字段)或用户变量(存储相应的字段值以供以后使用)的(field_name_or_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=MyISAM DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
现在使用以下 CREATE TABLE 语句在 test 数据库中创建一个 address表:
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)
不导入 <address> 元素中包含在 XML 注释中的数据。但是,由于address 表中有一个 person_id 列,因此每个 <address> 的父 <person> 元素的 person_id 属性值被导入到 address 表中。 安全考虑。与 LOAD DATA 语句一样,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 文件,请不要将 FILE 权限授予相应的MySQL用户,或者如果客户端用户帐户已经拥有该权限,则取消该权限。 重要 撤销 FILE 权限(或者首先不授予它)只会使用户无法执行 LOAD XML 语句(以及 LOAD_FILE() 函数);它不会阻止用户执行 LOAD XML LOCAL。若要禁止此语句,启动服务器或客户端时必须使用 --local-infile=OFF。 换句话说,FILE 权限只影响客户端是否可以读取服务器上的文件;它与客户端是否可以读取本地文件系统上的文件没有关系。