Subtract grouped aggregate column by another in 3 table join query - sql

I'm pretty new to joinings and advanced querying, what I want to do is to join three tables to make an summary of how many hours an employee has spent on courses (course data is omitted from examples).
!-SQL query is below the example table-!
The query must show:
A unique set of employee name.
Their individual allocated hours.
A sum of their hours spent
And return a final new column showing the allowance left.
"employees" table
id
employee_id
1
"Annachiara Darius"
2
"Samar Rajani"
3
"Taonga Eric"
4
"Tycho Sigdag"
5
"Naevius Matvei"
6
"Theophania Eglantine"
7
"Boro Stanislav"
"accounting" table where hours are recorded
id
employee_id
hours_done
1
1
2.50
2
1
2.80
3
2
5.60
4
2
3.30
5
4
4.50
6
5
8.90
7
6
7.60
8
3
6.50
9
7
1.00
10
5
10.30
11
7
11.50
12
5
5.60
13
7
100.00
14
2
30.00
"allocation" table
id
employee_id
hours_allocated
1
1
12
2
2
16
3
3
20
4
4
15
5
5
10
6
6
7
7
7
8
SELECT ACCOUNTING.EMPLOYEE_ID AS EMPLOYEE_ID,
EMPLOYEE.EMPLOYEE_NAME AS EMPLOYEE_NAME,
ALLOCATED.HOURS_ALLOCATED,
SUM(ACCOUNTING.HOURS_DONE) AS HOURS_SPENT,
SUM(ALLOCATED.HOURS_ALLOCATED - ACCOUNTING.HOURS_DONE) AS ALLOWANCE
FROM PUBLIC.ACCOUNTING ACCOUNTING
INNER JOIN
(SELECT EMPLOYEE_NAME,
EMPLOYEE_ID
FROM PUBLIC.EMPLOYEES GROUP
BY EMPLOYEE_ID) EMPLOYEE ON EMPLOYEE.EMPLOYEE_ID = ACCOUNTING.EMPLOYEE_ID
INNER JOIN
(SELECT HOURS_ALLOCATED,
EMPLOYEE_ID
FROM PUBLIC.ALLOCATION GROUP
BY EMPLOYEE_ID,
HOURS_ALLOCATED) ALLOCATED ON ALLOCATED.EMPLOYEE_ID = ACCOUNTING.EMPLOYEE_ID GROUP
BY ACCOUNTING.EMPLOYEE_ID,
EMPLOYEE_NAME,
ALLOCATED.HOURS_ALLOCATED
ORDER
BY EMPLOYEE_NAME ASC
Result from the query above
employee_id
employee_name
hours_allocated
hours_spent
allowance
1
"Annachiara Darius"
12
5.3
18.7
7
"Boro Stanislav"
8
112.5
-88.5
5
"Naevius Matvei"
10
24.8
5.2
2
"Samar Rajani"
16
38.9
9.1
3
"Taonga Eric"
20
6.5
13.5
6
"Theophania Eglantine"
7
7.6
-0.6
4
"Tycho Sigdag"
15
4.5
10.5
As you can see I've managed to get every column displaying the information I wanted correctly.
The problem:
Allowence column is only correct if the employee has only made one entry in the accounting table.
If employee has more than one entry in accounting the calculation is off/wrong.
The line I use to get the allowance is
SUM(ALLOCATED.HOURS_ALLOCATED - ACCOUNTING.HOURS_DONE) AS ALLOWANCE
I've been trying different stuff but can't seem to manage this part of the query.
How can I incorporate this into the group logic?

The answer was posted in a comment.
ALLOCATED.HOURS_ALLOCATED - SUM(ACCOUNTING.HOURS_DONE) is correct
but not
`SUM(ALLOCATED.HOURS_ALLOCATED - ACCOUNTING.HOURS_DONE)` AS ALLOWANCE

Related

SQL: how to average across groups, while taking a time constraint into account

I have a table named orders in a Postgres database that looks like this:
customer_id order_id order_date price product
1 2 2021-03-05 15 books
1 13 2022-03-07 3 music
1 14 2022-06-15 900 travel
1 11 2021-11-17 25 books
1 16 2022-08-03 32 books
2 4 2021-04-12 4 music
2 7 2021-06-29 9 music
2 20 2022-11-03 8 music
2 22 2022-11-07 575 travel
2 24 2022-11-20 95 food
3 3 2021-03-17 25 books
3 5 2021-06-01 650 travel
3 17 2022-08-17 1200 travel
3 19 2022-10-02 6 music
3 23 2022-11-08 70 food
4 9 2021-08-20 3200 travel
4 10 2021-10-29 2750 travel
4 15 2022-07-15 1820 travel
4 21 2022-11-05 8000 travel
4 25 2022-11-29 27 books
5 1 2021-01-04 3 music
5 6 2021-06-09 820 travel
5 8 2021-07-30 19 books
5 12 2021-12-10 22 music
5 18 2022-09-19 20 books
Here's a SQL Fiddle: http://sqlfiddle.com/#!17/262fc/1
I'd like to return the average money spent by customers per product, but only consider orders within the first 12 months of a given customer's first purchase within the given product group. (yes, this is challenging!)
For example, for customer 1, order ID 2 and order ID 11 would be factored into the average for books(because order ID 11 took place less than 12 months after customer 1's first order for books, which was order ID 2), but order ID 16 would not be factored into the average (because 8/3/22 is more than 12 months from customer 1's first purchase for books, which took place on 3/5/21).
Here is a matrix showing which orders would be included within a given product (denoted by "yes"):
The desired output would look as follows:
average_spent
books 22.20
music 7.83
travel 1530.71
food 82.50
How would I do this?
Thanks in advance for any assistance you can give!
You can use a subquery to check whether or not to include a product's price in the summation:
select o.product, sum(o.price)/count(*) val from orders o
where o.order_date < (select min(o1.order_date) from orders o1 where
o1.product = o.product and o.user_id = o1.user_id) + interval '12 months'
group by o.product
See fiddle

SQL return row when sum value is null

I got two tables. One with a bill of material and one with purchasing orders. Now I want to display the full bill of material of a product with the total on order amounts from the table purchasing.
**Billofmaterials**
BOM_ID BOM_Prod_id BOM_item_id
1 5 11
2 5 13
3 6 11
4 6 15
5 6 20
Example prod_id (product id) 6 has 3 items (id 11, 15 and 20).
**Purchasing**
PU_ID PU_item_id PU_amount PU_status
1 11 100 On order
2 11 650 On order
3 11 40 Received
4 20 600 On order
5 8 10 On order
6 15 150 Received
Now i got the following SQL
SELECT
BOM_item_id,
SUM(DISTINCT purchasing.PU_amount) as total_on_order
FROM Billofmaterials
LEFT JOIN purchasing
ON Billofmaterials.BOM_item_id= purchasing.PU_item_id
AND purchasing.PU_status != 'Received'
AND BOM_prod_id = 6
GROUP BY BOM_item_id
This query returns the following:
**Query result**
BOM_item_id total_on_order
11 750
20 600
Because there is only one received purchase order for BOM_item_id 15 it doesn't return a value. Now i want to retun BOM_item_id 15 also but with a total_on_order as 0 like:
**Expected result**
BOM_item_id total_on_order
11 750
15 0
20 600
What SQL feature/function do I need to use to get the expected result?
You can try the below -
SELECT BOM_item_id,coalesce(total_on_order,0) as total_on_order
FROM Billofmaterials left join
(
select PU_item_id,SUM(purchasing.PU_amount) as total_on_order
from purchasing
where purchasing.PU_status != 'Received'
group by PU_item_id
) purchasing
ON Billofmaterials.BOM_item_id= purchasing.PU_item_id
where BOM_prod_id = 6

Keep first record in group and populate rest with Null/0 in SQL?

I have the following table in my database:
date sales
1 2010-12-13 10
2 2010-12-13 10
3 2010-12-13 10
4 2010-12-13 10
5 2010-12-13 10
6 2010-12-14 20
7 2010-12-14 20
8 2010-12-14 20
9 2010-12-14 20
10 2010-12-14 20
Is there a way to attain the first record only and populate the rest with NULL or 0 for the remainder of the group? AS the grouping will be done by date and sales:
For example the intended output is:
date sales
1 2010-12-13 10
2 2010-12-13 0
3 2010-12-13 0
4 2010-12-13 0
5 2010-12-13 0
6 2010-12-14 20
7 2010-12-14 0
8 2010-12-14 0
9 2010-12-14 0
10 2010-12-14 0
So essentially to keep the first record but make the rest of the records in the group be 0 (maybe Null if that is quicker/easier)
The closest i have got to solving this is attaining just the first record through an inner join - but I think a partition over may solve it - just stuck at the moment!
Any help appreciated!
Using SQLite - but also GCP (SQL) is accesible to me
This might work in SQLite:
CASE WHEN id = MIN(id) OVER(PARTITION BY date) THEN sales ELSE 0 END as sales
If it doesn't you can prepare a subquery that has only the min ID per date and join it in:
SELECT
CASE WHEN y.id IS NULL THEN 0 ELSE sales END as sales
FROM
x
LEFT JOIN (SELECT MIN(id) as id FROM x GROUP BY date) y ON x.id= y.id

SQL help needed (oracle application express)

Given this information
Among all projects chen work on, list the name of project that has lowest budget.
EMPID NAME SALARY DID
---------------------------
1 kevin 32000 2
2 joan 42000 1
3 brian 37000 3
4 larry 82000 5
5 harry 92000 4
6 peter 45000 2
7 peter 68000 3
8 smith 39000 4
9 chen 71000 1
10 kim 46000 5
11 smith 46000 1
PID EMPID HOURS
-----------------
3 1 30
2 3 40
5 4 30
6 6 60
4 3 70
2 4 45
5 3 90
3 3 100
6 8 30
4 4 30
5 8 30
6 7 30
6 9 40
5 9 50
4 6 45
2 7 30
2 8 30
2 9 30
1 9 30
1 8 30
1 7 30
1 5 30
1 6 30
2 6 30
PID PNAME BUDGET DID
---------------------------------------
1 DB development 8000 2
2 network development 6000 2
3 Web development 5000 3
4 Wireless development 5000 1
5 security system 6000 4
6 system development 7000 1
This is what I've written so far:
select min(budget), pname
from employee e, workon w, project p
where e.empid = w.empid and p.pid = w.pid and name = 'chen'
group by pname
Which gives me
MIN(BUDGET) PNAME
--------------------------
8000 DB development
6000 security system
6000 network development
7000 system development
How can I select just the lowest (the minimum budget) from these values? Thanks for any help.
use explicit join syntax,
Here is one way using analytic function row_number, sequence number given based on budget with lowest being the first.
with cte
as
(
select row_number() over ( order by budget asc) as rn, pname, budget
from employee e
join workon w
on e.empid = w.empid
join project p
on p.pid = w.pid and name = 'chen'
)
select pname, budget from cte where rn =1
select budgets.budget, budgets.pname
from
(
select min(budget) as budget, pname
from employee e, workon w, project p
where e.empid = w.empid and p.pid = w.pid and name = 'chen'
group by pname
order by budget asc
) budgets
where rownum = 1

Need help for SQL query?

I have two tables:
Attendance Table
ID student_roll sem class_id
1 314 7 1
2 315 7 1
3 316 7 1
4 314 7 2
5 315 7 2
6 314 7 3
7 315 7 3
8 316 7 3
9 314 7 4
10 315 7 4
11 316 7 4
Class Table
class_id course t_id date
1 MC 2 14/3/14
2 MC 2 15/3/14
3 C 2 16/3/14
4 MC 2 17/3/14
In the attendance table you can see that roll no. 316 is absent in class_id 2.So suppose i want to count the no of class attended by roll no. 316 in a particular course (Here MC) how can i do so? please help me with the query...Thanks in advance:)
The following query will return the number of attendance records for a particular student (ie. student roll 316), with respect to a given class (ie. 'MC')
SELECT COUNT(*) as ClassCount
FROM attendance A
INNER JOIN class_table C
ON A.class_id = C.class_id
WHERE A.student_roll = 316
AND C.course = 'MC'