具有动态命名空间的 Oracle 提取值

Posted

技术标签:

【中文标题】具有动态命名空间的 Oracle 提取值【英文标题】:Oracle extractvalue with dynamic namespace 【发布时间】:2015-05-28 14:36:40 【问题描述】:

我有一个 XMLTYPE 列,其中包含类似的 XML 结构但具有不同的命名空间。我正在尝试通过使用带有 extractValue 运算符的动态命名空间字符串从不同行的 XML 中提取一些值,但到目前为止我无法使其工作。

更明确地说,这是我要运行的查询:

SELECT extractValue(X.XML, '/ns1:a', 'xmlns:ns1="'||XSD.NAMESPACE||'"')
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID

这里是创建表和数据的 SQL:

CREATE TABLE XSD_TEST (NAMESPACE VARCHAR2(1024), ID NUMBER(19) PRIMARY KEY);
CREATE TABLE XML_TEST(XML XMLTYPE, XSD_ID NUMBER(19));
ALTER TABLE XML_TEST ADD CONSTRAINT FK_XSD_ID FOREIGN KEY(XSD_ID) REFERENCES XSD_TEST(ID);
INSERT INTO XSD_TEST (NAMESPACE, ID) VALUES ('http://my.test/v1', 1);
INSERT INTO XSD_TEST (NAMESPACE, ID) VALUES ('http://my.test/v2', 2);
INSERT INTO XML_TEST (XML, XSD_ID) VALUES (XMLTYPE('<?xml version="1.0" encoding="UTF-8" standalone="no"?><v1:a xmlns:v1="http://my.test/v1">TEST1</v1:a>'), 1);
INSERT INTO XML_TEST (XML, XSD_ID) VALUES (XMLTYPE('<?xml version="1.0" encoding="UTF-8" standalone="no"?><v2:a xmlns:v2="http://my.test/v2">TEST2</v2:a>'), 2);

如果我跑步:

SELECT extractValue(X.XML, '/ns1:a', 'xmlns:ns1="'||XSD.NAMESPACE||'"')
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID
WHERE X.XSD_ID = 1

它正确地返回 TEST1。

如果我跑步:

SELECT extractValue(X.XML, '/ns1:a', 'xmlns:ns1="'||XSD.NAMESPACE||'"')
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID
WHERE X.XSD_ID = 2

它正确返回 TEST2。

但是如果我运行:

SELECT extractValue(X.XML, '/ns1:a', 'xmlns:ns1="'||XSD.NAMESPACE||'"')
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID;

它返回 TEST1 和 (null)

谁能告诉我为什么我会得到这样的结果以及我如何才能真正得到“正确”的结果:TEST1 和 TEST2?

【问题讨论】:

我从您的最终查询中得到 TEST1 和 TEST2。在 11gR2 (11.2.0.3) 中;但在 10gR2 (10.2.0.5) 中 TEST1 和 null。您在哪个版本中获得 null? @AlexPoole 我使用 Oracle 12 那令人惊讶;除了 extractValue 当然不推荐使用。 XMLQuery 和 XMLTable 版本在 11gR2 中对我有用,那么在 12c 中它们可能对你有用吗?我没有 12c 实例来测试它们。 @AlexPoole 谢谢,很高兴知道。您是否知道existsNodeextractValue 的等价物(或多或少),或者有一个链接可以为我指明正确的方向? 你可以用XMLQuery替换extractValue;并以XMLExists 存在节点。不是直接替换,您需要找出条件的 XQuery 版本。 【参考方案1】:

从 cmets 开始,extractValue() 已被弃用,这或许可以解释为什么它在 12c 中的行为与在 10g 中的行为相同,尽管问题中的代码在 11g 中有效。在 12c 中使用 XMLQuery 或 XMLTable 可能会更快乐;但我没有 12c 实例来测试这些,所以这些 11gR2 观察中的一些也可能不成立,尽管我希望大多数人会。

使用 XMLQuery,您不能通过串联嵌入命名空间路径:

SELECT XMLQuery('declare namespace ns1="'||XSD.NAMESPACE||'"; /ns1:a/text()'
  PASSING X.XML RETURNING CONTENT) AS VALUE
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID;

ORA-19109: RETURNING keyword expected

但您可以使用 CTE 生成完整的 XPath:

SELECT cast(XMLQuery('declare namespace ns1="http://my.test/v1"; /ns1:a/text()'
  PASSING X.XML RETURNING CONTENT) as varchar2(30)) AS VALUE
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID;

VALUE    
----------
TEST1
TEST2

或者作为一种替代方法,您可以使用通配符命名空间(我在这里进行转换是因为我的瘦驱动程序不喜欢返回的内容,但您可能不需要这样做):

SELECT CAST(XMLQuery('//*[local-name() = ''a'']/text()'
  PASSING X.XML RETURNING CONTENT) AS VARCHAR(10)) AS VALUE
FROM XML_TEST X;

VALUE    
----------
TEST1     
TEST2     

或者通配符命名空间,然后限制使用传递的变量,这有点简洁:

SELECT CAST(XMLQuery('//*[local-name() = ''a'' and namespace-uri() = $ns1]/text()'
  PASSING X.XML, XSD.NAMESPACE AS "ns1" RETURNING CONTENT) AS VARCHAR(10)) AS VALUE
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID;

VALUE    
----------
TEST1     
TEST2     

使用 XMLTable,您不能直接在任何一个中传递列值:

SELECT T.*
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID
CROSS JOIN XMLTable(XMLNamespaces(XSD.NAMESPACE as "ns1"), '/ns1:a'
  PASSING X.XML
  COLUMNS value VARCHAR2(80) PATH '.'
) T;

ORA-19102: XQuery string literal expected

您也可以在这里使用通配符方法:

SELECT T.*
FROM XML_TEST X
CROSS JOIN XMLTable('for $i in /*[local-name() = ''a''] return $i'
  PASSING X.XML
  COLUMNS value VARCHAR2(10) PATH '.'
) T;

VALUE    
----------
TEST1     
TEST2     

或者再次传递 URI:

SELECT T.*
FROM XML_TEST X
JOIN XSD_TEST XSD ON X.XSD_ID = XSD.ID
CROSS JOIN XMLTable('for $i in /*[local-name() = ''a'' and namespace-uri() = $ns1] return $i'
  PASSING X.XML, XSD.NAMESPACE AS "ns1"
  COLUMNS value VARCHAR2(10) PATH '.'
) T;

VALUE    
----------
TEST1     
TEST2     

SQL Fiddle,也在 11gR2 上(并且在这个环境中也需要对第一个查询进行强制转换)。

看看这些在 12c 中如何表现以及哪种最适合您将会很有趣。假设它们完全起作用......

【讨论】:

以上是关于具有动态命名空间的 Oracle 提取值的主要内容,如果未能解决你的问题,请参考以下文章

具有动态类名的 PHP 命名空间

Vuex:具有动态命名空间的 createNamespacedHelpers

如何使用 XPath 忽略命名空间

具有命名空间的 XML 文档上的 XPath

在命名空间定义中使用变量

ActiveRecord灯具如何动态插入全局命名空间?