【问题标题】:Get SQL xml attribute value using variable使用变量获取 SQL xml 属性值
【发布时间】:2012-02-10 16:45:51
【问题描述】:

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

我的 xml 看起来像这样:

<EventSpecificData>
  <Keyword>
    <Word>myWord</Word>
    <Occurences>1</Occurences>
    <Context>context</Context>
  </Keyword>
</EventSpecificData>

我想获取&lt;Word&gt;的值,所以我传入/Keyword/Word并将变量设置为:

set @value = @xmlPath.value('(/EventSpecificData/@attribute)[1]', 'varchar(max)')

但是,我不认为@attribute 实际上是在插入变量字符串。还有其他方法吗?

【问题讨论】:

  • 我不认为你可以这样做 - SQL XQuery 经常使用文字字符串,而且大多数时候,它们不能被 SQL 变量替换......

标签: sql sql-server


【解决方案1】:

这里有几个解决方案。

样本数据:

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 字符串,而使用参数化命令则不需要。

【讨论】:

    【解决方案2】:

    尝试连接字符串。

    set @value = @xmlPath.value('(/EventSpecificData/' + @attribute + ')[1]', 'varchar(max)')
    

    更新答案:

    让我们尝试 CASE'ing 操作。

    SELECT @value = CASE @attribute
            WHEN 'word' THEN [word]
            WHEN 'occurrence' THEN [occurrence]
            WHEN 'context' THEN [context]
            END AS [value]
    FROM
    (
        SELECT x.u.value('(/EventSpecificData/Keyword/Word)[1]', 'varchar(max)') AS [word]
            , x.u.value('(/EventSpecificData/Keyword/Occurrence)[1]', 'varchar(max)') AS [word]
            , x.u.value('(/EventSpecificData/Keyword/Context)[1]', 'varchar(max)') AS [word]
        FROM @xmlPath.nodes('/EventSpecificData') x(u)
    ) a
    

    【讨论】:

    • 感谢您的回复。执行此操作时,我收到消息“XML 数据类型方法“值”的参数 1 必须是字符串文字。”因为第一个参数是一个 XQuery。
    • @user1200083:该错误消息说明了一切 - 你必须有一个字符串文字 - 你不能有一个变量。
    • 好的,所以我可以像这样在文字中使用 sql 变量:select xmlPath.value('(*/*[local-name()=sql:variable("attribute1")]) [1]', 'varchar(MAX)') 假设属性 1 是'关键字'。但是,如果我将属性 1 设置为“关键字/字”,则它不会返回任何结果。它与正斜杠有关,因为 select xmlPath.value('(*/*[local-name()=sql:variable("attribute1")]/Word)[1]', 'varchar(MAX) ') 确实有效。
    猜你喜欢
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 2016-10-27
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 2011-07-17
    • 2016-08-01
    相关资源
    最近更新 更多