Find sum of hours for each date worked - sql

I have a table of timesheet entries set up like this:
id
job_id
employee_id
hours_worked
date_worked
1
1
111
8
2022-10-01
2
1
222
8
2022-10-01
3
1
222
8
2022-10-02
4
2
222
8
2022-10-03
5
2
111
8
2022-10-04
6
2
222
5
2022-10-05
7
3
111
8
2022-10-04
8
4
333
8
2022-10-07
9
4
111
3
2022-10-09
I'm trying to find the sum of hours for the first, second, third etc dates that work was done on each job
Ideally I'd like something like this:
job_id
Day1_hours
Day2_hours
Day3_hours
1
16
8
0
2
8
8
5
3
8
0
0
4
8
3
0
The trouble I'm running into is that there can be multiple employees working on each day of the job, so using a query to select the min(date_worked) greater than a subquery for min(date_worked) is sometimes giving me the same dates. There are sometimes days in between work done on a job, so I can't just add a day to the minimum value and check hours for that date.
How can I find the sum of hours_worked for the first date_worked, then the second, third etc?

PIVOT's are great but conditional aggregations offer a bit more flexibility
Example
Select job_id
,[Day1_Hours] = sum( case when DN=1 then hours_worked else 0 end)
,[Day2_Hours] = sum( case when DN=2 then hours_worked else 0 end)
,[Day3_Hours] = sum( case when DN=3 then hours_worked else 0 end)
From ( Select *
,DN = dense_rank() over (partition by job_id order by date_worked)
From YourTable
) A
Group By Job_ID

select job_id
,[1] as day1_hours
,[2] as day2_hours
,[3] as day3_hours
from (
select job_id
,hours_worked
,dense_rank() over(partition by job_id order by date_worked) as days
from t
) t
pivot (sum(hours_worked) for days in([1],[2],[3])) p
job_id
day1_hours
day2_hours
day3_hours
1
16
8
null
2
8
8
5
3
8
null
null
4
8
3
null
Fiddle

Related

Sum if same ID1 and ID2 - only once - SQL

I have the following data on SQL
EntryID
PersonID
JobID
JobSalary
1
1
1
270000
2
1
2
500000
3
2
3
320000
4
3
4
180000
5
2
3
320000
6
1
3
85000
7
1
1
270000
8
1
2
500000
9
2
3
320000
10
3
4
180000
For each entry, I want to have a column that calculates the total salary (of all jobs) of the specific person.
The tricky part is that multiple entries can refer to the same person and/or the same job, but I only want to sum up each job for each person once.
The output would be:
EntryID
PersonID
JobID
JobSalary
PersonTotalSalaryAllJobs
1
1
1
270000
855000
2
1
2
500000
855000
3
2
3
320000
320000
4
3
4
180000
180000
5
2
3
320000
320000
6
1
3
85000
855000
7
1
1
270000
855000
8
1
2
500000
855000
9
2
3
320000
320000
10
3
4
180000
180000
Any ideas on how to do this?
Thanks!
To get what you want you can use a query like this
SELECT
s.*,
pjts.total_salary
FROM salary s
LEFT JOIN (
SELECT
*,
SUM(jobsalary) OVER (PARTITION BY personid) AS total_salary
FROM (
SELECT DISTINCT
personid,
jobid,
jobsalary
FROM salary
) pjs
) pjts ON s.personid = pjts.personid AND s.jobid = pjts.jobid
You can check a working demo here
Or even simplier using only one subquery like this
SELECT
s.*,
pjts.total_salary
FROM salary s
LEFT JOIN (
SELECT
personid,
jobid,
SUM(jobsalary) OVER (PARTITION BY personid) AS total_salary
FROM salary
GROUP BY personid, jobid, jobsalary
) pjts ON s.personid = pjts.personid AND s.jobid = pjts.jobid
You can check a working demo here

Get earliest value from a column with other aggregated columns in postgresql

I have a very simple stock ledger dataset.
1. date_and_time store_id product_id batch opening_qty closing_qty inward_qty outward_qty
2. 01-10-2021 14:20:00 56 a 1 5 1 0 4
3. 01-10-2021 04:20:00 56 a 1 8 5 0 3
4. 02-10-2021 15:30:00 56 a 1 9 2 1 8
5. 03-10-2021 08:40:00 56 a 2 2 6 4 0
6. 04-10-2021 06:50:00 56 a 2 8 4 0 4
Output I want:
select date, store_id,product_id, batch, first(opening_qty),last(closing_qty), sum(inward_qty),sum(outward_qty)
e.g.
1. date store_id product_id batch opening_qty closing_qty inward_qty outward_qty
2. 01-10-2021 56 a 1 8 1 0 7
I am writing a query using First_value window function and tried several others but not able to get the out put I want.
select
date,store_id,product_id,batch,
FIRST_VALUE(opening_total_qty)
OVER(
partition by date,store_id,product_id,batch
ORDER BY created_at
) as opening__qty,
sum(inward_qty) as inward_qty,sum(outward_qty) as outward_qty
from table
group by 1,2,3,4,opening_total_qty
Help please.
As your expected result is one row per group of rows with the same date, you need aggregates rather than window functions which provide as many rows as the ones filtered by the WHERE clause. You can try this :
SELECT date_trunc('day', date),store_id,product_id,batch
, (array_agg(opening_qty ORDER BY datetime ASC))[1] as opening__qty
, (array_agg(closing_qty ORDER BY datetime DESC))[1] as closing_qty
, sum(inward_qty) as inward_qty
, sum(outward_qty ) as outward_qty
FROM table
GROUP BY 1,2,3,4
see the test result in dbfidle.

Can't use case & aggregation correctly

I have the following table
Cash_table
ID Cash Rates Amount
1 50 3 16
2 100 4 25
3 130 10 7
3 130 10 6
4 13 7 1.8
5 30 8 2.5
5 30 10 1
6 10 5 2
What I want as a result is to cumulate all the entries that have a Count(id)>1 like this:
ID New_Cash New_Rates New_Amount
1 50 3 16
2 100 4 25
3 130 10+10 130/(10+10)
4 13 7 1.8
5 30 8+10 30/(8+10)
6 10 5 2
So I only want to change the rows where Count(id)>1 and leave the rest like it was.
For the rows with count(id)>1 I want to sum up the rates and take the cash and divide it by the sum of the rates. The Rates alone aren't a problem since I can sum them up and group by id and get the desired result.
The problem is with the New_Amount column:
I am trying to do it with a case statement but it isn't working:
select id,
cash as new_cash,
sum(rates) as new_rates,
(case count(id)
when 1 then amount
else cash/sum(nvl(rates,null))
end) as new_amount
from Cash_table
group by id
As the cash value is always the same for an ID, you can group by that as well:
select id,
cash as new_cash,
sum(rates) as new_rates,
case count(id)
when 1 then max(amount)
else cash/sum(rates)
end as new_amount
from cash_table
group by id, cash
order by id
ID NEW_CASH NEW_RATES NEW_AMOUNT
---------- ---------- ---------- ----------
1 50 3 16
2 100 4 25
3 130 20 6.5
4 13 7 1.8
5 30 18 1.66666667
6 10 5 2
The first branch of the case expression needs an aggregate because you aren't grouping by amount; and the sum(nvl(rates,null)) can just be sum(rates). If you're expecting any null rates then you need to decide how you want the amount to be handled, but nvl(rates,null) isn't doing anything.
You can do the same thing without a case expression if you prefer, manipulating all the values - which might be more expensive:
select id,
cash as new_cash,
sum(rates) as new_rates,
sum(amount * rates)/sum(rates) as new_amount
from cash_table
group by id, cash
order by id

Single SQL query to display aggregate data while grouping by 3 fields

I have a table that contains basic info:
CREATE TABLE testing.testtable
(
recordId serial NOT NULL,
nameId integer,
teamId integer,
countryId integer,
goals integer,
outs integer,
assists integer,
win integer,
sys_time timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT testtable_pkey PRIMARY KEY (recordid)
)
I want one single SQL query, (with one record per person-team-country) to display the following data. Note that I want it to group by nameId, teamId, and countryId
Name, Team, and Country
Goal/out ratio (G/O)
Goal + Assist / out ratio (GA/O)
Win percentage (Win%)
The difference between the current goal/out ratio and what it was one month ago (rDif)
The difference between the current goal+assist/out ratio and what it was one month ago (fDif)
The difference between the current win % and what it was one month ago (winDif)
Example Table with all records:
Id nameId teamId countryId goals outs assists win sys_time
1 1 3 5 2 4 11 1 2013-01-01
2 1 3 5 9 4 19 1 2013-01-01
3 1 3 4 10 2 1 0 2013-01-01
4 1 3 4 11 50 14 1 2013-01-01
5 2 2 2 10 5 4 1 2013-01-01
6 2 3 5 4 7 15 0 2013-01-01
7 1 3 5 4 8 22 0 2014-07-01
8 1 3 4 11 3 5 1 2014-07-01
9 3 1 4 44 1 4 1 2014-07-01
Example desired output record (1-3-5):
nameId teamId countryId G/O GA/O Win% rDif fDif winDif
1 3 5 0.938 4.19 66 0.44 0.94 -0.34
The ratios are easy enough to retrieve.. for the differences, I've done the following:
select tt.nameid
avg(tt.goals) - avg(case when tt.sys_time < date_trunc('day', NOW() - interval '1 month') then tt.goals end) as change
from testing.testtable tt
group by tt.nameid
order by change desc
This works if I want the differences for only the nameIds. But I want it to pull one record for each combination of name-team-country. I can't seem to get that working.
You can group by multiple fields:
select tt.nameid, tt.teamID, tt.countryID,
avg(tt.goals) - avg(case when tt.sys_time < date_trunc('day', NOW() - interval '1 month') then tt.goals end) as change
from testing.testtable tt
group by tt.nameid, tt.teamID, tt.countryID
order by change desc
just off the top of my head I think it would work for you to use
group by tt.nameid, tt.teamId, tt.countryId

selecting distinct data using group by in SQL

I have an action log table which records when a registrant record was viewed by a compnay employee. I have an sql query like this:
SELECT [ID]
,[RegistrantID]
,[EmployeeID]
,[UserID]
,[CompanyID]
,[VacancyID]
,[Action]
,[ActionDate]
FROM [Hrge].[dbo].[hr_ActionLog]
where action = 4
and CompanyID = 3
order by ActionDate desc
and data is like this:
ID RegistrantID EmployeeID UserID CompanyID VacancyID Action ActionDate
1793 16295 15 16321 3 NULL 4 2013-08-04 16:45:40.457
1792 16292 15 16321 3 NULL 4 2013-08-04 16:45:33.003
1791 NULL 15 16321 3 NULL 3 2013-08-04 16:45:23.660
1790 16295 9 16289 3 NULL 4 2013-08-04 16:45:09.543
1789 16295 9 16289 3 NULL 4 2013-08-04 16:45:00.817
1799 16295 15 16321 3 NULL 4 2012-08-04 16:45:40.457
1797 16292 15 16321 3 NULL 4 2012-08-04 16:45:33.003
1796 NULL 15 16321 3 NULL 3 2012-08-04 16:45:23.660
1795 16295 9 16289 3 NULL 4 2012-08-04 16:45:09.543
1794 16295 9 16289 3 NULL 4 2012-08-04 16:45:00.817
I want to select distinct views to a registrantid record ( the first ones) in one year. if a registrant was viewed 10 tmes a year then it will show only first time it was viewed. If it was viewed 10 times by an employeed in 2 years then it will show first time it was viewed. if it was viewed by 2 employees of same company 10 times in one year then it first time viewed record will be shown. if it was seen 10 times by 2 employees of two different companies in one year then first record of two companies will be shown. do i need to use group by or what ?
Use the ranking function ROW_NUMBER with PARTITION BY RegistrantID ORDER BY ActionDate to get the first date for each RegistrantID:
WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY RegistrantID
ORDER BY ActionDate) AS RN
FROM [Hrge].[dbo].[hr_ActionLog]
WHERE action = 4
AND CompanyID = 3
)
SELECT [ID]
,[RegistrantID]
,[EmployeeID]
,[UserID]
,[CompanyID]
,[VacancyID]
,[Action]
,[ActionDate]
FROM CTE
WHERE RN = 1;
SQL Fiddle Demo