Reputation: 103
Please I'm trying to find the percentage difference between two values sum of imls2016.visits and sum of imls2010.visits in each row. Which was previously working fine before I added a third table in my joint statement (the table being imls_regions). But anytime I run the new code it throws a numeric overflow error. I have adjusted the precision and scale but I haven't been able to rectify this error. Please what should I change in my code: (PostgreSQL 12)
My code:
SELECT imlsreg.obereg,
imlsreg.obereg_name,
SUM(imls2016.visits) AS total_visits_2016,
SUM(imls2010.visits) AS total_visits_2010,
ROUND((CAST(SUM(imls2016.visits) AS decimal(10, 1)) - SUM(imls2010.visits)) / SUM(imls2010.visits) * 100, 1) AS pct_change_in_visits
FROM imls_regions AS imlsreg INNER JOIN imls_library_survey_2016 AS imls2016
ON imlsreg.obereg = imls2016.obereg
INNER JOIN imls_library_survey_2010 AS imls2010
ON imlsreg.obereg = imls2010.obereg
WHERE imls2016.visits >= 0 AND imls2010.visits >= 0
GROUP BY imlsreg.obereg, imlsreg.obereg_name
ORDER BY pct_change_in_visits ASC;
Upvotes: 2
Views: 10523
Reputation: 103
Okay I found the problem. My sum value had a precision that was more than 10 (meaning that the sum which when still in integer format had a length of 12 values) hence when casting it throws a Numeric field overflow error. To find out where I went wrong I removed the ROUND() AND CAST() functions in listing 1.1:
SELECT imlsreg.obereg,
imlsreg.obereg_name,
SUM(imls2016.visits) AS total_visits_2016,
SUM(imls2010.visits) AS total_visits_2010,
(SUM(imls2016.visits) - SUM(imls2010.visits)) / SUM(imls2010.visits) * 100 AS pct_change_in_visits
FROM imls_regions AS imlsreg INNER JOIN imls_library_survey_2016 AS imls2016
ON imlsreg.obereg = imls2016.obereg
INNER JOIN imls_library_survey_2010 AS imls2010
ON imlsreg.obereg = imls2010.obereg
WHERE imls2016.visits >= 0 AND imls2010.visits >= 0
GROUP BY imlsreg.obereg, imlsreg.obereg_name
ORDER BY pct_change_in_visits ASC;
This gave me the sum of visits I wanted to see but ignore the pct_change for now (most of the pct_change is 0 because dividing an two integers in sql gives you an integer hence it is important to cast an operating value to be a decimal or double precision numeric data-type)
After counting the number of length of the max visits value (12 in length) I was able to find an accurate precision value. I then reverted to my previous code and adjusted the precision to numeric (14, 1). See code in listing 1.2:
SELECT imlsreg.obereg,
imlsreg.obereg_name,
SUM(imls2016.visits) AS total_visits_2016,
SUM(imls2010.visits) AS total_visits_2010,
ROUND((CAST(SUM(imls2016.visits) AS decimal(14, 1)) - SUM(imls2010.visits)) / SUM(imls2010.visits) * 100, 2) AS pct_change_in_visits
FROM imls_regions AS imlsreg INNER JOIN imls_library_survey_2016 AS imls2016
ON imlsreg.obereg = imls2016.obereg
INNER JOIN imls_library_survey_2010 AS imls2010
ON imlsreg.obereg = imls2010.obereg
WHERE imls2016.visits >= 0 AND imls2010.visits >= 0
GROUP BY imlsreg.obereg, imlsreg.obereg_name
ORDER BY pct_change_in_visits ASC;
Upvotes: 0
Reputation: 463
When you cast to decimal(10,1)
, you are reducing the range of allowable values of SUM(imls2016.visits)
in exchange for greater precision, but this is causing your result to overflow.
There are two things you can do:
SUM(imls2016.visits) - SUM(imls2010.visits)
Implementing the first one may suffice, and you would define pct_change_in_visits
as:
ROUND(CAST(SUM(imls2016.visits) - SUM(imls2010.visits) AS decimal(10, 1)) / SUM(imls2010.visits) * 100, 1) AS pct_change_in_visits
Upvotes: 2