Redshift числовое прецизионное усечение - PullRequest
0 голосов
/ 01 октября 2018

Я столкнулся с ситуацией, когда не могу объяснить, как Redshift обрабатывает деление сумм.

Есть пример таблицы:

create table public.datatype_test(
a numeric(19,6),
b numeric(19,6));
insert into public.datatype_test values(222222.2222, 333333.3333);
insert into public.datatype_test values(444444.4444, 666666.6666);

Теперь я пытаюсь выполнить запрос:

select sum(a)/sum(b) from public.datatype_test;

Я получаю результат 0,6666 (4 знака после запятой).Он не связан с отображением инструмента, он действительно возвращает только 4 знака после запятой, и не имеет значения, большие или маленькие цифры в таблице.В моем случае 4 десятичных знака недостаточно точны.То же самое верно, если я использую AVG вместо SUM.

Если я использую MAX вместо SUM, я получаю: 0.6666666666666666666 (19 десятичных знаков).

Он также возвращает правильный результат (0.6666666666666667), если нетиспользуется физическая таблица:

with t as (
select 222222.2222::numeric(19,6) as a, 333333.3333::numeric(19,6) as b union all 
select 444444.4444::numeric(19,6) as a, 666666.6666::numeric(19,6) as b
)
select sum(a)/sum(b) as d from t; 

Я изучил документацию Redshift о SUM и вычислениях с числовыми значениями , но я все еще не получаю результат в соответствии сдокументация.

Использование типа данных float для столбцов таблицы не вариант, так как мне нужно хранить точные суммы в валюте, и 15 значащих цифр недостаточно.

Использование приведения к агрегации SUM также дает 0,6666666666666666666 (19десятичные дроби).

select sum(a)::numeric(19,6)/sum(b) from public.datatype_test;

Но это выглядит неправильно, и я не могу заставить инструменты BI делать этот обходной путь, также все, кто использует эти данные, не должны использовать этот тип обходного пути.

Я пытался использовать тот же тест в PostgreSQL 10, и он работает, как и должно, возвращая достаточное количество десятичных знаков для деления.

Могу ли я что-нибудьделать с настройкой базы данных, чтобы избежать приведения в SQL Query?Любые советы или рекомендации приветствуются.

Версия Redshift: PostgreSQL 8.0.2 для i686-pc-linux-gnu, скомпилированная GCC GCC (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.4081 Использование больших узлов dc2.8x

1 Ответ

0 голосов
/ 05 апреля 2019

Я столкнулся с подобными проблемами, и хотя у меня нет решения, которое не требует обходного пути, я могу хотя бы объяснить его.

Точность / масштаб результата деленияопределяется правилами в документе «Вычисления с числовыми значениями».

Следствием этих правил является то, что decimal(19,6), разделенное на другое decimal(19,6), вернет decimal(38,19).

Что с вами происходит, так это то, что MAX возвращает то же самоеточность / масштаб в качестве основного столбца, но SUM возвращает decimal(38,*), несмотря ни на что.(Это, вероятно, мера предосторожности для предотвращения переполнения сумм «больших данных»).Если вы поделите decimal(38,6) на другое, вы получите decimal(38,4).

Поддержка AWS, вероятно, не сочтет это дефектом - не существует стандарта SQL для обработки десятичной точности деления, и учитывая, что этоэто задокументированное поведение, это, вероятно, преднамеренное решение.

Единственный способ решить эту проблему - ввести числитель или умножить его на что-то вроде sum(a) * cast(1 as decimal(10,9)), которое является переносимым SQL и приведет к увеличению числа десятичных знаков в числителе.и, таким образом, результат.

Для удобства я сделал калькулятор в JSFiddle с правилами , чтобы вы могли поиграть с различными вариантами:

scale = Math.max(4, s1 + p2 - s2 + 1)
precision = p1 - s1 + s2 + scale

if (precision > 38) {
    scale = Math.max((38 + scale - precision), 4)
    precision = 38
}
...