My sql query with two tables - mysql

I have two tables named person and person_sibling. The person_sibling table contains the sibling id which refer to the sibling of the person( For ex: sibling of the person SANGEETHA is SURESH). I need to get all the names whose age is older than their sibling age. The table data is shown below
Person table:
NAME AGE FNAME ID GENDER
SANGEETHA 20 PONNURANGAM 2 FEMALE
SARANYA 22 CHOKALINGAM 3 FEMALE
KANNA 22 ALAGRI 4 MALE
LAVANYA 21 MUNISWAMI 1 FEMALE
SURESH 25 PONNURANGAM 20 MALE
SARALA 26 CHOKALINGAM 21 FEMALE
KARAN 20 ALAGRI 2 MALE
ARTHI 20 ALAGRI 25 FEMALE
person_sibling table:
ID SIBLING_ID
2 20
3 21
4 25
4 22
20 2
21 3
25 4
22 4
22 25
25 22
I've tried this:
SELECT name
FROM person p , person_sibling s
where p.id=s.sibling_id and p.age <=(select age from person where id=s.sibling_id)
But I can't able to get it. Can someone help please

SELECT p.name
FROM person p
inner join person_sibling s on p.id = s.id
inner join person sp on s.sibling_id = sp.id
where p.age > sp.age

A left join can give you all people that don't have an older sibling, it'll join to find all older siblings and a null check will just give you the rows where none exists.
SELECT DISTINCT p.* FROM person p
LEFT JOIN person_sibling ps ON p.id = ps.id
LEFT JOIN person s ON ps.sibling_id = s.id AND s.age > p.age
WHERE s.id IS NULL;
An SQLfiddle to test with.

Related

SQL: elegant way to get first, second, and third degree associations

I have tables named course, student and students_in in a MySQL database. The tables look like this:
course
course_id name
3 Physics
12 English
19 Basket Weaving
4 Computer Science
212 Discrete Math
102 Biology
20 Chemistry
50 Robotics
7 Data Engineering
student
id name
2 Sally
1 Bob
17 Robert
9 Pierre
12 Sydney
41 James
22 William
5 Mary
3 Robert
92 Doris
6 Harry
students_in
course_id student_id grade
3 2 B
212 2 A
3 12 A
19 12 C
3 41 A
4 41 B
212 41 F
19 41 A
12 41 B
3 17 C
4 1 A
102 1 D
102 22 A
20 22 A
20 5 B
50 3 A
12 92 B
12 17 C
7 6 A
Here is a Fiddle: http://sqlfiddle.com/#!17/8d86ee/34
My goal is to get the id and name of the students who:
have taken a course with Sally (i.e. "first-degree" relationship), OR
have taken a course with someone who has taken a course with Sally (i.e. "second-degree" relationship), OR
have taken a course with someone who has taken a course with someone who has taken a course with Sally (i.e. "third-relationship" relationship)
Essentially, we're looking for first-, second-, and third-degree relationships to Sally.
Here is a depiction of what this looks like:
Since Sally took course IDs 3 and 212, the desired result would look like this (not the colorful table above, which I provided for illustration of the logic involved):
student_id student_name
12 Sydney <-- took course ID 3 with Sally
41 James <-- took course ID 3 and 212 with Sally
17 Robert <-- took course ID 3 with Sally
1 Bob <-- took course ID 4 with James
92 Doris <-- took course ID 12 with James and Robert
102 William <-- took course ID 102 with Bob
I tried to solve this problem by using a Common Table Expression (CTE) to query the first-degree relationships, and can probably use two additional CTEs to get the second-degree and third-degree relationships. But, this feels like a very inelegant way to do this.
Can someone please help with an elegant approach to this problem?
Thank you!
You can use a recursive cte:
with recursive cte(cid, sid, name, l) as (
select si.course_id, si.student_id, s.name, 1 from students_in si
join student s on s.id = si.student_id where si.course_id in (select si1.course_id
from students_in si1 join student s1 on s1.id = si1.student_id and s1.name = 'Sally') and s.name != 'Sally'
union all
select si.course_id, si.student_id, s.name, c.l + 1 from cte c
cross join students_in si
join student s on s.id = si.student_id
where si.course_id in (select si1.course_id
from students_in si1 where si1.student_id = c.sid) and si.course_id != c.cid and si.student_id != c.sid and c.l < 3
)
select distinct sid, name from cte where name != 'Sally'
See fiddle.
With Recursive coursemates As (
Select y.student_id, 1 as removal
From students_in x Inner Join students_in y
On x.course_id=y.course_id
Where x.student_id=2
UNION
Select y.student_id, r.removal+1
From coursemates r Inner Join students_in x
On r.student_id=x.student_id
Inner Join students_in y
On x.course_id=y.course_id
Where removal<=2
)
Select c.student_id, min(c.removal) as howfar, min(s.name) as student_name
From coursemates c Left Outer Join student s
On c.student_id=s.student_id
Where student_id <> 2
Group By c.student_id
Order by 2, 1
A little verbose, but also a little more generalized than your try in that you can control the depth.
A few defensive additions: 1. Left join on student table in case no R.I. there. 2. Filter out Sally from the result (don't care that Robert was with Sally and then Sally was with Robert)
Join's repeated as many times as needed, also good, but perhaps less elegant:)
with rel as ( --join student->student thru course
select t1.student_id s1Id, t2.student_id s2Id
from students_in t1 inner join students_in t2
on t2.course_id=t1.course_id
where t2.student_id<>t1.student_id
group by t1.student_id,t2.student_id
)
,four as(
select t1.s1Id as s1Id1 ,t2.s1Id s2Id1,t2.s2Id s2Id2 ,t3.s2Id s3Id2
from rel t1 left join rel t2 on t2.s1Id=t1.s2Id and t2.s2Id<>t1.s1Id
left join rel t3 on t3.s1Id=t2.s2Id and t2.s2Id<>t1.s1Id
and t3.s2Id<>t1.s1Id
where t1.s1Id=2
group by t1.s1Id ,t2.s1Id,t2.s2Id,t3.s2Id
)
select t1.s1Id1,t1,s3Id2,s1.name,s3.name,s4.name,s6.name
from four t1
inner join student s1 on t1.s1Id1=s1.id
inner join student s3 on t1.s2Id1=s3.id
inner join student s4 on t1.s2Id2=s4.id
inner join student s6 on t1.s3Id2=s6.id

How to count how many having how many in mysql

I have persons i one table [person] and cars [car] registered to each person i another, connected by id.
I want to get a list of how many persons having how many cars.
Something like this:
PERSON
id
name
1
LISA
2
ADAM
3
RAY
CARS
id
id_person
brand
1
3
FORD
2
1
BMW
3
2
VOLVO
4
1
VOLVO
5
1
VW
RESULT
no cars
no persons
1
2
2
0
3
1
Just dont get how to do it?
You need to do it in two stages. The first is to get the number of cars per person -
SELECT p.id, COUNT(*) num_cars
FROM person p
JOIN cars c ON p.id = c.id_person
GROUP BY p.id
This can then be nested to count people per count -
SELECT num_cars, COUNT(*) num_people
FROM (
SELECT p.id, COUNT(*) num_cars
FROM person p
JOIN cars c ON p.id = c.id_person
GROUP BY p.id
) t
GROUP BY num_cars
ORDER BY num_cars ASC

MySQL: join three tables and get counts

I am so totally lost.
Working on a database for my co-rec team. Players, Matches, Available players for a match, chosen players for a match, etc.....
The first major step I'd like is to be able to combine Players, Matches and Available to get a list of matches with the number of Women and number of Men available.
Here are my tables:
Players (id, Gender, Name, ....)
id Gender Name
1 M David
2 M Alberto
3 F Alison
4 F Karen
5 F Callie
6 M Stephan
Matches (id, ...)
id
1
2
3
Available (id, matchID, playerID)
id matchID PlayerID
1 1 1
2 1 8
3 1 11
... ... ...
16 2 1
17 2 2
18 2 15
... ... ...
26 3 6
27 3 7
28 3 18
Desired Result
Match Women Men Total
1 5 10 15
2 4 6 10
3 6 10 16
... ... ... ...
Here's the closest I've got (just this morning):
select m.id, p.gender
from matches m
inner join available a on m.id = a.matchid
inner join players p on p.id = a.playerid
Morning clarity:
select m.id,
sum(case when p.gender="Male" then 1 else 0 end) "Males",
sum(case when p.gender="Female" then 1 else 0 end) "Females",
count(p.gender) "Total"
from matches m
inner join available a on m.id = a.matchid
inner join players p on p.id = a.playerid
group by m.id

Multiple Counts from many INNER JOIN tables with Conditions

I'm having a lot of trouble figuring out how to write this query. Here's an exmaple of the data set and what I need to query:
**System Table**
SystemID Active
1 T
2 T
3 T
4 F
5 F
6 F
**BlogPost Table**
BlogPostID SystemID Create_Month
100 2 Jan
101 2 Jan
102 2 Feb
103 3 Feb
104 3 Mar
105 6 Mar
106 6 Mar
**Comment Table**
Comment ID BlogPostID Liked
201 100 T
202 100 T
203 100 T
204 102 T
205 102 T
206 102 T
207 103 F
So, In words, I'm trying to get: By month, show me all the active systems who created a post during that month, the number of posts they made in aggregate, and the count of the subset of those posts who had a comment that was like.
The end result would be like:
Column 1 - Month
Column 2 - Count of Active Systems where a Post Created in Month
Column 3 - Count of Posts Applicable to those systems
Column 4 - Count of Applicable Posts that had comments that were liked
I don't even know where to start really. My terrible "this is obviously wrong" attempt is below. Any help is much appreciated, thanks!
SELECT
Month,
COUNT(DISTINCT system.systemid),
COUNT(blogpost.BlogPostID)
COUNT(comments.commentiD)
FROM
system INNER JOIN
blogpost ON system.systemid = blogpost.systemid INNER JOIN
comments ON blogpost.BlogPostID = comment.BlogPostID
WHERE
system.active = T
AND comments.like = T
GROUP BY month
A complicated one !
SELECT
b.Create_Month,
COUNT(DISTINCT s.SystemID) as SystemCount,
COUNT(DISTINCT b.BlogPostID) as PostsCount,
COUNT(DISTINCT t.BlogPostID) as PostsWithLike
FROM System s
JOIN BlogPost b
ON s.systemID = b.systemID
AND s.Active = 'T'
LEFT JOIN Comment c
ON b.BlogPostID = c.BlogPostID
LEFT JOIN
(
SELECT DISTINCT c.BlogPostID as BlogPostID
FROM
Comment c
GROUP BY c.BlogPostID
HAVING SUM(if(c.Liked='T',1,0))>0
) as t
ON b.BlogPostID = t.BlogPostID
GROUP BY b.Create_Month
This is probably what you want :
SELECT s.systemid, active, bp.create_month, bp.systemid, COUNT(bp.blogpostid), COUNT(c.liked)
FROM system AS s
LEFT OUTER JOIN Blogpost AS bp ON s.systemid = bp.systemid
LEFT OUTER JOIN Comment AS c ON bp.blogpostid = c.blogpostid
WHERE active = 'T' AND c.Liked = 'T' GROUP BY s.systemid,bp.create_month

SQL Sorting with Two Joined Tables

I have these two tables
people
============
id, name
and
answer_sheets
============
id, person_id, answer, date_answered
person_id is a foreign key from people.id
Now, what I wanted to do is to sort people in such in order basing it on the latest answer_sheets.date_answered (we can derive that one people row can have many answer_sheets rows)
Say for example we have the tables
people
============
id name
1 Person1
2 Person2
3 Person3
4 Person4
5 Person5
answer_sheets
=============
id person_id answer date_answered
1 1 string JUN 13
2 2 string JUN 15
3 3 string JUN 17
4 2 string JUN 18
5 1 string JUN 19
6 3 string JUN 20
7 2 string JUN 25
and I wanted to order people in ASC order based on a people row's answer_sheets.date_answered
the output must be
=============
id name last_date_answered
4 Person4 NIL
5 Person5 NIL
1 Person1 JUN 19
3 Person3 JUN 20
2 Person2 JUN 25
You can observe that people with ids 4 and 5 does not have an answer_sheet and yet they should be included in the list.
Question: What must be the appropriate SQL query for this? Thanks.
To get records to display even if there is no match, you can use a LEFT JOIN.
SELECT p.id, p.name, MAX(a.date_answered)
FROM people p
LEFT JOIN answer_sheets a on p.id = a.personID
GROUP BY p.id, p.name
ORDER BY MAX(date_answered) ASC
And, if you want to try it out, or play with it more, I made a SQLFiddle...
SELECT
people.id,
people.name,
baseview.last_date_answered
FROM (
SELECT
person_id,
MAX(date_answered) AS last_date_answered
FROM answer_sheets
ORDER BY IFNULL(MAX(date_answered),'0001-01-01')
) AS baseview
INNER JOIN people ON bseview.person_id=people.id
SELECT *
FROM people
CROSS APPLY ( SELECT MAX(date_answered) MaxDate
FROM answer_sheets
WHERE answer_sheets.PersonID = people.ID
) L