【问题标题】:Filtering in nodes() XQuery by Referencing Current Context Node通过引用当前上下文节点在 nodes() XQuery 中过滤
【发布时间】:2014-08-02 15:40:10
【问题描述】:

是否可以在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)')
+---------+--------+------------+ |个人ID |姓名 |职位 | +---------+--------+------------+ | 1 |弗兰克 |工程师 | | 2 |乔 |经理 | | 5 |鲍勃 |会计师 | | 6 |山姆 |机械师 | +----------+--------+------------+

问题

第二个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)

我可以在第二个 CROSS APPLY 的 XQuery 中引用第一个 nodes() 上下文节点吗?

(使用 JOIN-based approach 不起作用,因为上述数据来自表,而不是 XML 变量。)

【问题讨论】:

    标签: xml tsql xquery-sql


    【解决方案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) 中的示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-04
      • 1970-01-01
      相关资源
      最近更新 更多