I have a table of road condition ratings (roads are rated from 1-20; 20 being good).
with road_inspections
(road_id, year, cond) as (
select 1, 2009, 17 from dual union all
select 1, 2011, 16 from dual union all
select 1, 2015, 14 from dual union all
select 1, 2016, 18.3 from dual union all
select 1, 2019, 18.1 from dual union all
select 2, 2013, 17.5 from dual union all
select 2, 2016, 18 from dual union all
select 2, 2019, 18 from dual union all
select 2, 2022, 18 from dual union all
select 3, 2022, 20 from dual)
select * from road_inspections
ROAD_ID YEAR COND
---------- ---------- ----------
1 2009 17
1 2011 16
1 2015 14
1 2016 18.3
1 2019 18.1
2 2013 17.5
2 2016 18
2 2019 18
2 2022 18
3 2022 20
In a query, for each road, I want to generate rows to fill in the gaps between the years.
The result would look like this:
ROAD_ID YEAR COND
---------- ---------- ----------
1 2009 17
1 2010 17 *
1 2011 16
1 2012 16 *
1 2013 16 *
1 2014 16 *
1 2015 14
1 2016 18.3
1 2017 18.3 *
1 2018 18.3 *
1 2019 18.1
1 2020 18.1 *
1 2021 18.1 *
1 2022 18.1 *
2 2013 17.5
2 2014 17.5 *
2 2015 17.5 *
2 2016 18
2 2017 18 *
2 2018 18 *
2 2019 18
2 2020 18 *
2 2021 18 *
2 2022 18
3 2022 20
*=filler row
Question:
How can I create those filler rows using an Oracle SQL query?
(My priorities are: simplicity first, performance second.)
You can use the LEAD
analytic function with a LATERAL
joined hierarchical query to generate the missing rows from each row until the next row:
SELECT r.road_id,
y.year,
r.cond
FROM ( SELECT r.*,
LEAD(year, 1, EXTRACT(YEAR FROM SYSDATE) + 1)
OVER (PARTITION BY road_id ORDER BY year) AS next_year
FROM road_inspections r
) r
CROSS JOIN LATERAL (
SELECT r.year + LEVEL - 1 AS year
FROM DUAL
CONNECT BY r.year + LEVEL - 1 < r.next_year
) y
Which, for the sample data:
CREATE TABLE road_inspections (road_id, year, cond) as
select 1, 2009, 17 from dual union all
select 1, 2011, 16 from dual union all
select 1, 2015, 14 from dual union all
select 1, 2016, 18.3 from dual union all
select 1, 2019, 18.1 from dual union all
select 2, 2013, 17.5 from dual union all
select 2, 2016, 18 from dual union all
select 2, 2019, 18 from dual union all
select 2, 2022, 18 from dual union all
select 3, 2022, 20 from dual;
Outputs:
ROAD_ID YEAR COND 1 2009 17 1 2010 17 1 2011 16 1 2012 16 1 2013 16 1 2014 16 1 2015 14 1 2016 18.3 1 2017 18.3 1 2018 18.3 1 2019 18.1 1 2020 18.1 1 2021 18.1 1 2022 18.1 2 2013 17.5 2 2014 17.5 2 2015 17.5 2 2016 18 2 2017 18 2 2018 18 2 2019 18 2 2020 18 2 2021 18 2 2022 18 3 2022 20
db<>fiddle here