Проблема округления в SQL Server Требуется объяснение - PullRequest
1 голос
/ 12 января 2010

Я решил эту проблему, но мне просто интересно, почему это работает так, как работает. У меня есть временная таблица, из которой я выбираю, и я хочу отобразить имя, количество записей, соответствующих этому имени, и процентную долю этого имени от общего количества записей. У меня так было изначально:

SELECT name, number,  
CASE WHEN number = 0 THEN 0 ELSE
convert(Numeric(10,2), number / CONVERT(decimal(5,2),SUM(number)) * 100)
END as "Percentage of Total" 
FROM #names 
group by name, number

Результаты, которые я получил, были:

name                      number      Percentage of Total
------------------------- ----------- ---------------------------------------
Test 1                      0           0.00
Test 2                     22          100.00
Test 3                     28          100.00

Когда я изменяю запрос на это, результаты верны:

    declare @total decimal(5,2)

    select @total = SUM(number) FROM #names

    SELECT name, number, convert(Numeric(10,2), number/ @total * 100) as "Percentage of Total"  
    FROM #names
    group by name, number

Правильные результаты:

name                      number      Percentage of Total
------------------------- ----------- ---------------------------------------
Test 1                     22          44.00
Test 2                      0           0.00
Test 3                     28          56.00

Может кто-нибудь объяснить, что происходит, я хотел бы понять это лучше. Спасибо!

Jon

1 Ответ

1 голос
/ 12 января 2010

Вы первый запрос групп по номеру.

Поскольку у вас нет дубликатов чисел, number / SUM(number) эквивалентно 1 / COUNT (кроме случаев, когда число равно 0).

Ваш второй запрос не группирует по номеру, он вычисляет общую сумму.

Используйте это вместо:

SELECT  name, number * 100.0 / SUM(number) OVER ()
FROM    #names

При использовании с предложением OVER, SUM становится аналитической функцией, а не агрегатной.

Он не сокращает несколько записей в одну: вместо этого он возвращает общее значение вместе с каждой записью:

-- This is an aggregate function. It shrinks all records into one record and returns the total sum

WITH    q (name, number) AS
        (
        SELECT  'test1', 0
        UNION ALL
        SELECT  'test2', 22
        UNION ALL
        SELECT  'test3', 28
        )
SELECT  SUM(number)
FROM    q

--
50

-- This is an analytical function. It calcuates the total sum as well but does not shrink the records.

WITH    q (name, number) AS
        (
        SELECT  'test1', 0
        UNION ALL
        SELECT  'test2', 22
        UNION ALL
        SELECT  'test3', 28
        )
SELECT  SUM(number) OVER ()
FROM    q

--
50
50
50
...