XQuery в SQL-сервере делает SUM над нулевым значением - PullRequest
11 голосов
/ 11 мая 2009

Я пытаюсь извлечь денежные суммы, хранящиеся в некоторых плохо отформатированных столбцах xml (для столбца XML не определена схема, которая, я думаю, является частью проблемы) Я получаю ошибку преобразования всякий раз, когда сталкиваюсь с узлом с 0 в качестве значения.

Пример:

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

дает сумму 3, а:

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

выдает ошибку: «Ошибка преобразования типа данных nvarchar в числовой.»

Есть идеи, как сделать так, чтобы мой запрос возвращал 0 при суммировании списка узлов с нулевым значением?

Ответы [ 3 ]

7 голосов
/ 11 мая 2009

Ваш комментарий предлагает ответ на вашу проблему.

Вместо преобразования в числовое значение преобразуйте в число с плавающей точкой Научные обозначения будут преобразованы в число с плавающей точкой.

4 голосов
/ 01 июля 2010

Вы также можете использовать и если утверждение, как это:

@x.value('if (sum(/List/value) = 0) then 0 else sum(/List/value)', 'numeric')
3 голосов
/ 13 августа 2012

Я наткнулся на этот вопрос и, в конечном счете, на ответ, глядя на аналогичную проблему с целыми числами. Несмотря на задержку с момента последнего ответа, я добавляю сюда, если это поможет кому-то еще в будущем.

Сначала ваш основной ответ:

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

В XQuery вы можете преобразовать значение в стандартный тип XML-схемы, который затем будет корректно обрабатываться SQL Server.

ОБРАТИТЕ ВНИМАНИЕ: по умолчанию «числовой» в SQL Server не имеет десятичных разрядов (масштаб «0»)! Вы, вероятно, намеревались сделать что-то вроде:

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

(вы не можете заставить SQL Server выводить точность или масштаб из значения, возвращенного из XML, вы должны явно указать его)

Наконец, фактическая проблема, которую мне лично нужно было решить, была почти такой же, за исключением того, что я имел дело с целыми числами, которые также борются с представлением "0" в xml double значений:

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

ОБНОВЛЕНИЕ: Проблема с решением для обработки десятичных чисел, которое я выложил выше (преобразование в десятичное число в XQuery до того, как SQL получит синтаксический анализ значения), заключается в том, что агрегирование фактически происходит с (предполагаемый / предполагаемый) тип данных с плавающей точкой (двойной). Если значения, которые вы сохранили в своем XML-файле, требуют высокой степени точности, это может быть неправильным решением - агрегирование с плавающей запятой может фактически привести к потере данных. Например, здесь мы теряем последнюю цифру суммируемого нами числа:

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

(выходит "2000000000000000000000000000", что неверно)

Эта проблема в равной степени относится и к другим подходам, предлагаемым здесь, таким как явное чтение значения как «float» в T-SQL.

Чтобы избежать этого, вот последний вариант, использующий выражение XQuery FLWOR для установки типа данных перед операцией агрегирования. В этом случае агрегация происходит правильно, и мы имеем правильное суммированное значение (при этом также обрабатывая значения «0», если / когда они происходят):

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

(выходит "2000000000000000000000000002", правильное значение)

...