具有动态命名空间的 Oracle extractvalue
Oracle extractvalue with dynamic namespace
我有一个 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
它正确return 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
正确return 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;
它 returns TEST1 和 (null)
谁能告诉我为什么会得到这样的结果,以及我如何才能真正得到 "correct" 结果:TEST1 和 TEST2?
根据评论,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 中的表现以及哪种最适合您将会很有趣。假设它们完全有效...
我有一个 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
它正确return 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
正确return 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;
它 returns TEST1 和 (null)
谁能告诉我为什么会得到这样的结果,以及我如何才能真正得到 "correct" 结果:TEST1 和 TEST2?
根据评论,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 中的表现以及哪种最适合您将会很有趣。假设它们完全有效...