在 xml 数据中搜索值

pri*_*ejt 4 xml sql-server

我有一个名称表:

表A

John
Jim
Jason
Run Code Online (Sandbox Code Playgroud)

和一个 xml 字符串表:

表B

示例 1

    <Show Title="" ShowTitle="=False" ShowLine="=False" ShowDescription="=False" ShowExpandCollapse="=False" IsVisible="=True" Description="" PageBreakAfter="=False" PageCaption="" AppLink="" InfoLink="" ImageLink="" Pause="=False" PauseMessage="" PauseMessageStyle="" PauseTitle="" ScreenStyle="">
  <ShowOption Sequence="1" Name="Jim" Caption="Test" SelectOptionsImageLinkFieldExpression="ImageLink" />
</Show>
Run Code Online (Sandbox Code Playgroud)

示例 2

<vars>
  <var name="MatrixName">
    <value>="LockExitPairCompatability"</value>
  </var>
  <var name="Jason">
    <value>=If(Exists(Minute.Value.Min), ToNumber(Minute.Value.Min), 0)</value>
  </var>
  <var name="MinNum">
    <value>=If(Exists(Agency) AND Agency = "Cert", 0, MinNum)</value>
  </var>
  <var name="Where">
    <value>="Site='" + Root.Site + "' AND HWType IN " + ToSQLArray(Root.Components[ActiveDoor].HardwareType)</value>
  </var>
  <var name="Where2">
    <value>=" AND HWSubType IN " + ToSQLArray(Root.Components[ActiveDoor].LockSubType)</value>
  </var>
  <var name="CompatibleList">
    <value>=usr.FetchMatrix</value>
  </var>
</vars>
Run Code Online (Sandbox Code Playgroud)

示例 3

<rule>
  <property name="CollectionVariable" DisplayName="Collection Variable" ValueType="RValueExpression">John</property>
  <property name="KeyVariable" DisplayName="Key Variable" ValueType="LValueExpression">NextLock</property>
</rule>
Run Code Online (Sandbox Code Playgroud)

如何搜索以查找表 A 中的哪些名称不在任何 xml 字符串中?显示的数据只是示例。数据库中的 xml 具有各种不同的格式。

在 xml 数据中,它可能是“Root.Name”。即使使用“根”,我仍然希望它找到它。附在它上面。

Mik*_*son 6

我假设您想查看所有属性和所有元素。

对表 B 中的 XML 列使用函数exists和一个谓词来检查表A 中的值是否存在。函数sql:column用于将值从A 获取到XQuery 表达式中。

declare @A table(Name varchar(50));
insert into @A(Name) values('John'),('Jim'),('Jason'),('Mike');

declare @B table(XMLCol xml);

insert into @B(XMLCol) values
('<Show Title="" ShowTitle="=False" ShowLine="=False" ShowDescription="=False" ShowExpandCollapse="=False" IsVisible="=True" Description="" PageBreakAfter="=False" PageCaption="" AppLink="" InfoLink="" ImageLink="" Pause="=False" PauseMessage="" PauseMessageStyle="" PauseTitle="" ScreenStyle="">
  <ShowOption Sequence="1" Name="Jim" Caption="Test" SelectOptionsImageLinkFieldExpression="ImageLink" />
</Show>'),
('<vars>
  <var name="MatrixName"><value>="LockExitPairCompatability"</value></var>
  <var name="Jason"><value>=If(Exists(Minute.Value.Min), ToNumber(Minute.Value.Min), 0)</value></var>
  <var name="MinNum"><value>=If(Exists(Agency) AND Agency = "Cert", 0, MinNum)</value></var>
  <var name="Where"><value>="Site=''" + Root.Site + "'' AND HWType IN " + ToSQLArray(Root.Components[ActiveDoor].HardwareType)</value></var>
  <var name="Where2"><value>=" AND HWSubType IN " + ToSQLArray(Root.Components[ActiveDoor].LockSubType)</value></var>
  <var name="CompatibleList"><value>=usr.FetchMatrix</value></var>
</vars>'),
('<rule>
  <property name="CollectionVariable" DisplayName="Collection Variable" ValueType="RValueExpression">John</property>
  <property name="KeyVariable" DisplayName="Key Variable" ValueType="LValueExpression">NextLock</property>
</rule>');

select A.Name
from @A as A
where not exists (
                 select *
                 from @B as B
                 where B.XMLCol.exist('(//@*, //text())[. = sql:column("A.Name")]') = 1
                 );
Run Code Online (Sandbox Code Playgroud)

// 搜索所有后代。

@ 指定您正在查找属性。

* 是属性名称的通配符。

//text() 为您提供所有后代文本值(不是属性)。

(//@*, //text()) 将属性与文本值结合起来

[. = sql:column("A.Name")]检查当前值的谓词A.Name


如果你想检查一个值是否包含A.Name你应该使用contains函数。

select A.Name
from @A as A
where not exists (
                 select *
                 from @B as B
                 where B.XMLCol.exist('(//@*, //text())[contains(., sql:column("A.Name"))]') = 1
                 );
Run Code Online (Sandbox Code Playgroud)

如果需要不区分大小写的比较,请将两个参数小写为contains.


Pet*_*ier 5

我喜欢找借口提起这个老栗子。它是一个基于 TSQL 的 XML 粉碎器,可将您的 XML 文档分解为一系列(某种)键值对。当你不知道你在寻找什么时,你必须寻找一切。

CREATE OR ALTER FUNCTION dbo.fnXmlNodeValue 
(   
    @x xml   
)
RETURNS TABLE 
AS
RETURN 
-- credit to https://stackoverflow.com/a/10885014/4709762
(
    WITH cte AS 
    (  
        SELECT 
            lvl =               1,  
            Name =              x.value('local-name(.)','NVARCHAR(MAX)'),  
            ParentName =        CAST(NULL AS NVARCHAR(MAX)), 
            ParentPosition =    CAST(1 AS INT), 
            NodeType =          CAST(N'Element' AS NVARCHAR(20)),  
            FullPath =          x.value('local-name(.)','NVARCHAR(MAX)'),  
            XPath =             x.value('local-name(.)','NVARCHAR(MAX)')  
                + N'[' + CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS NVARCHAR) + N']',  
            Position =          ROW_NUMBER() OVER(ORDER BY (SELECT 1)), 
            Tree =              x.value('local-name(.)','NVARCHAR(MAX)'),  
            Value =             x.value('text()[1]','NVARCHAR(MAX)'), 
            this =              x.query('.'),         
            t =                 x.query('*'),  
            Sort =              CAST(CAST(1 AS VARBINARY(4)) AS VARBINARY(MAX)),  
            ID =                CAST(1 AS INT)  
        FROM @x.nodes('/*') a(x)  
        UNION ALL 
        SELECT 
            lvl =               p.lvl + 1,  
            Name =              c.value('local-name(.)','NVARCHAR(MAX)'),  
            ParentName =        CAST(p.Name AS NVARCHAR(MAX)), 
            ParentPosition =    CAST(p.Position AS INT), 
            NodeType =          CAST(N'Element' AS NVARCHAR(20)),  
            FullPath =          CAST(p.FullPath + N'/' + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX)),  
            XPath =             CAST(p.XPath + N'/'+ c.value('local-name(.)','NVARCHAR(MAX)') 
                + N'['+ CAST(ROW_NUMBER() OVER(PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)') ORDER BY (SELECT 1)) AS NVARCHAR)+ N']' AS NVARCHAR(MAX)), 
            Position =          ROW_NUMBER() OVER(PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)') ORDER BY (SELECT 1)), 
            Tree =              CAST( SPACE(2 * p.lvl - 1) + N'|' + REPLICATE(N'-', 1) + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX)), 
            Value =             CAST( c.value('text()[1]','NVARCHAR(MAX)') AS NVARCHAR(MAX) ), 
            this =              c.query('.'),  
            t =                 c.query('*'),  
            Sort =              CAST(p.Sort + CAST( (lvl + 1) * 1024 + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS VARBINARY(4)) AS VARBINARY(MAX) ),  
            CAST((lvl + 1) * 1024 + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS INT)  
        FROM cte p  
        CROSS APPLY p.t.nodes('*') b(c)
    )
    ,cte2 AS 
    (
        SELECT 
            lvl AS Depth,  
            Name AS NodeName,  
            ParentName, 
            ParentPosition, 
            NodeType,  
            FullPath,  
            XPath,  
            Position, 
            Tree AS TreeView,  
            Value,  
            this AS XMLData,  
            Sort, ID  
        FROM cte  
        UNION ALL 
        SELECT 
            p.lvl,  
            x.value('local-name(.)','NVARCHAR(MAX)'),  
            p.Name, 
            p.Position, 
            CAST(N'Attribute' AS NVARCHAR(20)),  
            p.FullPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'),  
            p.XPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'),  
            1, 
            SPACE(2 * p.lvl - 1) + N'|' + REPLICATE('-', 1)  
            + N'@' + x.value('local-name(.)','NVARCHAR(MAX)'),  
            x.value('.','NVARCHAR(MAX)'),  
            NULL,  
            p.Sort,  
            p.ID + 1  
        FROM cte p  
        CROSS APPLY this.nodes('/*/@*') a(x)  
    )  
    SELECT 
        ID = ROW_NUMBER() OVER(ORDER BY Sort, ID),  
        ParentName, 
        ParentPosition, 
        Depth, 
        NodeName, 
        Position,   
        NodeType, 
        FullPath, 
        XPath, 
        TreeView, 
        Value, 
        XMLData 
    FROM cte2
);
Run Code Online (Sandbox Code Playgroud)

如何搜索以查找表 A 中的哪些名称不在任何 xml 字符串中?

在将您的 XML 文档分解为其键值对后,您执行反半连接以查找搜索目标中不存在的名称。

with shredder as (
    select b.id, 
           x.NodeName, 
           x.NodeType,
           x.FullPath, 
           x.Value 
    from TableB as b 
    cross apply dbo.fnXmlNodeValue(b.x) as x
) 
select * 
from TableA as a
where not exists (
    select 1 
    from shredder as s
    where s.Value = a.name
);
Run Code Online (Sandbox Code Playgroud)

dbfiddle 上的完整演示!

...或者你总是crtl+f可以一堆¯\_(?)_/¯