sql query to get the number of cooperating - sql

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

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

how to select unique records from a table based on a column which has distinct values in another column

I have below table SUBJ_SKILLS which has records like
TCHR_ID | LINE_NBR | SUBJ | SUBJ_TYPE
--------| ------- | ---------- | ----------
1 | 1 | Maths | R
1 | 2 | 101 | U
2 | 1 | BehaviourialTech | U
3 | 2 | Maths | R
4 | 1 | RegionalLANG | U
5 | 3 | ForeignLANG | U
5 | 4 | Maths | R
6 | 2 | Science | R
7 | 1 | 101 | U
7 | 3 | Physics | R
..
..
I am trying to retrieve records like below (i.e. single teacher who taught multiple different subjects)
TCHR_ID | LINE_NBR | SUBJ | SUBJ_TYPE
--------| ------- | ---------- | ----------
5 | 3 | ForeignLANG | U
5 | 4 | Maths | R
7 | 1 | 101 | U
7 | 3 | Physics | R
1 | 1 | Maths | R
1 | 2 | 101 | U
Here, the line numbers are unique, means that TCHR_ID:5 taught Physics (which was LINE_NBR=1, but was removed later). So, the LINE_NBR are not updated and stay as is.
i also have a look up table (SUBJ_LKUP) for subject and their categories/type like below ('R' for Regular subject and 'U' for Unique subject )
SUBJ | SUBJ_TYPE
----------------- | ------------
Maths | R
Physics | R
ForeignLANG | U
101 | U
Science | R
BehaviourialTech | U
RegionalLANG | U
My approach to resolve this was to create a table which have 2 records for Teacher and use another query on base table (SUBJ_SKILLS) and new table to filter out distinct records. I came up with below queries..
Query-1:
create table tchr_with_2_subj as select SS.TCHR_ID
from SUBJ_SKILLS SS, SUBJ_LKUP SL
where SS.SUBJ = SL.SUBJ
and SL.SUBJ_TYPE IN ('R', 'U') AND SS.TCHR_ID IN
(select SS.TCHR_ID from SUBJ_SKILLS SS)
GROUP BY SS.TCHR_ID HAVING COUNT(*) = 2)
Query-2:
select SS.TCHR_ID from SUBJ_SKILLS SS, tchr_with_2_subj tw2s
where SS.TCHR_ID = tw2s.TCHR_ID
GROUP BY SS.TCHR_ID,SS.SUBJ_TYPE HAVING COUNT(*) > 1)
Question:
1)'IN' condition in Query-1 is causing problems and pulling wrong records.
2) Is there a better way to write query to pull matching records using a single query (i.e. instead of creating a table)
Could someone help me on this pls.
For the answer to your original question, I would use window functions:
select ss.*
from (select ss.*,
min(subj) over (partition by tchr_id) as mins,
max(subj) over (partition by tchr_id) as maxs
from SUBJ_SKILLS ss
) ss
where mins <> maxs;
It is unclear how the subject type fits in, but if you need to include that, similar logic will work.
Your second table can be obtained from your first table with:
select ss.*
from
subj_skills as ss
inner join (
select tchr_id
from subj_skills
group by tchr_id
having count(*) > 1
) as mult on mult.tchr_id=ss.tchr_id;
I'd use analytic functions here, asomething like:
select tchr_id, line_nbr, subj, SUBJ_TYPE
from (select count(distinct subj) over (partition by tchr_id) as grp_cnt,
s.*
from subj_skills s)
where grp_cnt > 1
If you need to filter out invalid records, you can do it in the inner query. If a teacher cannot teach the same subject multiple times (the req 'multiple different subjects' can be translated to 'multiple subjects'), then I'd rather use count(*) instead of count(distinct subj).

PostgreSQL join two tables with LIMIT 1

I have two tables:
First table "persons"
id | name |
---------------
1 | peter |
3 | martin |
5 | lucy |
Second table "meetings"
id | date | id_persons |
--------------------------------
1 | 2014-12-08 | 1 |
2 | 2013-05-10 | 2 |
3 | 2015-08-25 | 1 |
4 | 2016-10-18 | 1 |
5 | 2012-01-01 | 3 |
6 | 2016-09-28 | 5 |
I need somehow get only last date from "meeting" table for every person (or selected). And result table must be order by name. I thought, it could be like this, but WHERE clause in LEFT JOIN can't be used:
SELECT meetings.id, meetings.date, persons.name FROM persons
LEFT JOIN (SELECT meetings.date, meetings.id, meetings.id_persons FROM
meetings WHERE persons.id = meetings.id_persons ORDER BY
meetings.date DESC LIMIT 1) m ON m.id_persons = persons.id
WHERE persons.id < 6 ORDER BY persons.name
So I started with DISTINCT and it worked, but I think that it is not good idea:
SELECT * FROM
(SELECT DISTINCT ON (persons.id) persons.id, persons.name,
m.date, m.id FROM persons
LEFT JOIN (SELECT meetings.id, meetings.date, meetings.id_persons
FROM meetings ORDER BY meetings.date DESC) m
ON m.id_persons = persons.id
WHERE persons.id < 6 ORDER BY persons.id) p
ORDER BY p.name
Result what I need is:
name | date | id_meetings
-----------------------------------
lucy | 2016-09-28 | 6
martin | 2012-01-01 | 5
peter | 2016-10-18 | 4
Could you help me with better solution?
In Postgres, the easiest way is probably distinct on:
select distinct on (p.id) p.*, m.*
from persons p left join
meetings m
on m.id_persons = p.id
order by p.id, m.date desc;
Note: distinct on is specific to Postgres.

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

How to GROUP BY into separate columns

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