Below is a SQL query problem for which I am not able to understand correct approach:
DB tables:
Employee: emp_id, emp_name
Credit: credit_id, emp_id, credit_date, credit_amount
debit: debit_id, emp_id, debit_date, debit_amount
Here, each person can have multiple incomes and expenses.
Query requirement: At the end of each day, each employee will have some asset('credit till now' - 'debit till now'). We need to find top five employees in terms of maximum asset and the date on which they had this maximum asset.
I have tried the below query but seems like I am missing something:
select Credit.emp_id, Credit.date, (Credit.income_amount - Debit.credit_amount) from
(select emp_id, sum(amount) as credit_amount
from credit) Credit
LEFT JOIN LATERAL (
select emp_id, sum(amount) as debit_amount
from debits
where debits.emp_id = Credit.emp_id and Credit.date >= debits.date
group by debits.emp_id
) Debit
ON true
Here I'm breaking the query to make it more readable.
First of all, we need to get the total amount on a day-level for both credit and debit both, so that we can join the credit and debit table on the day level with the same emp_id.
with
credit as(
select emp_id,credit_date date,sum(credit_amount) as amount
from credit
group by 1,2),
debit as(
select emp_id,debit_date,sum(debit_amount) as amount
from expenses
group by 1,2),
Now we need to full outer join the "credit" and "debit" subqueries
payments as (
select distinct
case when c.emp_id is null then d.person_id else c.emp_id end as emp_id ,
case when c.emp_id is null then d.date else c.date end as date,
case when c.emp_id is null then 0 else i.amount end as credit ,
case when d.emp_id is null then 0 else d.amount end as debit
from credit c
full outer join debit d on d.emp_id=c.emp_id and d.date=c.date
),
Now we will take day-wise cumulative sum for credit, debit and total balance as shown below.
total_balance as(
SELECT emp_id, date,
sum(credit) OVER (PARTITION BY emp_id ORDER BY date asc) AS total_credit,
sum(debit) OVER (PARTITION BY emp_id ORDER BY date asc) AS total_debit,
(sum(income) OVER (PARTITION BY person_id ORDER BY date asc) -
sum(expense) OVER (PARTITION BY person_id ORDER BY date asc)) as total_balance
FROM group_payment
ORDER BY person_id, date),
Now we need to use the rank() function to assign rank based on total balance (desc) for an emp_id (ie. rank=1 will be assigned to the largest total balance on a day for a particular emp_id). The query is shown below.
ranks as (select emp_id,date,total_balance,
rank() over (partition by emp_id order by total_balance desc) as rank
from total_balance ),
Now pick the rows having rank=1 (ie. MAX of total_balance on a day for an emp_id and the date on which it was MAX). Order it by total_balance descending and pick the top 5 rows
emp_order as (select emp_id,date,total_balance
from ranks
where rank=1
order by 3 desc
limit 5)
Now pick the name from the employee table.
select emp_id,name, date, total_balance as balance
from emp_order eo
join Employee e on e.emp_id = eo.emp_id
order by 4 desc