如何在MySQL存储过程中循环遍历XML元素?

时间:2016-03-22 22:06:57

标签: mysql xml xpath xml-parsing

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手册页中的一个例子。

4 个答案:

答案 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 |