MySQL文档有这个例子:
mysql> DELIMITER |
mysql> CREATE PROCEDURE myproc ()
-> BEGIN
-> DECLARE i INT DEFAULT 1;
-> DECLARE xml VARCHAR(25) DEFAULT '<a>X</a><a>Y</a><a>Z</a>';
->
-> WHILE i < 4 DO
-> SELECT xml, i, ExtractValue(xml, '//a[$i]');
-> SET i = i+1;
-> END WHILE;
-> END |
有没有办法在不对元素数量进行硬编码的情况下完成此操作(本例中的值4
)?
更新 我正在寻找一般的解决方案。不要被问题中显示的特定XML分散注意力,它只是MySQL手册页中的一个例子。
答案 0 :(得分:1)
如果您尝试批量处理具有丰富的非关系结构的大型XML文件,那么我说在MySQL处理它时遇到的唯一挑战是处理1- N个关系。
显然,我发现处理2D行字段结构化XML的例子完全不相关,就像你应该能够处理原始CSV文件一样有效(并且任何一年级学生都可以编写一个XSL样式表来进行转换)。相反,我对XML感兴趣,例如:
<batch>
<tx id="12483" timestamp="2016-04-23 14:40:42"
uuid="5395db0c-0984-11e6-b4b6-e0f427e40467">
<shop id="78" location="238 Park Ave, NYC"/>
<items>
<book isbn="9780062409867"
title="Go Set a Watchman"
author="Lee, Harper"
unitprice="11.52"
quantity="1"/>
<book isbn="9781593080259"
title="The Picture of Dorian Gray"
author="Wilde, Oscar"
unitprice="7.99"
quantity="1"/>
<book isbn="9780143039020"
title="The Quiet American"
author="Greene, Graham"
unitprice="5.56"
quantity="1"/>
<book isbn="9780307277770"
title="The Painted Veil"
author="Maugham, W. Somerset"
unitprice="7.05"
quantity="1"/>
<bluray asin="B017S3OP34"
title="The Martian"
year="2015"
discs="1"
language="English"
unitprice="17.93"
quantity="1"/>
</items>
</tx>
</batch>
用例如。异构集合(book和bluray),所有这些集合都可以根据特定的业务逻辑映射到关系数据库中。
现在回到你的问题。可以使用XPath检索XML中任何1-N关系的行数,即。 extractValue('..', 'count(path/to/element)')
然后通过构造特定于索引的XPath查询来迭代这个(唉,非常有必要)。例如,您可以在存储过程中处理上述内容,例如
delimiter ;;
DROP PROCEDURE IF EXISTS `import_xml`;;
CREATE PROCEDURE import_xml(xml_input MEDIUMTEXT)
BEGIN
DECLARE k INT UNSIGNED DEFAULT 0;
DECLARE row_count INT UNSIGNED;
DECLARE xpath TEXT;
DROP TEMPORARY TABLE IF EXISTS `books`;
CREATE TEMPORARY TABLE books (tx_id BIGINT, isbn BIGINT, title VARCHAR(255), author VARCHAR(63), unit_price DECIMAL(6,2), quantity TINYINT(4));
-- process books
-- book count in the collection
SET row_count := extractValue(xml_input,'count(//items/book)');
-- iterate over books
WHILE k < row_count DO
SET k := k + 1;
SET xpath := concat('//items/book[', k, ']');
INSERT INTO books VALUES (
extractValue(xml_input, '//tx/@id'),
extractValue(xml_input, concat(xpath,'/@isbn')),
extractValue(xml_input, concat(xpath,'/@title')),
extractValue(xml_input, concat(xpath,'/@author')),
extractValue(xml_input, concat(xpath,'/@unitprice')),
extractValue(xml_input, concat(xpath,'/@quantity'))
);
END WHILE;
-- all books are in the temporary table `books` ready for further processing
SELECT * FROM books;
END;
;;
delimiter ;
CALL import_xml(@xml);
话虽如此,在开始将大型XML解析为MySQL之前,您可能需要花一点时间来编写一个长存储过程与编写一个时髦的XSLT样式表来对比,该样式表将XML整齐地转换为一批SQL语句包含在您可能很容易加载到MySQL中的事务中。我说两种方法都值得你花时间。
或许:您直接从MySQL运行的XML + XSLT?
HTH
答案 1 :(得分:1)
如果您有一个简单的 XML 结构,那么您可以使用递归 CTE 在单个查询中提取详细信息。以下仅在 MySQL 8 上测试过,所以我不能保证它是否适用于早期版本。
SET @xml =
'<objects>
<object>
<objectid>101</objectid>
<objecttype>T1</objecttype>
</object>
<object>
<objectid>102</objectid>
<objecttype>T2</objecttype>
</object>
<object>
<objectid>103</objectid>
<objecttype>T3</objecttype>
</object>
</objects>';
WITH RECURSIVE RowGenerator (SeqNum)
AS (
SELECT 1
UNION ALL
SELECT SeqNum + 1
FROM RowGenerator
WHERE SeqNum < ExtractValue(@xml, 'count(/objects/object)')
)
SELECT @RowNum := SeqNum
, ExtractValue(@xml, '/objects/object[$@RowNum]/objectid')
, ExtractValue(@xml, '/objects/object[$@RowNum]/objecttype')
FROM RowGenerator;
答案 2 :(得分:0)
<强>不强>
SQL没有XML解析功能。
将数据作为字符串传递给具有XML解析功能的环境。
如果您尝试根据XML数据过滤查询,那么答案是您不应该首先在数据库中存储XML并且现在遇到很多麻烦(查找规范化)。
答案 3 :(得分:0)
感谢@DaveB 提到 count()
函数,我现在可以这样写了:
mysql> DELIMITER |
mysql> CREATE PROCEDURE myproc ()
-> BEGIN
-> DECLARE i INT DEFAULT 1;
-> DECLARE xml VARCHAR(25) DEFAULT '<a>X</a><a>Y</a><a>Z</a>';
-> SELECT @count := ExtractValue(@xml, 'count(//a)');
->
-> WHILE i < @count + 1 DO
-> SELECT xml, i, ExtractValue(xml, '//a[$i]');
-> SET i = i+1;
-> END WHILE;
-> END |