2009-05-11 8 views
11

J'essaie d'extraire des sommes monétaires stockées dans des colonnes xml mal formées (il n'y a pas de schéma défini pour la colonne XML qui, je suppose, fait partie du problème). J'obtiens une erreur de conversion chaque fois que je rencontre un noeud avec 0 comme valeur.XQuery dans un serveur SQL faisant SUM sur une valeur nulle

Exemple:

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

donne la somme 3 alors:

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

soulève l'erreur: "Erreur de conversion type de données nvarchar à numérique."

Toute idée de comment je peux rendre ma requête retourner 0 lors de la synthèse d'une liste de nœuds de valeur nulle?

+3

Il semble que sum (/ List/value) renvoie un flottant "0.0E0" lorsque la somme est nulle et que ce nombre ne peut pas être converti en numérique. Une idée de pourquoi je vois ce comportement? –

Répondre

6

Votre commentaire suggère une réponse à votre problème.

Au lieu de convertir en numérique, convertir en flottant. La notation scientifique se convertira en float.

3

vous pouvez également utiliser et si la déclaration comme ceci:

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

je suis tombé sur cette question, et finalement une réponse, tout en regardant un problème similaire avec des nombres entiers. Malgré le retard depuis la dernière réponse, j'ajoute ici au cas où cela aiderait quelqu'un d'autre à l'avenir.

Première votre réponse de base:

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 vous pouvez jeter la valeur à un standard XML type de schéma, qui sera ensuite traitée correctement par SQL Server. S'il vous plaît noter: le "numérique" par défaut dans SQL Server n'a pas de décimales (échelle de "0")! Vous avez sans doute l'intention de faire quelque chose comme:

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 

(vous ne pouvez pas obtenir SQL Server pour déduire la précision ou l'échelle de la valeur renvoyée par la Xml, vous devez spécifier explicitement)

Enfin, le réel question que je personnellement besoin d'adresse était presque exactement la même chose, sauf que je traitais avec des nombres entiers, qui supportent mal les représentation XML de « 0 » double valeurs:

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 

MISE à JOUR: Le problème avec la solution de gestion des décimales que j'ai posté plus haut (conversion en décimal dans XQuery avant que SQL ne parvienne à analyser la valeur) est que l'agrégation se produit avec le type de données (supposé/inféré) à virgule flottante (double). Si les valeurs que vous avez stockées dans votre XML requièrent un haut degré de précision, cela peut être une mauvaise chose à faire - l'agrégation à virgule flottante peut en fait entraîner une perte de données. EG ici, nous perdons le dernier chiffre du numéro nous sommateur:

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 

(sort à « 2000000000000000000000000000 », ce qui est faux)

Cette question vaut également pour d'autres approches proposées ici, comme explicitement lire la valeur "float" dans T-SQL.

Pour éviter cela, voici une dernière option utilisant une expression XQuery FLWOR pour définir le type de données avant l'opération d'agrégation. Dans ce cas, l'agrégation se produit correctement, et nous avons la valeur additionnée correcte (tout en traitant également « 0 » valeurs si/quand ils se produisent):

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 

(SORT à « 2000000000000000000000000002 », la valeur correcte)

+0

J'aime la méthode ici d'utiliser XQuery pour convertir la valeur en un type de schéma XML standard. C'était exactement ce que je cherchais, et je préférais le convertir en un flotteur, puis revenir à autre chose. C'est un peu plus explicite, mais je peux éviter de passer à un type, puis à un autre, et je pense que c'est plus clair. – mttjohnson

Questions connexes