How to GROUP BY into separate columns - sql

I have 3 tables:
The first one contains information about persons. The relevant column is the personID.
The second contains exercises a person can do. There are for example 3 exercises with an exID.
The third contains the points a person (personID) reached in an exercise (exID). So each row here stands for an examination a person has taken. But not everyone need to have taken every exam.
What I would like to have is a result with the columns personID, exam_one, exam_two, exam_three, ...(ongoing, depending on how many exams there are). And each row of the result should contain the personID and the points from the respective exam.
For exams not taken there should be NULL or something.
Example for table persons:
personID | Name | ...
-------------------
1 | Max |
2 | Peter |
Example for exercises table:
exID | exName | maxPoints | ...
-------------------------------
1 | exam1 | 20
2 | exam2 | 25
3 | exam3 | 20
Example for points table:
personID (fkey) | exID (fkey) | points
----------------------------------------
1 | 1 | 12.5
1 | 3 | 10
2 | 1 | 5
2 | 2 | 8.5
2 | 3 | 10
Wished result:
personId | exam1 | exam2 | exam3
------------------------------------
1 | 12.5 | NULL | 10
2 | 5 | 8.5 | 10
Is there a way to do this? I use PostgreSQL

You can use something like the following:
select p.personId,
sum(case when e.exname = 'exam1' then t.points end) Exam1,
sum(case when e.exname = 'exam2' then t.points end) Exam2,
sum(case when e.exname = 'exam3' then t.points end) Exam3
from persons p
left join points t
on p.personID = t.personID
left join exercises e
on t.exid = e.exid
group by p.personid
See SQL Fiddle with Demo

Related

SQL query on conditional expressions

I am starting an SQL crash course and I have this problem it's bugging me I can't find a solution and with lockdown I have no one I can ask. It's about conditional expressions. In the problem some people play different instruments and they play in duets occasionally in school. I need to find a query that retrieves a table with name and guitar (yes or no) if they play that instrument. I have to work with the following tables:
Table students
| id | name | grade | gender
+-----+-------------+---------+------
| 1 | John | 12 | M
| 2 | Andrew | 11 | F
| 3 | Abigail | 11 | F
| 4 | Simon | 10 | M
Table duets
| id | idStudent1 | idStudent2 | duetOf
+----+------------+------------+------
| 20 | 1 | 2 | Piano
| 35 | 2 | 4 | Piano
| 36 | 3 | 2 | Drums
| 37 | 4 | 1 | Drums
| 35 | 4 | 2 | Guitar
| 36 | 4 | 2 | Flute
| 37 | 4 | 3 | Piano
Query:
select s.name, Coalesce (d.duets, '(none)')
from students s, duet d
where s.id = d.idStudent1
and s.id = d.idStudeant2
and d.instrument = 'guitar'
Any tips on how I can tackle this kind of problem?
You can do conditional aggregation:
select
s.id,
s.name,
max(case when d.duetOf = 'Piano' then 'Yes' else 'No' end) piano,
max(case when d.duetOf = 'Guitar' then 'Yes' else 'No' end) guitar
from students s
left join duets d
on s.id in (d.idStudent1, d.idStudent2)
group by s.id, s.name
This generates one record for each record in students, with two columns that indicate whether the student belongs to a duet that plays guitar or piano.
If you need to display more columns from the students table, you can add them in both the select and group by clauses.
Check this
select s.name, case when duetOf = 'Guitar' then 'Yes' Else 'No' End as Guitar
from duet d
inner join students s on d.idStudent1 = s.Id or d.idStudent2 = s.Id

Combine multiple counts with group by including zeros

I have 3 tables: Equipment, Sections and Users and i want to combine 3 tables grouped by section and show total equipment per section and total users per section including zeros
Example equipment table:
ID Device Name SectionID Device Type
1 Holly's computer 1 PC
2 John's mobile 2 mobile
3 Maggie's printer 3 printer
4 Jonathan's scanner 3 scanner
5 George's scanner 2 scanner
6 Dugans handheld 5 scanner
7 Main printer 5 printer
Example sections table:
ID Section
1 Finance
2 HR
3 IT
4 Manager
5 Storage
Example users table
ID User SectionID
1 John 3
2 George 2
3 Amanda 2
4 Andy 4
5 Katherine 1
I tried this:
SELECT
b.section AS GROUPED_SECTION,
COUNT(distinct a.sectionid) AS TOTAL_EQUIPMENT,
COUNT(distinct c.sectionid) AS TOTAL_USERS
FROM Equipment a
LEFT JOIN Section b ON a.sectionid=b.id
LEFT JOIN Users c on a.sectionid=c.sectionid
GROUP BY b.description
ORDER BY b.description
but something is not working correctly
I want to create a query that will have the following result:
SECTION TOTAL_EQUIPMENT TOTAL_USERS
------- --------------- ------------
Finance 1 1
IT 2 1
HR 2 2
Manager 0 1
Storage 2 0
-1st column presents distinct sections from Equipment table
-2nd column presents total equipment per section according to Equipment table
-3rd column presents total users under that section according to Users table
Using UNION ALL for SectionID on equipment and users tables. and make a grp column to split two different tables.
then do OUTER JOIN base on sections table, final use aggregate function get count.
Query 1:
SELECT s.Section,
COUNT(CASE WHEN grp = 1 THEN 1 END) TOTAL_EQUIPMENT,
COUNT(CASE WHEN grp = 2 THEN 1 END) TOTAL_USERS
FROM sections s
LEFT JOIN (
select SectionID,1 grp
from equipment
UNION ALL
select SectionID,2 grp
from users
) t1 on t1.SectionID = s.ID
GROUP BY s.Section
Results:
| Section | TOTAL_EQUIPMENT | TOTAL_USERS |
|---------|-----------------|-------------|
| Finance | 1 | 1 |
| HR | 2 | 2 |
| IT | 2 | 1 |
| Manager | 0 | 1 |
| Storage | 2 | 0 |
The query is straightforward with CTE, count each of the two child tables, then join them to parent table (section)
Live test: http://sqlfiddle.com/#!18/f9f99/4
with eq as
(
select sectionid, count(*) as total_equipment
from equipment
group by sectionid
)
, uq as
(
select sectionid, count(*) as total_users
from users
group by sectionid
)
select s.id, s.section,
total_equipment = isnull(eq.total_equipment, 0),
total_users = isnull(uq.total_users, 0)
from sections s
left join eq on s.id = eq.sectionid
left join uq on s.id = uq.sectionid
order by s.section
Output:
| id | section | total_equipment | total_users |
|----|---------|-----------------|-------------|
| 1 | Finance | 1 | 1 |
| 2 | HR | 2 | 2 |
| 3 | IT | 2 | 1 |
| 4 | Manager | 0 | 1 |
| 5 | Storage | 2 | 0 |

How to get number of students per course in sql?

So I have these 3 tables:
t_student which looks like this:
STUDENT_ID| FIRST_NAME |LAST_NAME
-----------------------------------
1 | Ivan | Petrov
2 | Ivan | Ivanov
3 | Georgi | Georgiev
t_course which looks like this:
course_id | NAME |LECTURER_NAME
-----------------------------------
1 | Basics | Vasilev
2 | Photography| Loyns
t_enrolment which looks like this:
enrolment_id| student_fk |course_fk | Avarage_grade
-------------------------------------------------------
1 | 1 | 1 |
2 | 3 | 1 |
3 | 4 | 1 |
4 | 2 | 1 |
5 | 1 | 2 | 5.50
6 | 2 | 2 | 5.40
7 | 5 | 2 | 6.00
I need to make 'select' statement and present the number of students per course. The result should be:
Count_students | Course_name
-----------------------------
4 | Basics
3 | Photography
Select all courses from your course Table, join the enrolment table and group by your course id. With count() you can select the number of Students
SELECT MAX(t_course.NAME) AS Course_name, COUNT(t_enrolment.student_fk) AS Count_students
FROM t_course
LEFT JOIN t_enrolment ON t_enrolment.course_fk = t_course.course_id
GROUP BY t_course.course_id;
If you want to select the same student in one course only once (if more then one enrolment can happen) you can use COUNT(DISTINCT t_enrolment.student_fk)
UPDATE
To make it working not only in mySQL I added an aggregate function to the name column.
Depending on the SQL database you are using you will have to add quotes or backticks.
Is this your homework?
select count(*) Count_students, c.name as course_name from t_enrolment e, t_course c group where e.course_fk = c.course_id by c.name
You need a select statement with a join to the couse table (for the Course_name). Group by 't_course'.name to use the COUNT(*) function
this will work:
SELECT COUNT(*) AS Count_students, c.NAME AS Course_name
FROM t_enrolment e
JOIN course c
ON e.course_fk = c.course_id
GROUP BY c.NAME
More information
Count function
Group by
Join

JOIN where row may not exist in one table

I have two tables:
students
+-------+------+
| id | name |
+-------+------+
| 1 | Bob |
+-------+------+
| 2 | Sam |
+-------+------+
and
courses
+----+------------+---------+--------+
| id | student_id | teacher | period |
+----+------------+---------+--------+
| 1 | 1 | Mr. X | 1 |
+----+------------+---------+--------+
| 2 | 1 | Ms. Y | 2 |
+----+------------+---------+--------+
| 3 | 2 | Mr. X | 2 |
+----+------------+---------+--------+
| 4 | 2 | Ms. Y | 3 |
+----+------------+---------+--------+
And this is the result I need from these two tables:
list of students and period 1 teacher
+------------+------+-----------------+
| student_id | name | period 1 teacher|
+------------+------+-----------------+
| 1 | Bob | Mr. X |
+------------+------+-----------------+
| 2 | Sam | null |
+------------+------+-----------------+
Okay, I need a list of students and the teacher they have for a certain period (in this case, period 1). They may, however, have no teacher listed for that period in the courses table, in which case I want 'null' for that column on that student (as above with 'Sam').
The closest I have is this:
SELECT students.id,students.name,courses.teacher
FROM students
LEFT JOIN courses ON students.id = courses.student_id AND courses.period = '1'
But I only ever get back rows that exist in BOTH tables (in this example, only the 'Bob' student would be returned since 'Sam' has no period 1 teacher.
I feel certain it is something simple, but my Google-fu has failed me thus far.
Can you try this:
SELECT students.id,students.name,courses.teacher
FROM students
LEFT JOIN (select * from courses WHERE courses.period = '1') courses
ON students.id = courses.student_id
if I understand correctly and according to the context is to appear only the student BOB.
Assuming it to be true, replace the LEFT for INNER what will work.
SELECT students.id,students.name,courses.teacher
FROM students
INNER JOIN courses ON students.id = courses.student_id AND courses.period = '1'
I hope it is useful

sql query to get the number of cooperating

I need help with SQL query. I have this join table, with 22486 rows.
idPerson are people working on movie, pesonType '1' is actor and '2' is director. I need get director and actor and number of their cooperating on movies.
For example, director idPeroson = 3 work with actor idPerson = 14 on 2 movies.
| idFilm | idPerson | peronType |
+---------+---------+--------+
| 1 | 14 | 1 |
| 1 | 3 | 2 |
| 1 | 34 | 1 |
| 2 | 3 | 2 |
| 2 | 14 | 1 |
+---------+---------+--------+
I'm going crazy from that. Thank you very much. And sorry for bad english
Try this:
SELECT DISTINCT
d.idPerson AS Director,
p.idPerson AS Actor,
(SELECT COUNT(p2.idfilm)
FROM Persons p2
INNER JOIN Persons d2 ON p2.idFilm = d2.idFilm
AND d2.peronType = 2
WHERE p2.peronType = 1
AND p2.idperson = p.idperson
AND d2.idperson = d.idperson ) Counts
FROM Persons p
INNER JOIN Persons d ON p.idFilm = d.idFilm
AND d.peronType = 2
WHERE p.peronType = 1;
SQL Fiddle Demo
This will give you:
| DIRECTOR | ACTOR | COUNTS |
-----------------------------
| 3 | 14 | 2 |
| 3 | 34 | 1 |
A more effective way to have the same good result will be :
SELECT
D.idPerson Director,
P.idPerson Actor,
count(1) NBFilm
FROM Persons D
INNER JOIN Persons P ON D.idFilm = P.idFilm
WHERE P.peronType = 1
AND D.peronType = 2
GROUP BY D.idPerson, P.idPerson
So you will have only one inner join. sub queries in select can be very cost effective.
This will give you the person working on number of films,
select idperson,count(distinct idfilm) NoOfFilms,
case when persontype=2 then 'Director' else 'Actor' end Profession
from table1
group by idperson
SQL_LIVE_DEMO
Sample Output:
IDPERSON NOOFFILMS PROFESSION
3 2 Director
14 2 Actor
34 1 Actor