具有动态命名空间的Oracle extractvalue

时间:2015-05-28 14:36:41

标签: sql xml oracle namespaces xmltype

我有一个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)

有人可以告诉我为什么我会得到这样的结果以及我如何才能真正得到正确的&#34;结果:TEST1和TEST2?

1 个答案:

答案 0 :(得分:2)

继续发表评论之后,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中的行为方式会很有趣,哪种方式最适合你。假设他们一直工作......