I have a employee leave balance table as follows
emp_code | leave_type | yearmonth | Balance | Priority |
---|---|---|---|---|
1 | PL | 202205 | 2 | 0 |
1 | SL | 202205 | 1 | 1 |
2 | PL | 202205 | 3 | 0 |
2 | SL | 202205 | 1 | 1 |
3 | PL | 202205 | 1 | 0 |
3 | SL | 202205 | 1 | 1 |
and a Attendance Table as follows
emp_code | date | yearmonth | Attendance | Leave |
---|---|---|---|---|
3 | 2022-05-01 | 202205 | 1 | |
3 | 2022-05-02 | 202205 | 1 | |
3 | 2022-05-03 | 202205 | 1 | |
1 | 2022-05-01 | 202205 | 0 | |
1 | 2022-05-02 | 202205 | 0 | |
1 | 2022-05-03 | 202205 | 0 | |
1 | 2022-05-04 | 202205 | 0 | |
2 | 2022-05-01 | 202205 | 1 | |
2 | 2022-05-02 | 202205 | 1 |
I just wanted to update the attendance table
with the respective leave (based on the priority and availability) if the attendance field value is 0
For eg: employee 1 have 3 leave balance and 4 days absent
After the update, the records for emp_code 1 in attendance should be as follows
emp_code | date | yearmonth | Attendance | Leave |
---|---|---|---|---|
1 | 2022-05-01 | 202205 | 0 | PL |
1 | 2022-05-02 | 202205 | 0 | PL |
1 | 2022-05-03 | 202205 | 0 | SL |
1 | 2022-05-04 | 202205 | 0 |
I know, we can do this through SP or function. But my company policy does not allow me to create SP or functions (I can update this via my backend code, but there are millions of records there to be updated so I am worried about the performance)
I wonder, is there any ways to achieve this in PG using CTE/Window function/any other means ?
here is a fiddle https://www.db-fiddle.com/f/4jyoMCicNSZpjMt4jFYoz5/4952
Thanks
CodePudding user response:
If the last attendance record in your fiddle is in error and removed, then my approach would be:
- Expand the leave balance for the month into rows using
generate_series()
and assign row numbers based on the priority - Assign row numbers to absences within a month
- Calculate changes by left join from absences to leave records
with leave_rows as (
select b.*,
row_number() over (partition by emp_code, yearmonth
order by priority) as use_order
from emp_leave_balance b
cross join lateral generate_series(1, b.balance, 1)
), absence_rows as (
select a.*,
row_number() over (partition by emp_code, yearmonth
order by date) as use_order
from attendance a
where attendance = 0
), changes as (
select a.emp_code, a.date, a.yearmonth, a.attendance, l.leave_type
from absence_rows a
left join leave_rows l
on (l.emp_code, l.yearmonth, l.use_order) =
(a.emp_code, a.yearmonth, a.use_order)
)
update attendance
set leave = c.leave_type
from changes c
where (c.emp_code, c.date) = (attendance.emp_code, attendance.date)
;
Your updated fiddle