2012-02-10 21 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