【问题标题】:xquery return Boolean if node exists at a certain location or not如果节点存在于某个位置,xquery 返回布尔值
【发布时间】:2012-10-13 18:17:21
【问题描述】:

我在 SQL 2008 数据库中有类似于以下内容的 XML,存储在 XML 字段中。如果节点存在于 XML 的特定部分中,我想返回真或假指示。

<root>
    <node attribute1='value1' attribute2='value2'>
        <sub1 name='ID' value="1" />
        <sub2 name='project' value="abc" />
        <sub3 name='Lead' value="John" />
    </node>
    <entry attribute1='value1' attribute2='value2'>
        <message>start</message>
    </entry>
    <entry attribute1='value1' attribute2='value2'>
        <attribute name='project' value='done'>
    </entry>    
    <node attribute1='value1'>
        <sub1 name='ID' value="2" />
        <sub2 name='project' value="abc" />
        <sub3 name='Lead' value="John" />
    </node>
    <entry attribute1='value1' attribute2='value2'>
        <message>start</message>
    </entry>
    <node attribute1='value1'>
        <sub1 name='ID' value="3" />
        <sub2 name='project' value="abc" />
        <sub3 name='Lead' value="John" />
    </node>
    <entry attribute1='value1' attribute2='value2'>
        <message>start</message>
    </entry>
    <node attribute1='value1'>
        <sub1 name='ID' value="4" />
        <sub2 name='project' value="abc" />
        <sub3 name='Lead' value="John" />
    </node> 
    <entry attribute1='value1' attribute2='value2'>
        <message>start</message>
    </entry>
    <entry attribute1='value1' attribute2='value2'>
        <attribute name='project' value='done'>
    </entry>
</root>

您会注意到,&lt;attribute&gt; 节点可能出现在具有“ID”的节点之后,也可能不会出现。在此示例中,您可以在第一和第四“部分”中看到它,因为没有更好的术语。

具有如下表结构:

ID (PK)
EventID (FK)
RawXML (XML)
Created (datetime)

这是我目前所拥有的 SQL/xQuery 的摘录:

WITH XMLNAMESPACES(
  'http://www.w3.org/2001/XMLSchema-instance' as xsi,
),
t1 as(
    SELECT distinct
      x.EventId
    , c.value ('(//node/sub[@name=''ID'']/@value)[1]', 'nvarchar(max)') as ID   
    , c.value ('(//node/sub[@name=''ID''][1][descendant::attribute/@name=''project''])[1]', 'nvarchar(max)' ) as Exists     
    FROM
      Table1 x
    CROSS APPLY
      RawXML.nodes('./.') as t(c)
     )
select distinct
  t1.ID
, t1.Exists
from t1

我将运行脚本 4 次或更多次(每次运行前递增所有单例值)

对于给定的 XML,我需要在运行 4 次查询后得到以下结果: (不知道 ID 的值,所以我不能在查询中使用它们)

    ID    Exists
   ----   -------
    1      true
    2      false
    3      false
    4      true

使用给出的 SQL,我没有收到任何错误,但它需要很长时间(超过 45 分钟),我什至还没有让它完成。解析 XML 真的不应该花这么长时间。

更新:我限制了我的查询以确保它只解析一行(一个 XML 文件)并在 57 秒内完成。但是,当我应该为 ID 1 获得“1”时,我得到了 ID 1 和 ID 2 的“0”结果。

我相信你们中的大多数人都知道 SQL Server 不支持跟随兄弟等,所以很遗憾,这不是一个选项。

仅供参考,我已成功使用它找到了“Project”的两个实例,但它忽略了它们在 xml 中出现的位置。:

c.value ('(//node[descendant::attribute/@name=''Project''])[1]', 'nvarchar(max)' ) as TrueFalse

所以基本上,我需要知道 name='Project' 的节点是否存在于 name='ID' 的节点之后,但在 name='ID' 的节点的下一个实例之前存在

【问题讨论】:

    标签: xpath xquery xquery-sql


    【解决方案1】:

    您的 XML 中有一些错误,根据您使用的查询判断,我还更改了子节点。

    您可以使用row_number() 枚举您的 ID 和项目节点,然后使用常规 SQL 而不是 XQuery 检查“下一行”是项目节点还是 ID 行。

    -- Temp table to hold the extracted values from the XML
    create table #C
    (
      rn int primary key,
      ID int
    );
    
    -- Get the enumerated rows with ID.
    -- project nodes will have NULL in ID
    insert into #C
      select row_number() over(order by T.N) as rn,
             T.N.value('sub[@name = "ID"][1]/@value', 'int') as ID
      from table1
        cross apply RawXML.nodes('/root/*[sub/@name = "ID" or attribute/@name = "project"]') as T(N)
    
    -- Get the ID's and check if the next row is a project node
    select C1.ID,
           case when exists (
                              select *
                              from #C as C2
                              where C1.rn + 1 = C2.rn and 
                              C2.ID is null
                            ) 
             then 1
             else 0
           end as [Exists]
    from #C as C1
    where C1.ID is not null;
    
    drop table #C;
    

    SQL Fiddle

    您可以在没有临时表的情况下使用 CTE 来代替,但我怀疑临时表版本会更快。

    with C as
    (
      select row_number() over(order by T.N) as rn,
              T.N.value('sub[@name = "ID"][1]/@value', 'int') as ID
      from table1
        cross apply RawXML.nodes('/root/*[sub/@name = "ID" or attribute/@name = "project"]') as T(N)
    )
    select C1.ID,
            case when exists (
                              select * 
                              from C as C2 
                              where C1.rn + 1 = C2.rn and 
                                    C2.ID is null
                            ) 
              then 1
              else 0
            end as [Exists]
    from C as C1
    where C1.ID is not null;
    

    SQL Fiddle

    【讨论】:

    • 谢谢米凯尔。我会尝试你的解决方案,看看我是否可以让它工作。我快速输入了 XML,所以肯定有错误。由于敏感信息,我无法发布我的实际 XML。也感谢 Temp 表上的提示。我希望这将有助于运行时间。
    • 好吧,我已经尝试让它与 XML 一起工作,但到目前为止还没有运气。现在我只是想看看将要插入到临时表中的内容,但是我在拉取 Value() 的行上得到了下面的“单例”错误,即使我指定了第一个实例 [1]。这是完整的错误:XQuery [Table1.RawXML.value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'
    • 好的,如果我将 [1] 指定移动到 @value 一侧,我可以让它返回结果,如下所示:T.N.value('sub[@name = "ID"]/@value[1]', 'int') as ID。我还必须将“交叉应用”更改为:cross apply RawXML.nodes('/root/*sub[@name = "ID" or attribute/@name = "project"]')。这些行是有序的,但是每一行都有一个与之关联的 ID 值,而不是“项目”行的 NULL。
    • @N1tr0 那么你的 XML 看起来不像你发布的那样。您必须将attribute 作为sub 的子项吗?如果您发布的内容看起来像您拥有的 XML,我可以查看它并了解应该如何修改查询。
    • 好吧,我不得不尝试一下我的 SQL,因为我的源 XML 比示例复杂得多,但我让它工作了。谢谢米凯尔!你摇滚!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    • 2021-03-21
    相关资源
    最近更新 更多