2009-05-11 50 views
11

Estoy tratando de extraer sumas monetarias almacenadas en algunas columnas xml de formato pobre (no hay un esquema definido para la columna XML que supongo es parte del problema). Recibo un error de conversión cada vez que encuentro un nodo con 0 como valor.XQuery en SQL Server haciendo SUM sobre valor cero

Ejemplo:

select xml.value('sum(/List/value)', 'numeric') sum 
from (select cast('<List><value>1</value><value>2</value></List>' as xml) xml) a 

da la suma 3 mientras que:

select xml.value('sum(/List/value)', 'numeric') sum 
from (select cast('<List><value>0</value><value>0</value></List>' as xml) xml) a 

plantea el error: "Error que convierte el tipo de datos nvarchar a numérico".

¿Alguna idea de cómo puedo hacer que mi consulta devuelva 0 al resumir una lista de nodos de valor cero?

+3

Parece que sum (/ List/value) devuelve un flotante "0.0E0" cuando la suma es cero y ese número no se puede convertir a numérico. ¿Alguna idea de por qué veo este comportamiento? –

Respuesta

6

Su comentario sugiere una respuesta a su problema.

En lugar de convertir a numérico, conviértalo en flotante. La notación científica se convertirá en flotación.

3

también se puede utilizar y si esta afirmación como:

@x.value('if (sum(/List/value) = 0) then 0 else sum(/List/value)', 'numeric') 
3

yo me encontré con esta pregunta, y en última instancia una respuesta, mientras mira a un problema similar con números enteros. A pesar de la demora desde la última respuesta, agregaré aquí en caso de que ayude a alguien más en el futuro.

En primer lugar su respuesta básica:

select xml.value('xs:decimal(sum(/List/value))', 'numeric') sum 
from (select cast('<List><value>0</value><value>0</value></List>' as xml) xml) a 

En XQuery se puede convertir el valor a un tipo de esquema XML estándar, que luego se maneja correctamente por SQL Server.

TEN EN CUENTA: el valor "numérico" predeterminado en SQL Server no tiene ningún lugar decimal (escala de "0")! Es probable que la intención de hacer algo más como:

select xml.value('xs:decimal(sum(/List/value))', 'numeric(20,5))') sum 
from (select cast('<List><value>0</value><value>0</value></List>' as xml) xml) a 

(no se puede obtener SQL Server para inferir la precisión o escala desde el valor devuelto por el XML, debe especificar explícitamente)

Por último, el actual tema que en lo personal necesario para hacer frente era casi exactamente lo mismo, excepto que estaba tratando con números enteros, que también luchan con la representación XML de "0" double valores:

select xml.value('xs:int(sum(/List/value))', 'int') sum 
from (select cast('<List><value>0</value><value>0</value></List>' as xml) xml) a 

ACTUALIZACIÓN: El problema con la solución de manejo decimal que publiqué arriba (convirtiendo a decimal en XQuery antes de que SQL llegue a analizar el valor) es que la agregación realmente ocurre con el tipo de datos flotante (doble) asumido/inferido. Si los valores que ha almacenado en su Xml requieren un alto grado de precisión, esto puede ser lo que no debe hacer: la agregación de coma flotante puede resultar en una pérdida de datos. EG aquí perdemos el último dígito del número que estamos sumando:

select xml.value('xs:decimal(sum(/List/value))', 'numeric(28, 0)') sum 
from (select cast('<List> 
     <value>1000000000000000000000000001</value> 
     <value>1000000000000000000000000001</value> 
    </List>' as xml) xml) a 

(que sale a "2000000000000000000000000000", lo que está mal)

Este problema se aplica igualmente a otros enfoques que aquí se ofrecen, tales como explícitamente leyendo el valor como "flotante" en T-SQL.

Para evitar esto, esta es una opción final que utiliza una expresión XQuery FLWOR para establecer el tipo de datos antes de la operación de agregación. En este caso, la agregación se produce correctamente, y tenemos el valor sumado correcta (a la vez que el manejo de los valores "0" si/cuando se producen):

select xml.value('sum(for $r in /List/value return xs:decimal($r))', 'numeric(28, 0)') sum 
from (select cast('<List> 
     <value>1000000000000000000000000001</value> 
     <value>1000000000000000000000000001</value> 
    </List>' as xml) xml) a 

(sale a "2000000000000000000000000002", el valor correcto)

+0

Me gusta el método aquí de usar XQuery para convertir el valor a un tipo de esquema XML estándar. Eso era exactamente lo que estaba buscando, y prefiero eso en lugar de convertirlo en una carroza, y luego volver a otra cosa. Es un poco más explícito, pero puedo evitar moverlo a un tipo y luego a otro, y creo que eso es más claro. – mttjohnson

Cuestiones relacionadas