Redshift numeric precision truncating

I have encountered situation that I can't explain how Redshift handles division of SUMs.

There is example table:

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); 

Now I try to run query:

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

I get result 0.6666 (4 decimals). It is not related to tool display, it really returns only 4 decimal places, and it doesn't matter how big or small numbers are in table. In my case 4 decimals is not precise enough. Same stands true if I use AVG instead of SUM.

If I use MAX instead of SUM, I get : 0.6666666666666666666 (19 decimals).

It also returns correct result (0.6666666666666667) when no phisical table is used:

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; 

I have looked into Redshift documentation about SUM and Computations with Numeric Values, but I still don't get result according to documentation.

Using float datatype for table columns is not an option as I need to store precise currency amounts and 15 significant digits is not enough.

Using cast on SUM aggregation also gives 0.6666666666666666666 (19 decimals).

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

But it looks wrong, and I can't force BI tools to do this workaround, also everyone who uses this data should not use this kind of workaround.

I have tried to use same test in PostgreSQL 10, and it works as it should, returning sufficient amount of decimals for division.

Is there anything I can do with database setup to avoid casting in SQL Query? Any advice or guidance is highly appreciated.

Redshift version: PostgreSQL 8.0.2 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.4081 Using dc2.8xlarge nodes

1

1 Answer

I have run into similar issues, and although I don't have a solution that doesn't require a workaround, I can at least explain it.

The precision/scale of the result of division is defined by the rules in the "computations with numeric values" document.

A consequence of those rules is that a decimal(19,6) divided by another decimal(19,6) will return decimal(38,19).

What's happening to you, though, is that MAX returns the same precision/scale as the underlying column, but SUM returns decimal(38,*) no matter what. (This is probably a safety precaution to prevent overflow on sums of "big data"). If you divide decimal(38,6) by another, you get decimal(38,4).

AWS support will probably not consider this a defect -- there is no SQL standard for how to treat decimal precision in division, and given that this is documented behavior, it's probably a deliberate decision.

The only way to address this is to typecast the numerator, or multiply it by something like sum(a) * cast(1 as decimal(10,9)) which is portable SQL and will force more decimal places in the numerator and thus the result.

As a convenience I made a calculator in JSFiddle with the rules so you can play around with different options:

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 } 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like