Home > other >  MySQL - Calculate percentage change between latest 2 available year
MySQL - Calculate percentage change between latest 2 available year

Time:09-06

I have a table in MySQL 8.0 with the following structure.

CREATE TABLE `rg` ( `name` text, 
                    `zip` text,
                    `Year` bigint,
                    `r` double,
                    `geom` GEOMETRY
                  );

INSERT INTO `rg` (name, zip, Year, r, geom) 

VALUES 

('AB, NM', '87105', '2015', '10', 'POLYGON ((35.066062 -106.700005))'),

('AB, NM', '87105', '2016', '20', 'POLYGON ((35.067912 -106.700884))'),

('AB, NM', '87105', '2017', '30', 'POLYGON ((35.067912 -106.700884))')

How do I write a query that returns the percentage difference of r between the latest 2 available years? I group by name, zip and geom columns.

select name, zip, ST_AsText(geom) as geom, ....
from rg
GROUP BY name, zip, geom

Expected output:

AB, NM 87105 POLYGON ((35.067912 -106.700884)) 50 

i.e difference between r values 20, 30 of 2016 and 2017 is 50.

CodePudding user response:

Use two CTE's, one to get the previous max year, and the other for the max year. JOIN them together using name and zip, and run the difference calculation (mentioned as your expected output above) to get the percentage change between latest two available years.

Calculation:

((maxYear.r-previousMaxYear.r)/previousMaxYear.r * 100)

WITH 
previousMaxYear (name, zip, geom, r) AS (
SELECT name, zip, geom, r FROM rg WHERE year = (SELECT MAX(year) - 1 FROM rg LIMIT 1)
),
maxYear (name, zip, geom, r) AS (
SELECT name, zip, geom, r FROM rg WHERE year = (SELECT MAX(year) FROM rg LIMIT 1)
)
SELECT a.name, a.zip, ST_AsText(a.geom) as geom, CONCAT(ROUND((a.r-b.r)/b.r * 100), '%') AS 'Difference'
FROM maxYear a
  INNER JOIN previousMaxYear b ON
  a.name = b.name
  AND 
  a.zip = b.zip

Result:

name zip geom Difference
AB, NM 87105 POINT(35.067912 -106.700884) 50%

db<>fiddle here.

UPDATE:

Since, as you stated, the previous year may not always be MAX(year) - 1, you can use one CTE to partition it by year DESC in order to get the sequential number for each row.

Then, JOIN the CTE with itself (self join):

  • JOIN the max date (alias a) WHERE row number = 1 (RN=1).
  • JOIN the next max date (alias b) WHERE row number = 2 (RN=2).

Finally, LIMIT 1 row to return the values from alias a.

WITH CTE AS (SELECT name, zip, geom, r, ROW_NUMBER() OVER (ORDER BY year DESC) AS RN FROM rg)
SELECT
  a.name, 
  a.zip, 
  ST_AsText(a.geom) AS geom,
  CONCAT(ROUND((a.r-b.r)/b.r * 100), '%') AS 'difference'
FROM CTE
INNER JOIN (CTE AS a)
    ON (a.name = CTE.name 
        AND a.zip = CTE.zip 
        AND a.RN = 1)
INNER JOIN (CTE as b)
    ON (b.name = CTE.name 
        AND b.zip = CTE.zip 
        AND b.RN = 2)
LIMIT 1

db<>fiddle here.

CodePudding user response:

Your math is a bit off

vut the query would look like, but 50 i can't get

The window function LEAD gives you the next value in Line. you can use it to make mathematical operation

Row_number gives you the row number also for the parttiion and order given in the window function.

As you are only interested in the last two years, we sort the rows by the YEAR and want the partition of name and zip

The calculation of the percentage is

r / r(from the last year) * 100

OR

r(from the last eyxr) / r * 100

But as i can't get 50 per cent i added both calcculations, select the one you want

WITH CTE AS (SELECT
name, zip, Year, r, geom,
   LEAD(r) OVER (PARTITION BY name, zip ORDER BY YEAR DESC) / r * 100 as lasttwoyears
  , ROW_NUMBER() OVER (PARTITION BY name, zip ORDER BY YEAR DESC) rn
FROM rg)
SELECT name, zip,geom, lasttwoyears  FROM CTE WHERe rn = 1
name       zip     geom                                                lasttwoyears
AB, NM     87105   000000000101000000c3802557b18841408a929048dbac5ac0  66.66666666666666
WITH CTE AS (SELECT
name, zip, Year, r, geom,
   r / LEAD(r) OVER (PARTITION BY name, zip ORDER BY YEAR DESC)  * 100 as lasttwoyears
  , ROW_NUMBER() OVER (PARTITION BY name, zip ORDER BY YEAR DESC) rn
FROM rg)
SELECT name, zip,geom, lasttwoyears  FROM CTE WHERe rn = 1
name       zip     geom                                                lasttwoyears
AB, NM     87105   000000000101000000c3802557b18841408a929048dbac5ac0  150

db<>fiddle here

  • Related