2012-09-13 31 views
5

私はSO:XML data type method “value” must be a string literalで読んだことがありますが、私の問題は少し異なります。私は離れて選び、パスが与えられている変数にxmlのビットを持っています。失敗することはもちろん、XMLデータ型メソッド "value"の引数1は文字列リテラルでなければなりません

declare @x xml 
select @x = '....' 
select @x.value('(' + @path + ')[1]', 'varchar(max)') 

しかし:もともと、私はこれを試してみました。 sql:variableを見つけて試しました。

select @x.value('(sql:variable("@path"))[1]', 'varchar(max)') 

しかし、それは奇妙なことに@path(なぜ?)の値を返します。私はそれを乱してきましたが、それが正しいことをすることはできません。

誰でもいいですか?

+0

実際のSO URLをここで使用してください。質問や回答の一番下にある 'share'リンクを右クリックして' Copy'を選ぶと、これはタイトルで正しくリンクされます(上記のURLに行ったように)。ありがとう。 –

答えて

2

マイクロソフトサイトのwBobの助けを借りて、今私はきれいな解決策を得ました。パフォーマンスは、当然のことながら、文書全体としての懸念は、単一パスのためにマップされますされているが、改善は読者:)

if object_id('VMConfigVal') is not null 
drop function VMConfigVal 
go 
create function VMConfigVal(@x xml, @path varchar(max)) 
returns nvarchar(max) 
as 
begin 
    declare @ret nvarchar(max) 

    ;with cte as 
    (
    select value = x.c.value('.', 'varchar(50)') 
    ,  path = cast (null as varchar(max)) 
    ,  node = x.c.query('.') 
    from @x.nodes('/*') x(c) 
    union all 
    select n.c.value('.', 'varchar(50)') 
    ,  isnull(c.path + '/', '/') 
     +  n.c.value('local-name(.)', 'varchar(max)') 
    ,  n.c.query('*') 
    from cte c 
    cross apply c.node.nodes('*') n(c) 
    ) 
    select @ret = value from cte where path = @path 
    return @ret 
    end 
go 

ので、私は今のような何かを行うことができますのための提案の可能性として残されています。

select dbo.VMConfigVal(MyXMLConfig, '/hardware/devices/IDE/ChannelCount') 
from someTable 

甘い!

3

sql:variable()がリテラル値を返すので、selectは@pathの値を返します。実際には、SQLサーバーにリテラル値@pathをドキュメントから選択するよう要求しています。私はあなたがそうのように、動的SQLを使用したことになる欲しいものをやっての知っている唯一の方法:

declare @xml xml = ' 
<root> 
    <element attr="test">blah</element> 
</root>'; 

declare @p nvarchar(max) = '(//element/text())[1]'; 
declare @sql nvarchar(max) 
    = 'select @x.value(''' + @p + ''', ''nvarchar(max)'')'; 

exec sp_executesql @sql, @parameters = N'@x xml', @x = @xml; 

しかし、私は、これは非常に良い練習(などの入力を検証し、SQLインジェクション、考える)ではないことを警告すべきです

+0

私はUDFの中でこれをしようとしているので、私は運が悪いです。 grrr ...(ありがとう) – ekkis

+0

hmm ...代わりに、私は '/ my/path/mytag [@ someattr = sql:variable(" @ myattr ")]'のようなことをすることができます。タグ内の属性を探す代わりに、指定されたパス/タグを検索するクエリ? – ekkis

+0

'@ xml.value( '* // [local-name()= sql:variable(" @ elementname ")])[1]'、 'nvarchar(max)')'を選択すると、 '@elementname = 'element''で置き換えます。たぶん、いくつかのハッキングであなたはそれをパスで調べることができますが、私はあなたに詳細を知らずに確実に伝えることはできません。 –

1

あなたはここでしかリテラルのXPathから名前で子要素を見つけて、抽象的な名前を付ける必要がある場合は、いくつかのオプションがあります

// Returns the /root/node/element/@Value with @Name contained in @AttributeName SQL variable. 
SELECT @Xml.value('(/root/node/element[@Name=sql:variable("@AttributeName")]/@Value)[1]', 'varchar(100)') 

// Returns the text of the child element of /root/node with the name contained in @ElementName SQL variable. 
SELECT @Xml.value('(/root/node/*[name(.)=sql:variable("@ElementName")]/text())[1]', 'varchar(100)') 

// Searching the xml hierarchy for elements with the name contained in @ElementName and returning the text(). 
SELECT @Xml.value('(//*[name(.)=sql:variable("@ElementName")]/text())[1]', 'varchar(100)') 

あなたが実行する@ElementNameまたは@AttributeName SQL変数を宣言する必要がありますこれら。最初のステートメントをテストしましたが、他の2つのステートメント、FYIを明示的にテストしていません。

+0

しかし、私は要素へのパスをハードコーディングしたくないということでした。 – ekkis

+0

2番目のステートメントで提案されている形式を使用して、local-name()よりname()より良い運がありました。このフォーマットは、クロス・アプリケーションを使用するよりもずっとうれしいです。確かにそうです。 – edhubbell

関連する問題