在 values 方法中使用 xpath 中的 sql:column 函数

Using sql:column function in xpath in values method

我有两个 table:

我想获取每个 RowId 的每个 NodePath 的值列表。

此查询在“设置”列的值方法的 xpath 中使用 sql:column 函数,但它 returns NodePath 本身而不是值:

declare @settings table (RowId int identity, Settings xml)
insert @settings (Settings) values ('<settings><Settings1><Settings1a><Setting1a1>1-1a1</Setting1a1></Settings1a><Setting1b>1-1b</Setting1b><Setting1c>1-1c</Setting1c></Settings1><Settings2><Setting2a>1-2a</Setting2a></Settings2></settings>')
insert @settings (Settings) values ('<settings><Settings1><Settings1a><Setting1a1>2-1a1</Setting1a1></Settings1a><Setting1b>2-1b</Setting1b><Setting1c>2-1c</Setting1c></Settings1><Settings2><Setting2a>2-2a</Setting2a></Settings2></settings>')
insert @settings (Settings) values ('<settings><Settings1><Settings1a><Setting1a1>3-1a1</Setting1a1></Settings1a><Setting1b>3-1b</Setting1b><Setting1c>3-1c</Setting1c></Settings1><Settings2><Setting2a>3-2a</Setting2a></Settings2></settings>')

declare @nodesToFind table (NodePath varchar(max))
insert @nodesToFind (NodePath) values ('/Settings/Settings1/Settings1a/Setting1a1')
insert @nodesToFind (NodePath) values ('/Settings/Settings1/Setting1b')
insert @nodesToFind (NodePath) values ('/Settings/Settings1/Setting1c')
insert @nodesToFind (NodePath) values ('/Settings/Settings2/Setting2a')

select
    S.RowId, 
    NTF.NodePath,
    S.Settings.value('(sql:column("NodePath"))[1]', 'varchar(max)')
from @settings S
cross apply @nodesToFind NTF

结果是这样的:

RowId  NodePath                                   Value
-----  -----------------------------------------  -----------------------------------------
1      /Settings/Settings1/Settings1a/Setting1a1  /Settings/Settings1/Settings1a/Setting1a1
2      /Settings/Settings1/Settings1a/Setting1a1  /Settings/Settings1/Settings1a/Setting1a1
3      /Settings/Settings1/Settings1a/Setting1a1  /Settings/Settings1/Settings1a/Setting1a1
1      /Settings/Settings1/Setting1b              /Settings/Settings1/Setting1b
2      /Settings/Settings1/Setting1b              /Settings/Settings1/Setting1b
3      /Settings/Settings1/Setting1b              /Settings/Settings1/Setting1b
1      /Settings/Settings1/Setting1c              /Settings/Settings1/Setting1c
2      /Settings/Settings1/Setting1c              /Settings/Settings1/Setting1c
3      /Settings/Settings1/Setting1c              /Settings/Settings1/Setting1c
1      /Settings/Settings2/Setting2a              /Settings/Settings2/Setting2a
2      /Settings/Settings2/Setting2a              /Settings/Settings2/Setting2a
3      /Settings/Settings2/Setting2a              /Settings/Settings2/Setting2a

S.Settings.value('(sql:column("NodePath"))[1]', 'varchar(max)') 行有什么问题?

您尝试使用的 XQuery 实际上是 运行 动态的。遗憾的是,您不能在 SQL 服务器中使用动态 XQuery。每个 XQuery 必须 是静态的。

在你的特定情况下你可以做的是将每个节点谓词分解成单独的列。然后在 XQuery 中,您可以通过检查每一列来下降到相关节点。例如:

declare @settings table (RowId int identity, Settings xml)
insert @settings (Settings) values ('<settings><Settings1><Settings1a><Setting1a1>1-1a1</Setting1a1></Settings1a><Setting1b>1-1b</Setting1b><Setting1c>1-1c</Setting1c></Settings1><Settings2><Setting2a>1-2a</Setting2a></Settings2></settings>')
insert @settings (Settings) values ('<settings><Settings1><Settings1a><Setting1a1>2-1a1</Setting1a1></Settings1a><Setting1b>2-1b</Setting1b><Setting1c>2-1c</Setting1c></Settings1><Settings2><Setting2a>2-2a</Setting2a></Settings2></settings>')
insert @settings (Settings) values ('<settings><Settings1><Settings1a><Setting1a1>3-1a1</Setting1a1></Settings1a><Setting1b>3-1b</Setting1b><Setting1c>3-1c</Setting1c></Settings1><Settings2><Setting2a>3-2a</Setting2a></Settings2></settings>')

declare @nodesToFind table (NodePath1 nvarchar(max), NodePath2 nvarchar(max), NodePath3 nvarchar(max), NodePath4 nvarchar(max))
insert @nodesToFind (NodePath1, NodePath2, NodePath3, NodePath4) values ('settings','Settings1','Settings1a','Setting1a1')
insert @nodesToFind (NodePath1, NodePath2, NodePath3, NodePath4) values ('settings','Settings1','Setting1b',null)
insert @nodesToFind (NodePath1, NodePath2, NodePath3, NodePath4) values ('settings','Settings1','Setting1c',null)
insert @nodesToFind (NodePath1, NodePath2, NodePath3, NodePath4) values ('settings','Settings2','Setting2a',null)

select
    S.RowId, 
    NTF.*,
    S.Settings.value('((
        for $i1 in *[local-name() = sql:column("NodePath1")]
        return 
            if (empty(sql:column("NodePath2")))
            then $i1
            else for $i2 in ($i1/*[local-name() = sql:column("NodePath2")])
                 return
                     if (empty(sql:column("NodePath3")))
                     then $i2
                     else for $i3 in $i2/*[local-name() = sql:column("NodePath3")]
                          return
                              if (empty(sql:column("NodePath4")))
                              then $i3
                              else $i3/*[local-name() = sql:column("NodePath4")]
    )/text())[1]', 'varchar(max)')
from @settings S
cross join @nodesToFind NTF

db<>fiddle

如您所见,由于存在多个可能的节点级别,因此它变得更加复杂(并且可能更慢)。如果您可以将其限制为仅一级节点,则可以删除 if else 部分。