为什么不:
SELECT
SomeXmlColumn.nodes('/people/person') AS foo(b)
FROM MyTable
Run Code Online (Sandbox Code Playgroud)
工作?
几乎所有我在SQL Server中看到(或得到)使用XPath查询的答案要求您使用XML将XML文档加入自身CROSS APPLY.
为什么?
SELECT
p.value('(./firstName)[1]', 'VARCHAR(8000)') AS firstName,
p.value('(./lastName)[1]', 'VARCHAR(8000)') AS lastName
FROM table
CROSS APPLY field.nodes('/person') t(p)
Run Code Online (Sandbox Code Playgroud)
例如:
SELECT a.BatchXml.value('(Name)[1]', 'varchar(50)') AS Name,
a.BatchXml.value('(IDInfo/IDType)[1]', 'varchar(50)') AS IDType,
a.BatchXml.value('(IDInfo/IDOtherDescription)[1]', 'varchar(50)') AS IDOtherDescription
FROM BatchReports b
CROSS APPLY b.BatchFileXml.nodes('Customer') A(BatchXml)
WHERE a.BatchXml.exist('IDInfo/IDType[text()=3]')=1
Run Code Online (Sandbox Code Playgroud)
例如:
SELECT b.BatchID,
x.XmlCol.value('(ReportHeader/OrganizationReportReferenceIdentifier)[1]','VARCHAR(100)') AS OrganizationReportReferenceIdentifier,
x.XmlCol.value('(ReportHeader/OrganizationNumber)[1]','VARCHAR(100)') AS OrganizationNumber
FROM Batches b
CROSS APPLY b.RawXml.nodes('/CasinoDisbursementReportXmlFile/CasinoDisbursementReport') x(XmlCol);
Run Code Online (Sandbox Code Playgroud)
SELECT nref.value('first-name[1]', 'nvarchar(32)') FirstName,
nref.value('last-name[1]', 'nvarchar(32)') LastName
FROM [XmlFile] CROSS APPLY [Contents].nodes('//author') AS p(nref)
Run Code Online (Sandbox Code Playgroud)
他们都使用它.但是没有人(甚至不是SQL Server联机丛书)解释了为什么需要它,它解决了什么问题,它正在做什么,或者它是如何工作的.
即使是采用XML的最简单的例子:
<people>
<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>
</people>
Run Code Online (Sandbox Code Playgroud)
并返回值:
FirstName LastName
========= ========
Jon Johnson
Kathy Carter
Bob Burns
Run Code Online (Sandbox Code Playgroud)
需要加入:
SELECT
p.value('(./firstName)[1]', 'VARCHAR(8000)') AS firstName,
p.value('(./lastName)[1]', 'VARCHAR(8000)') AS lastName
FROM table
CROSS APPLY field.nodes('/person') t(p)
Run Code Online (Sandbox Code Playgroud)
令人困惑的是,它甚至没有使用它加入的表,为什么需要它呢?
由于从未记录或解释过查询XML,希望我们现在可以解决这个问题.
所以让我们从一个实际的例子开始,因为我们想要一个实际的答案,这给出了一个实际的解释:
DECLARE @xml xml;
SET @xml =
'<people>
<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>
</people>';
;WITH MyTable AS (
SELECT @xml AS SomeXmlColumn
)
Run Code Online (Sandbox Code Playgroud)
现在我们有psuedo表,我们可以查询:

首先,我需要人民.在真正的 XML中,我可以轻松返回三行:
/people/person
Run Code Online (Sandbox Code Playgroud)
其中NodeList包含三个节点:
<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>
Run Code Online (Sandbox Code Playgroud)
在SQL Server中,相同的查询:
SELECT
SomeXmlColumn.query('/people/person')
FROM MyTable
Run Code Online (Sandbox Code Playgroud)
不返回三行,而是返回包含三个节点的XML的一行:
<person>
<firstName>Jon</firstName>
<lastName>Johnson</lastName>
</person>
<person>
<firstName>Kathy</firstName>
<lastName>Carter</lastName>
</person>
<person>
<firstName>Bob</firstName>
<lastName>Burns</lastName>
</person>
Run Code Online (Sandbox Code Playgroud)
显然这是不合适的,当我的最终目标是返回3 行.我不得不将三行分成三行.
我的实际目标是获得firstName和lastName.在XPath中我可以做类似的事情:
/people/person/firstName|/people/person/lastName
Run Code Online (Sandbox Code Playgroud)
这让我得到了六个节点,虽然他们没有相邻
<firstName>Jon</firstName>
<lastName>Johnson</lastName>
<firstName>Kathy</firstName>
<lastName>Carter</lastName>
<firstName>Bob</firstName>
<lastName>Burns</lastName>
Run Code Online (Sandbox Code Playgroud)
在SQL Server中,我们尝试类似的东西
SELECT
SomeXmlColumn.query('/people/person/firstName') AS FirstName,
SomeXmlColumn.query('/people/person/lastName') AS LastName
FROM MyTable
Run Code Online (Sandbox Code Playgroud)
它给我们一行,每列包含一个XML片段:
FirstName LastName
============================ ============================
<firstName>Jon</firstName> <lastName>Johnson</lastName>
<firstName>Kathy</firstName> <lastName>Carter</lastName>
<firstName>Bob</firstName> <lastName>Burns</lastName>
Run Code Online (Sandbox Code Playgroud)
......现在我累了.我花了三个小时写这个问题,在我花了四个小时询问昨天的问题之前.我稍后会回到这个问题; 当它在这里凉爽的时候,我有更多的精力去寻求帮助.
根本问题在于,无论我做什么,我都只会返回一行.我想要返回三行(因为有三个人).SQL Server 确实有一个可以将XML行(称为节点)转换为SQL Server行(称为行)的函数.这是.nodes功能:
当您想要将xml数据类型实例分解为关系数据时,nodes()方法很有用.它允许您识别将映射到新行的节点.
这意味着您.nodes使用xml数据类型的XPath查询"调用"该方法.过去在SQL Server中作为一行有三个节点的东西,回来(正确)为三个节点:
.nodes('/people/person') AS MyDerivedTable(SomeOtherXmlColumn)
Run Code Online (Sandbox Code Playgroud)
从概念上讲,它返回:
SomeOtherXmlColumn
------------------------------------------------------------------------
<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>
Run Code Online (Sandbox Code Playgroud)
但是,如果你真的尝试使用它,它不起作用:
DECLARE @xml xml;
SET @xml =
'<people>
<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>
</people>';
SELECT *
FROM @xml.nodes('/people/person') AS MyDervicedTable(SomeOtherXmlColumn)
Run Code Online (Sandbox Code Playgroud)
给出错误:
消息493,级别16,状态1,行8
从nodes()方法返回的列'SomeOtherXmlColumn'不能直接使用.它只能与四个XML数据类型的方法之一使用的,存在(),节点(),查询()和()的值,或在IS NULL和IS NOT NULL检查.
我认为这是因为我不允许查看结果集(即*不允许).没问题.我将使用.query我原来使用的相同:
SELECT SomeOtherXmlColumn.query('/') AS SomeOtherOtherXmlColumn
FROM @xml.nodes('/people/person') AS MyDervicedTable(SomeOtherXmlColumn)
Run Code Online (Sandbox Code Playgroud)
哪个返回行.但是,不是将节点列表拆分成行,而是复制整个XML:
SomeOtherOtherXmlColumn
----------------------------------------
<people><person><firstName>Jon</firstName><lastName>Johnson</lastName></person><person><firstName>Kathy</firstName><lastName>Carter</lastName></person><person><firstName>Bob</firstName><lastName>Burns</lastName></person></people>
<people><person><firstName>Jon</firstName><lastName>Johnson</lastName></person><person><firstName>Kathy</firstName><lastName>Carter</lastName></person><person><firstName>Bob</firstName><lastName>Burns</lastName></person></people>
<people><person><firstName>Jon</firstName><lastName>Johnson</lastName></person><person><firstName>Kathy</firstName><lastName>Carter</lastName></person><person><firstName>Bob</firstName><lastName>Burns</lastName></person></people>
Run Code Online (Sandbox Code Playgroud)
这是有道理的.我期待 SQL Server中的XPath查询表现得像XPath.但事后仔细阅读文档说不然:
nodes()方法的结果是一个包含原始XML实例的逻辑副本的行集.在这些逻辑副本中,每个行实例的上下文节点都设置为使用查询表达式标识的节点之一,以便后续查询可以相对于这些上下文节点进行导航.
xml列来做前面的示例是针对类型的变量xml.现在我们必须改进.nodes函数以使用包含xml列的表:
SELECT
SomeXmlColumn.nodes('/people/person')
FROM MyTable
Run Code Online (Sandbox Code Playgroud)
不,那不起作用:
消息227,级别15,状态1,行8
"节点"不是有效的功能,属性或字段.
虽然它.nodes 是一种有效的xml数据类型方法,但当您尝试在xml数据类型上使用它时,它根本不起作用.在xml数据类型上使用时,它也不起作用:
SELECT *
FROM MyTable.SomeXmlColumn.nodes('/people/person')
Run Code Online (Sandbox Code Playgroud)
消息208,级别16,状态1,行8
无效的对象名称'MyTable.SomeXmlColumn.nodes'.
我认为这就是为什么CROSS APPLY需要修饰语.不是因为您要加入任何内容,而是因为SQL Server解析器将拒绝识别,.nodes除非它前面带有关键字cross apply:
SELECT
'test' AS SomeTestColumn
FROM MyTable CROSS APPLY MyTable.SomeXmlColumn.nodes('/people/person') AS MyDerivedTable(SomeOtherXmlColumn)
Run Code Online (Sandbox Code Playgroud)
我们开始到达某个地方:
SomeTestColumn
--------------
test
test
test
Run Code Online (Sandbox Code Playgroud)
因此,如果我们想要查看返回的XML:
SELECT
SomeOtherXmlColumn.query('/')
FROM (MyTable CROSS APPLY MyTable.SomeXmlColumn.nodes('/people/person') AS MyDerivedTable(SomeOtherXmlColumn))
Run Code Online (Sandbox Code Playgroud)
现在我们有三排.
它似乎cross apply不是用于连接,而只是一个允许.nodes工作的关键字
而且似乎SQL Server优化器只是拒绝接受任何使用
.nodes
Run Code Online (Sandbox Code Playgroud)
你必须实际使用:
CROSS APPLY .nodes
Run Code Online (Sandbox Code Playgroud)
这就是它的样子.如果是这样的话 - 那没关系.这是规则.这导致了多年的混乱; 我以为我正在与cross apply运营商加入其他东西.
除了我相信它还有更多.不知何故,实际上必须cross apply发生这种情况.但我无法看到 - 或为什么.
ant*_*nth 14
查询:
SELECT x.i.value('(./text())[1]', 'VARCHAR(10)')
FROM MyTable.SomeXmlColumn.nodes('./people/person/firstName') AS x(i);
Run Code Online (Sandbox Code Playgroud)
不起作用,原因与此查询不起作用的原因相同:
SELECT *
FROM Person.Person.FirstName;
Run Code Online (Sandbox Code Playgroud)
但这样做:
SELECT FirstName
FROM Person.Person;
Run Code Online (Sandbox Code Playgroud)
-
FROM子句需要rowset,所以这是有效的,因为nodes()返回rowset:
DECLARE @xml AS XML =
'<people>
<person><firstName>Jon</firstName><lastName>Johnson</lastName></person>
<person><firstName>Kathy</firstName><lastName>Carter</lastName></person>
<person><firstName>Bob</firstName><lastName>Burns</lastName></person>
</people>';
SELECT x.i.value('(./text())[1]', 'VARCHAR(10)')
FROM @xml.nodes('./people/person/firstName') AS x(i);
Run Code Online (Sandbox Code Playgroud)
如果xml不是变量而是表中的值,我们首先需要从这个值中提取行,这就是当CROSS APPLY派上用场时:
SELECT x.i.value('(./text())[1]', 'VARCHAR(10)')
FROM MyTable as t
CROSS APPLY
t.SomeXmlColumn.nodes('./people/person/firstName') AS x(i);
Run Code Online (Sandbox Code Playgroud)
CROSS APPLY运算符将右表达式应用于左表(MyTable)中的每个记录.
比较'普通' CROSS APPLY查询:
SELECT c.CustomerID, soh.TotalDue, soh.OrderDate
FROM Sales.Customer AS c
CROSS APPLY
(SELECT TOP(2) TotalDue, OrderDate
FROM Sales.SalesOrderHeader
WHERE CustomerID = c.CustomerID
ORDER BY TotalDue DESC) AS soh;
Run Code Online (Sandbox Code Playgroud)
c.CustomerID是我们的.SomeXmlColumn
你问题的答案就在你的问题中.
nodes()方法的结果是行集
你也做不到这一点
WITH T(X) AS
(
SELECT 1
)
SELECT X, (SELECT 'A' AS Y UNION ALL SELECT 'B' AS Y)
FROM T
Run Code Online (Sandbox Code Playgroud)
但你可以做到
WITH T(X) AS
(
SELECT 1
)
SELECT X, Y
FROM T
CROSS APPLY (SELECT 'A' AS Y UNION ALL SELECT 'B' AS Y) C
Run Code Online (Sandbox Code Playgroud)
SELECT ... FROM T无论您在SELECT列表中调用哪些函数,直行都无法向结果集添加或减去行.这不是SQL的工作原理.
| 归档时间: |
|
| 查看次数: |
22093 次 |
| 最近记录: |