使用变量获取SQL xml属性值

use*_*083 9 sql sql-server

我有一个SQL函数,它接受一个名为的变量attribute,这是我想从中获取值的xml属性.xmlPath是完整的XML字符串.

我的xml看起来像这样:

<EventSpecificData>
  <Keyword>
    <Word>myWord</Word>
    <Occurences>1</Occurences>
    <Context>context</Context>
  </Keyword>
</EventSpecificData>
Run Code Online (Sandbox Code Playgroud)

我想获取值<Word>,所以我传入/Keyword/Word并设置一个变量:

set @value = @xmlPath.value('(/EventSpecificData/@attribute)[1]', 'varchar(max)')
Run Code Online (Sandbox Code Playgroud)

但是,我不认为@attribute实际上是插入变量字符串.还有另一种方法吗?

Mik*_*son 15

以下是一些适合您的解决方案.

样本数据:

declare @xml xml
set @xml = 
'<EventSpecificData>
  <Keyword>
    <Word>myWord</Word>
    <Occurences>1</Occurences>
    <Context>context</Context>
  </Keyword>
</EventSpecificData>'
Run Code Online (Sandbox Code Playgroud)

无论父母如何,从名为Word的节点获取第一个值.用于//进行深度搜索并使用local-name()以匹配节点名称.

declare @Attribute varchar(max)

set @Attribute = 'Word'
select @xml.value('(//*[local-name() = sql:variable("@Attribute")])[1]',  'varchar(max)')
Run Code Online (Sandbox Code Playgroud)

使用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)')
Run Code Online (Sandbox Code Playgroud)

由于参数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
Run Code Online (Sandbox Code Playgroud)

但是你应该知道,如果你使用它,你会对SQL注入攻击持开放态度.一些狡猾的最终用户可能会想出一个如下所示的属性字符串:

set @Attribute = 'Keyword/Word)[1]'', ''varchar(max)'') select @@version --'
Run Code Online (Sandbox Code Playgroud)

使用它执行动态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
Run Code Online (Sandbox Code Playgroud)

最后一个版本使用quotename()安全吗?看看Erland Sommarskog 这篇文章动态SQL的诅咒和祝福.

引用:

因此,使用quotename()和quotestring(),我们是否可以像使用参数化命令一样保护SQL注入?也许.我不知道有任何方法可以通过quotename()或quotestring()来填充SQL.然而,您正在将用户输入插入到SQL字符串中,而使用参数化命令则不会.