是否可以在nodes()
的XQuery语句中引用当前上下文节点?
假设我将XML数据排序在表/表变量中,其中每个文档的一个部分通过属性引用另一个部分中的元素。 (在下面,人和位置通过ID关联。)
DECLARE @Data TABLE (
ID INT NOT NULL PRIMARY KEY,
XmlData XML NOT NULL
)
INSERT INTO @Data
VALUES (1, '<Root>
<People>
<Person id="1">Frank</Person>
<Person id="2">Joe</Person>
</People>
<Positions>
<Position assignedToPerson="1">Engineer</Position>
<Position assignedToPerson="2">Manager</Position>
</Positions>
</Root>'),
(2, '<Root>
<People>
<Person id="5">Bob</Person>
<Person id="6">Sam</Person>
</People>
<Positions>
<Position assignedToPerson="6">Mechanic</Position>
<Position assignedToPerson="5">Accountant</Position>
</Positions>
</Root>')
人员位置配对的结果集可以这样产生:
SELECT
PersonID = person.value('@id', 'NVARCHAR(50)'),
Name = person.value('.', 'NVARCHAR(50)'),
Position = position.value('.', 'NVARCHAR(50)')
FROM @Data
CROSS APPLY XmlData.nodes('/Root/People/Person') People(person)
CROSS APPLY person.nodes('/Root/Positions/Position') Positions(position)
WHERE person.value('@id', 'NVARCHAR(50)')= position.value('@assignedToPerson[1]','NVARCHAR(50)')
+----------+-------+------------+ | PersonID | Name | Position | +----------+-------+------------+ | 1 | Frank | Engineer | | 2 | Joe | Manager | | 5 | Bob | Accountant | | 6 | Sam | Mechanic | +----------+-------+------------+
第二个CROSS APPLY
在每次调用时将相关XML文档中定义的每个位置分成自己的行。结果是文档中每个人与文档中定义的每个位置配对。将结果集过滤到相关的人员 - 位置对发生在WHERE
子句中。
我想通过在第二个XQuery中引用People(person)
的上下文节点来消除将所有人匹配到每个位置 - 就像这样:
SELECT
PersonID = person.value('@id', 'NVARCHAR(50)'),
Name = person.value('.', 'NVARCHAR(50)'),
Position = position.value('.', 'NVARCHAR(50)')
FROM @Data
CROSS APPLY XmlData.nodes('/Root/People/Person') People(person)
CROSS APPLY person.nodes('/Root/Positions/Position[@assignedToPerson={{**reference to @ID of context node**}}]') Positions(position)
我可以在第二个nodes()
的XQuery中引用第一个CROSS APPLY
上下文节点吗?
(使用JOIN-based approach不起作用,因为上述数据来自表,而不是XML变量。)
答案 0 :(得分:1)
基本上,你想加入人和职位。
(1)如果那些存储在表格中的人和位置的数据可以很容易地完成。这也意味着,我将此信息存储在单个列中而不是单个XML列中。
(2)如果由于某些原因你不能这样做,那么你可以重新设计XML:
<Root>
<People>
<Person id="1" name="Frank" position="Engineer" />
<Person id="2" name="Joe" position="Manager" />
</People>
</Root>
(示例:
UPDATE x
SET XmlData = NewXmlData
FROM (
SELECT d.ID, d.XmlData, d.XmlData.query('
<Root>
<People>
{for $per in (/Root/People/Person)
for $pos in (/Root/Positions/Position[@assignedToPerson = $per/@id])
return
<Person id="{$per/@id}" name="{$per/text()}" position="{$pos/text()}"/>}
</People>
</Root>
') AS NewXmlData
FROM @Data d
) x
SELECT d.ID,
x.XmlPerson.value('(@name)[1]', 'NVARCHAR(50)') AS name,
x.XmlPerson.value('(@position)[1]', 'NVARCHAR(50)') AS position
FROM @Data d
CROSS APPLY d.XmlData.nodes('/Root/People/Person') x(XmlPerson)
)
或
<Root>
<People>
<Person id="1" name="Frank">
<Positions>
<Position>Engineer</Position>
</Positions>
</Person>
<Person id="2" name="Joe">
<Positions>
<Position>Manager</Position>
</Positions>
</Person>
</People>
</Root>
(不完整的例子:
SELECT d.XmlData.query('
<Root>
<People>
{for $per in (/Root/People/Person)
return
<Person id="{$per/@id}" name="{$per/text()}">
<Positions>
{for $pos in (/Root/Positions/Position[@assignedToPerson = $per/@id])
return <Position>{string($pos/text()[1])}</Position>}
</Positions>
</Person>
}
</People>
</Root>') AS NewXmlData
FROM @Data d
)
(3)如果由于某些原因,您无法做到这一点,并且如果您想找到更好的解决方案(从性能的角度来看),那么您可以使用以下解决方案之一:
PRINT 'Solution #1'
SELECT Person.Person_id,
Person.Person_name,
Position_name = Person.XmlData.value('(/Root/Positions/Position[@assignedToPerson = sql:column("Person_id")]/text())[1]', 'NVARCHAR(50)')
FROM (
SELECT Person_id = x.XmlPerson.value('(@id)[1]', 'INT'),
Person_name = x.XmlPerson.value('(text())[1]', 'NVARCHAR(50)'),
d.XmlData
FROM @Data d
CROSS APPLY d.XmlData.nodes('/Root/People/Person') x(XmlPerson)
) Person
PRINT 'Solution #2'
SELECT Person.Person_id,
Person.Person_name,
Position.Position_name
FROM (
SELECT Person_id = x.XmlPerson.value('(@id)[1]', 'INT'),
Person_name = x.XmlPerson.value('(text())[1]', 'NVARCHAR(50)'),
d.XmlData
FROM @Data d
CROSS APPLY d.XmlData.nodes('/Root/People/Person') x(XmlPerson)
) Person INNER /*HASH*/ JOIN (
SELECT Position_assignedToPerson = x.XmlPerson.value('(@assignedToPerson)[1]', 'INT'),
Position_name = x.XmlPerson.value('(text())[1]', 'NVARCHAR(50)')
FROM @Data d
CROSS APPLY d.XmlData.nodes('/Root/Positions/Position') x(XmlPerson)
) Position ON Person.Person_id = Position.Position_assignedToPerson
PRINT 'Solution #3'
SELECT Person.Person_id,
Person.Person_name,
Position_name = y.XmlPosition.value('(text())[1]', 'NVARCHAR(50)')
FROM (
SELECT Person_id = x.XmlPerson.value('(@id)[1]', 'INT'),
Person_name = x.XmlPerson.value('(text())[1]', 'NVARCHAR(50)'),
d.XmlData
FROM @Data d
CROSS APPLY d.XmlData.nodes('/Root/People/Person') x(XmlPerson)
) Person
CROSS APPLY Person.XmlData.nodes('/Root/Positions/Position[@assignedToPerson = sql:column("Person_id")]') y(XmlPosition);
注意:如果这不是一次性任务,那么我将使用(1)。
注意#2:问题的唯一解决方案(或多或少)“是否可以在nodes()的XQuery语句中引用当前上下文节点?”是(2)的例子。