2012-02-10 78 views
9

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

我的XML看起来是这样的:

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

我想要得到<Word>的价值,所以我通过/Keyword/Word,并设置一个变量:

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

不过,我不知道认为@attribute实际上是插入变量字符串。有没有另一种方法来做到这一点?

+0

我不认为你可以这样做 - SQL XQuery经常使用字面字符串,而且大多数情况下,它们不能用SQL变量替换.... – 2012-02-10 16:53:19

回答

14

这里有几个解决方案适合您。

的样本数据:

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/'[email protected]+')[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/'[email protected]+')[1]', '''')+', ''varchar(max)'')' 
exec sp_executesql @SQL, N'@xml xml', @xml 

是最后的版本使用quotename()安全吗?看看这篇由Erland Sommarskog The Curse and Blessings of Dynamic SQL发表的文章。

报价:

与QUOTENAME()和quotestring()

那么,我们是否有良好的保护 针对SQL注入,因为我们有与参数化的命令?也许。 I 不知道任何注入quotename()或者quotestring()的SQL的注入方式。尽管如此,您正在将用户输入插入到 SQL字符串中,而使用参数化命令则不然。

1

尝试连接字符串。

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 
+1

感谢您的回应。当我这样做时,我得到消息“XML数据类型方法的参数1”值“必须是字符串文字”。因为第一个参数是一个XQuery。 – user1200083 2012-02-10 16:55:20

+1

@ user1200083:该错误消息说明了这一切 - 你必须有一个字符串文字 - 你不能有一个变量。 – 2012-02-10 18:09:37

+0

好的,所以我可以像这样在字面量中使用和sql变量:select xmlPath.value('(*/* [local-name()= sql:variable(“attribute1”)])[1]','varchar (MAX)'),假设attribute1是'关键字'。但是,如果我将attribute1设置为“Keyword/Word”,则不会返回任何结果。它与正斜杠有关,因为选择xmlPath.value('(*/* [local-name()= sql:variable(“attribute1”)]/Word)[1]','varchar(MAX) ')确实有效。 – user1200083 2012-02-10 19:56:22