Find missing values in the student table
Create a CTE that returns the years that you search for and another one that returns the distinct student ids.
Cross join them and left join your table and then filter out all the matching rows.
The remaining rows contain all the years each student has not paid.
Then aggregate and use LISTAGG()
to collect all the years for each student:
WITH years(year) AS ( SELECT 2018 FROM dual UNION ALL SELECT 2019 FROM dual UNION ALL SELECT 2020 FROM dual UNION ALL SELECT 2021 FROM dual ), students(Student_ID) AS (SELECT DISTINCT Student_ID FROM tablename)SELECT s.Student_ID, LISTAGG(y.year, ',') WITHIN GROUP (ORDER BY y.year) Year_of_DefaultFROM years y CROSS JOIN students sLEFT JOIN tablename tON t.Year_of_paid_fee = y.year AND t.Student_ID = s.Student_IDWHERE t.Student_ID IS NULLGROUP BY s.Student_ID
See the demo.
If your Oracle version is 19c or later then use Query#1 or for older version use Query#2.
CREATE TABLE mytable (Student_ID int, Year_of_paid_fee int); Insert INTO mytable VALUES (101, 2018); Insert INTO mytable VALUES (101, 2019); Insert INTO mytable VALUES (101, 2021); Insert INTO mytable VALUES (102, 2018); Insert INTO mytable VALUES (102, 2021);
Query#1 (For Oracle 19c and later)
with allyears as ( select 2018 yr from dual union all select 2019 from dual union all select 2020 from dual union all select 2021 from dual ) select mt.student_id , LISTAGG(distinct yr, ',') WITHIN GROUP (ORDER BY yr) Year_of_Default from mytable mt cross join allyears where not exists(select * from mytable m WHERE mt.student_id=m.student_id and yr=m.YEAR_OF_PAID_FEE) group by mt.student_id
Query#2 (For older version of oracle than 19c)
with allyears as ( select 2018 yr from dual union all select 2019 from dual union all select 2020 from dual union all select 2021 from dual ) ,cte as ( select distinct mt.student_id , yr from mytable mt cross join allyears where not exists(select * from mytable m WHERE mt.student_id=m.student_id and yr=m.YEAR_OF_PAID_FEE) ) select student_id, LISTAGG(yr,',') WITHIN GROUP (ORDER BY yr) Year_of_Default from cte group by student_id
Output:
STUDENT_ID | YEAR_OF_DEFAULT |
---|---|
101 | 2020 |
102 | 2019,2020 |
db<fiddle here
One option would be using MINUS
operator in order to subtract the year values of the table from the set of years (2018
..2021
) for each student such as
SELECT Student_ID, LISTAGG(Year,',') WITHIN GROUP (ORDER BY Student_ID) AS Year_of_Default FROM ( SELECT DISTINCT Student_ID, level + 2017 AS Year FROM t CONNECT BY level <= 4 MINUS SELECT Student_ID,Year FROM t ) GROUP BY Student_ID