2012-02-10 21 views
9

Tengo una función SQL que toma una variable llamada attribute, que es el atributo xml Quiero obtener el valor de. xmlPath es la cadena XML completa.Obtener valor de atributo XML de SQL mediante la variable

Mi XML tiene el siguiente aspecto:

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

Quiero obtener el valor de <Word>, por lo que pase en /Keyword/Word y establecer una variable a:

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

Sin embargo, no lo creo think @attribute en realidad está insertando la cadena de variables. Hay otra manera de hacer esto?

+0

No creo que pueda hacer eso: el SQL XQuery funciona con cadenas literales con bastante frecuencia, y la mayoría de las veces, no se pueden reemplazar con variables de SQL .... –

Respuesta

14

Aquí hay un par de soluciones para usted.

datos de la muestra:

declare @xml xml 
set @xml = 
'<EventSpecificData> 
    <Keyword> 
    <Word>myWord</Word> 
    <Occurences>1</Occurences> 
    <Context>context</Context> 
    </Keyword> 
</EventSpecificData>' 

Obtener el primer valor de nodo denominado Palabra independientemente de los padres. // utilizar para realizar una búsqueda profunda y utilizar local-name() para que coincida con el nombre del nodo.

declare @Attribute varchar(max) 

set @Attribute = 'Word' 
select @xml.value('(//*[local-name() = sql:variable("@Attribute")])[1]', 'varchar(max)') 

Proporcionar padre nombre de nodo y atributo en variables separadas utilizando local-name() en dos niveles.

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

Dado que el parámetro a nodes tiene que ser una cadena literal que invita a utilizar SQL dinámico para resolver esto. Podría parecerse a esto para que funcione con su contenido variable original.

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 

Pero usted debe ser consciente de que si utiliza esta usted es vulnerable a ataques de inyección SQL. Algunas usuario final retorcido podría llegar a una cadena del atributo que tiene este aspecto:

set @Attribute = 'Keyword/Word)[1]'', ''varchar(max)'') select @@version --' 

La ejecución del SQL dinámico con que le dará dos conjuntos de resultados. El select @@version es sólo allí para mostrar algo de código inofensiva, pero podría ser mucho peor cosas allí.

Puede utilizar quotename() para evitar el ataque de inyección SQL. Al menos evitará el intento hecho por mí.

set @Attribute = 'Keyword/Word' 
set @SQL = 'select @xml.value('+quotename('(/EventSpecificData/'[email protected]+')[1]', '''')+', ''varchar(max)'')' 
exec sp_executesql @SQL, N'@xml xml', @xml 

es la última versión utilizando quotename() seguro? Echa un vistazo a este artículo de Erland Sommarskog The Curse and Blessings of Dynamic SQL.

Cita:

Así que con QUOTENAME() y quotestring(), tenemos que hacer como una buena protección contra la inyección de SQL como lo hemos hecho con los comandos parametrizados? Tal vez. I no sé de ninguna forma de inyectar SQL que se deslice a través de quotename() o quotetring(). Sin embargo, está interpolando la entrada del usuario en la cadena SQL , mientras que con los comandos parametrizados, no lo hace.

1

Trate concatenación de la cadena.

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

respuesta Actualizado:

Probemos CASE'ing la operación.

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

Gracias por la respuesta. Cuando hago esto, aparece el mensaje "El argumento 1 del método XML tipo de datos" valor "debe ser un literal de cadena". ya que el primer argumento es un XQuery. – user1200083

+1

@ user1200083: ese mensaje de error lo dice todo, debe tener un literal de cadena, no puede tener una variable. –

+0

Ok, entonces puedo usar la variable sql en el literal como este: select xmlPath.value ('(*/* [local-name() = sql: variable ("attribute1")]) [1]', 'varchar (MAX) ') suponiendo que el atributo1 es' Palabra clave '. Sin embargo, si configuro attribute1 en 'Keyword/Word', entonces no devuelve ningún resultado. Tiene algo que ver con las barras diagonales, ya que selecciona xmlPath.value ('(*/* [local-name() = sql: variable ("attribute1")]/Word) [1]', 'varchar (MAX) ') funciona. – user1200083

Cuestiones relacionadas