2012-09-13 7 views
5

Ich habe durch SO gelesen: XML data type method “value” must be a string literal aber mein Problem ist ein bisschen anders. Ich habe ein bisschen xml in einer Variable, die ich auseinander nehmen möchte und mir einen Pfad gegeben habe. ursprünglich habe ich das versucht:Das Argument 1 der XML-Datentyp Methode "Wert" muss ein String-Literal sein

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

aber natürlich schlägt das fehl. dann habe ich die sql: variable gefunden und folgendes versucht:

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

aber das bringt seltsamerweise den Wert von @path zurück (warum?). Ich habe mich damit herumgeschlagen, kann es aber nicht richtig machen.

Wer denkt an jemanden?

+0

Bitte verwenden Sie die tatsächlichen SO-URLs hier. Sie können mit der rechten Maustaste auf den "share" -Link am Ende jeder Frage oder Antwort klicken und "Copy" wählen, und es wird korrekt nach Titel verknüpft (wie ich es mit Ihrer URL oben getan habe). Vielen Dank. –

Antwort

2

mit Hilfe von wBob auf der Microsoft-Website, habe ich jetzt eine saubere Lösung. Die Leistung wird, natürlich ein Anliegen wie das gesamte Dokument wird im Interesse eines einzelnen Pfades erhalten abgebildet, aber Verbesserungen sind als Vorschlag Möglichkeiten für die Leser überlassen :)

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 

so kann ich jetzt wie etwas tun:

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

süß!

3

Ihre Auswahl gibt den Wert @path zurück, da sql:variable() einen Literalwert zurückgibt, so dass Sie SQL Server bitten, den Literalwert @path aus dem Dokument auszuwählen, was er tut. Der einzige Weg, ich weiß, zu tun, was Sie wollen, wäre mit dynamischem SQL, etwa so:

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; 

Aber ich sollte Sie warnen, dass dies nicht eine sehr gute Übung (man denke etwa SQL-Injektionen, die Überprüfung von Eingabe, etc.)

+0

Ich habe kein Glück, weil ich versuche, dies innerhalb einer UDF zu tun. grrr ... (danke) – ekkis

+0

hmm ... alternativ kann ich etwas wie '/ my/pfad/mytag [@ something = sql: variable (" @ myattr ")]' ... so wie könnte ich ein erstellen Abfrage, die nach einem bestimmten Pfad/Tag sucht, anstatt nach einem Attribut im Tag zu suchen? – ekkis

+0

Sie können dies tun: wählen Sie '@ xml.value ('(// * [local-name() = sql: variable (" @ elementname ")]) [1]', 'nvarchar (max)')', mit '@elementname = 'element''. Vielleicht kannst du es mit etwas Hacking auf den Pfad bringen, aber ich kann es dir nicht sagen, ohne weitere Details zu kennen. –

1

Wenn Sie nur ein untergeordnetes Element mit Namen und wollen abstrakten Namen aus dem XPath wörtlichen finden müssen hier sind einige Optionen:

// 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)') 

Sie benötigen @ElementName oder @AttributeName SQL Variable zu deklarieren, laufen diese. Ich habe die erste Aussage getestet, habe aber die anderen 2 Aussagen, FYI, nicht explizit getestet.

+0

aber der Punkt war, dass ich den Pfad zu dem Element – ekkis

+0

nicht hart-codieren wollte Ich hatte mehr Glück mit local-name() als name (.) Mit dem Format in der 2. Anweisung vorgeschlagen. Dieses Format ist eine ganze Menge schöner und sauberer als Cross-Apply, das ist sicher. – edhubbell

Verwandte Themen