这里有几个解决方案。
样本数据:
declare @xml xml
set @xml =
'<EventSpecificData>
<Keyword>
<Word>myWord</Word>
<Occurences>1</Occurences>
<Context>context</Context>
</Keyword>
</EventSpecificData>'
从名为 Word 的节点获取第一个值,而不考虑父节点。使用//进行深度搜索,使用local-name()匹配节点名称。
declare @Attribute varchar(max)
set @Attribute = 'Word'
select @xml.value('(//*[local-name() = sql:variable("@Attribute")])[1]', 'varchar(max)')
在两个级别使用local-name()在单独的变量中提供父节点名称和属性。
declare @Node varchar(max)
declare @Attribute varchar(max)
set @Attribute = 'Word'
set @Node = 'Keyword'
select @xml.value('(/EventSpecificData
/*[local-name() = sql:variable("@Node")]
/*[local-name() = sql:variable("@Attribute")])[1]', 'varchar(max)')
由于nodes 的参数必须是字符串文字,它邀请使用动态sql 来解决这个问题。它可能看起来像这样,以使其与您的原始变量内容一起使用。
set @Attribute = 'Keyword/Word'
declare @SQL nvarchar(max)
set @SQL = 'select @xml.value(''(/EventSpecificData/'+@Attribute+')[1]'', ''varchar(max)'')'
exec sp_executesql @SQL, N'@xml xml', @xml
但是你应该知道,如果你使用这个,你很容易受到 SQL 注入攻击。一些狡猾的最终用户可能会想出一个如下所示的属性字符串:
set @Attribute = 'Keyword/Word)[1]'', ''varchar(max)'') select @@version --'
使用它执行动态 SQL 将为您提供两个结果集。 select @@version 只是用来显示一些无害的代码,但其中可能会更糟糕。
您可以使用quotename() 来防止SQL 注入攻击。它至少会阻止我的尝试。
set @Attribute = 'Keyword/Word'
set @SQL = 'select @xml.value('+quotename('(/EventSpecificData/'+@Attribute+')[1]', '''')+', ''varchar(max)'')'
exec sp_executesql @SQL, N'@xml xml', @xml
最后一个版本是使用quotename()safe 吗?看看 Erland Sommarskog The Curse and Blessings of Dynamic SQL 的这篇文章。
引用:
那么使用quotename() 和quotestring(),我们有同样好的保护吗?
像我们使用参数化命令一样反对 SQL 注入?也许。一世
不知道有什么方法可以注入通过 quotename() 或
引号()。尽管如此,您将用户输入插入到
SQL 字符串,而使用参数化命令则不需要。