在 values 方法中使用 xpath 中的 sql:column 函数
Using sql:column function in xpath in values method
我有两个 table:
- 一个名为 @settings 的 xml 值
- 另一个名为@nodesToFind 的节点列表要从第一个 table
中的 xml 值中提取
我想获取每个 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
如您所见,由于存在多个可能的节点级别,因此它变得更加复杂(并且可能更慢)。如果您可以将其限制为仅一级节点,则可以删除 if
else
部分。
我有两个 table:
- 一个名为 @settings 的 xml 值
- 另一个名为@nodesToFind 的节点列表要从第一个 table 中的 xml 值中提取
我想获取每个 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
如您所见,由于存在多个可能的节点级别,因此它变得更加复杂(并且可能更慢)。如果您可以将其限制为仅一级节点,则可以删除 if
else
部分。