postgresql aggregate of aggregate (sum of sum) - sql

I've got workers who have many sales and who belong to departments. I'd like to see how many sales a department is making per day.
For simplicity, let's say a worker belongs to only one department.
Example:
departments:
| id | name |
| 1 | Men's Fashion |
| 2 | Women's Fashion |
workers:
| id | name |
| 1 | Timmy |
| 2 | Sally |
| 3 | Johnny |
sales:
| id | worker_id | datetime | amount |
| 1 | 1 | 2013-1-1 08:00:00 | 1 |
| 2 | 1 | 2013-1-1 09:00:00 | 3 |
| 3 | 3 | 2013-1-2 08:00:00 | 8 |
department_employees
| id | worker_id | department_id |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 2 |
I'd like to get
| department | amount |
| Men's Fashion | 4 |
| Women's Fashion | 8 |
To get the individual worker's total sales, I can do
SELECT worker_id, SUM(amount) FROM sales
GROUP BY worker_id
How do I take those sums (the total amount sold per worker) and aggregate it by department?

Don't sum the sum, rather join from sales through the department_employees table to the department:
select d.name, sum(s.amount)
from sales s
join department_employees de on de.worker_id = s.worker_id
join departments d on d.id = de.department_id
group by d.name

Aggregate functions and group by work in a statement with joints too.
Try something like:
SELECT name, SUM(amount) FROM departments, department_employees, sales
WHERE departments.id = department_employees.department_id
AND sales.worker_id = department_employees.worker_id
GROUP BY name

Related

Get total from child table with parent table Id

I'm trying to query data from population table from members
table using the first state id (sid), by selecting the first appeared "sid" on members table
for each state id "sid" without duplicating all appeared sid on members table.
I want to get on male and female total for each state using sid. but when I query I get total of all record
from poupulation table
Example:
male_child(20) + female_cahild(70) for sid = 1
male_child(10) + female_cahild(12) for sid = 3
total = 112
Here is my sql query :
SELECT sum(p.number)as total FROM population p
JOIN members m ON p.mid = m.mid
states
+------------
|sid | name |
+----+------+
| 11 | A |
| 23 | B |
+-----------+
members
+-------------------------+
| mId | sid | date |
+------+------+-----------+
| 1 | 11 | 10-2-2021 |
| 2 | 11 | 15-2-2021 |
| 3 | 23 | 12-2-2021 |
| 4 | 23 | 16-2-2021 |
+--------------=----------+
pupulation table
pupulation
+----------------------------------------+
| pid | mid | gender | type | number |
+-----+-----+--------+--------+----------+
| 1 | 1 | male | child | 20 |
| 2 | 1 | female | child | 50 |
| 3 | 2 | male | child | 20 |
| 4 | 2 | female | child | 20 |
| 5 | 3 | male | child | 10 |
| 6 | 3 | female | child | 12 |
| 7 | 4 | female | child | 30 |
| 8 | 4 | female | child | 25 |
+----------------------------------------+
result : getting total / sum of the first `members`.`sid` 11 on row1 and 23 on
row3 of members table then sum their population
that will be (20 + 50) + (10 + 12) = 92
You haven't actually shown what your desired results are, but I suspect you just need to group by the relevant columns
select
s.name,
p.gender,
Sum(p.number) as total
from population p
join members m on p.mid = m.mid
join states s on s.sid = m.sid
group by s.name, p.gender
Edit
To get the desired total, simply get the minimum id for each member and sum the rows for those IDs
select Sum(number) as Total
from population
where mid in (select Min(mid) from members group by sid)
Example DB Fiddle

How to use SQL SELECT as field name

I'm trying to use select as column name to store the get total member, But I didn't succeed to get the solution for the right sql statement to use.
Here is my database table structure and sql that I have used.
Sql
SELECT
name,
SELECT
(
(COALESCE(SUM(adult),0) + COALESCE(SUM(elders),0))
) as total
FROM members
LEFT JOIN company
ON company.companyId = members.companyId
GROUP BY company.companyId
My tables
company table
+----------------------+
| companyId | Name |
+-----------+----------+
| 1 | Company1 |
| 2 | Company2 |
+-----------+----------+
members table
+--------------------------------------------+
| id | companyId | gender | adult | elders |
+------+-----------+--------+-------+--------+
| 1 | 1 | male | 200 | 700 |
| 1 | 1 | female | 300 | 50 |
| 1 | 2 | male | 100 | 500 |
| 1 | 2 | female | 900 | 800 |
+------+-----------+--------+-------+--------+
expected Results table
+-------------------------------------------------+
| companyId | Name | adult | elders | Total |
+-----------+----------+-------+--------+---------+
| 1 | Company1 | 500 | 750 | 1250 |
| 1 | Company2 | 1000 | 1300 | 2300 |
+-----------+----------+-------+--------+---------+
Thanks in advance
A LEFT JOIN starting on members is not appropriate, unless members could lack a company. I'll just use a regular JOIN.
Then you want aggregation with arithmetic:
SELECT c.companyId, c.name,
SUM(m.adults + m.elders) as total,
SUM(m.adults) as adults, SUM(m.elders) as elders
FROM members m JOIN
company c
ON c.companyId = m.companyId
GROUP BY c.companyId, c.name;
You would use a LEFT JOIN if one of the following were true and you wanted to avoid filtering rows out:
Some members do not have a valid companyId.
Some companies have no rows in members.

SQL count on multiple columns including 0 value

I've joined up my tables such that every entry is unique and I want to get a COUNT() value for how many unique courses the teachers teach. I figured I would make a table of distinct courses then do a count based on the teacher's id, however this doesn't account for teachers who taught no courses and I wish to return a zero value in this case. How do I go about getting these zero values?
Table for reference:
id | name | course_id | sec_id | semester | year
-------+------------+-----------+--------+----------+------
33456 | A | | | |
10101 | B | CS-101 | 1 | Fall | 2009
76766 | C | BIO-301 | 1 | Summer | 2010
12121 | D | FIN-201 | 1 | Spring | 2010
10101 | B | CS-347 | 1 | Fall | 2009
76543 | E | | | |
83821 | F | CS-319 | 2 | Spring | 2010
83821 | F | CS-190 | 2 | Spring | 2009
98345 | G | EE-181 | 1 | Spring | 2009
10101 | B | CS-315 | 1 | Spring | 2010
22222 | H | PHY-101 | 1 | Fall | 2009
45565 | I | CS-101 | 1 | Spring | 2010
15151 | J | MU-199 | 1 | Spring | 2010
32343 | K | HIS-351 | 1 | Spring | 2010
83821 | F | CS-190 | 1 | Spring | 2009
45565 | I | CS-319 | 1 | Spring | 2010
76766 | C | BIO-101 | 1 | Summer | 2009
58583 | L | | | |
ps. I believe I am using PostgreSQL.
[EDIT] the expected result is a table of id's, names, and a number showing the amount of courses the teacher has taught, including 0 if they have not taught any course.
[EDIT 2] I only need a query on this table, all the other work is done. If there is no value for course_id, sec_id, semester, year then that teacher has not taught a course (in the case of teachers A, E, and L; who would have a count of 0). I only need a way to count these courses, nothing else.
let's assume the table name is t:
select distinct count(course_id) filter (where course_id is not null) over (partition by id,name),id, name
from t
order by name;
count | id | name
-------+-------+--------------
0 | 33456 | A
3 | 10101 | B
2 | 76766 | C
1 | 12121 | D
0 | 76543 | E
3 | 83821 | F
1 | 98345 | G
1 | 22222 | H
2 | 45565 | I
1 | 15151 | J
1 | 32343 | K
0 | 58583 | L
(12 rows)
https://www.postgresql.org/docs/current/static/sql-expressions.html
I believe, this query should do the job:
SELECT COUNT(DISTINCT course_id), t.id, name FROM courses r Right Outer JOIN teachers t ON (r.id=t.id) group by t.id, name;
I assumed, that not all teachers where in the table you have provided, thus the need for the Outer Join. Tested on similar database on Oracle.
Do the joins with subquery to find the teachers course count
select t.name, coalesce(c.course_count, 0) course_count
from table t left join (
select name, count(distinct course _id) course_count
from table
group by name
) c on c. name = t.name
group by t.name

SQL Query to do grouping across a join

I've an enrollment table containing student IDs, course IDs and teacher IDs.
___________________
| sID | cID | tID |
___________________
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | 3 | 3 |
| 2 | 1 | 1 |
| 2 | 3 | 5 |
| 3 | 1 | 1 |
| 3 | 2 | 2 |
I would like to get a table that can tell me how many students are in each course with a given professor. In other words, I'd like this:
_____________________________
| cID | tID | numOfStudents |
____________________________
| 1 | 1 | 3 |
| 2 | 2 | 2 |
| 3 | 3 | 1 |
| 3 | 5 | 1 |
I've tried
SELECT cID, tID, count(sID)
FROM enrollment
GROUP BY tID
but this type of formula, with different combinations is not working for me. Does anyone have any other suggestions?
Just add cid to the GROUP BY:
SELECT cID, tID, count(*)
FROM enrollment
GROUP BY cid,tID
sqlfiddle demo
From the docs:
When GROUP BY is present, it is not valid for the SELECT list
expressions to refer to ungrouped columns except within aggregate
functions, since there would be more than one possible value to return
for an ungrouped column.
SELECT cID, tID, count(sID)
FROM enrollment
GROUP BY 1,2

How to return smallest value inside the resultset as a separate column in SQL?

I've been struggling with the following SQL query.
My resultset is now:
| Id | Customer | Sales |
| 1 | 1 | 10 |
| 2 | 1 | 20 |
| 3 | 2 | 30 |
| 4 | 2 | 40 |
What I'd like to do is to add additional column that shows the smallest sale for that customer:
| Id | Customer | Sales | SmallestSale |
| 1 | 1 | 10 | 10 |
| 2 | 1 | 20 | 10 |
| 3 | 2 | 30 | 30 |
| 4 | 2 | 40 | 30 |
As the select query to get those three columns is now rather complex I'd like to avoid subqueries.
Any ideas?
Mika
Assuming your RDBMS supports windowed aggregates
SELECT Id,
Customer,
Sales,
MIN(Sales) OVER (PARTITION BY Customer) AS SmallestSale
FROM YourTable
select s.Id, s.Customer, s.Sales, sm.SmallestSale
from Sales s
inner join (
select Customer, min(sales) as SmallestSale
from Sales
group by Customer
) sm on s.Customer = sm.Customer