A SQL query to get info from multiple tables - sql

OK, I'm an absolute beginner in SQL and I got one task to solve and I'm stuck, so I need help on your ideas how to get the required results.
I have 2 tables - first one is PARENTS, with the following data:
ID Name Age
1 John 25
2 Peter 28
3 Anny 30
4 Jack 32
and the second table is CHILDRENS, with the following data:
children_id parent_id name age
1 1 mary 5
2 1 Susanne 4
3 2 stephen 12
4 4 Kevin 7
What SQL command can be used to get following result:
id parent name number of childrens
1 John 2
2 Peter 1
3 Anny 0
4 Jack 1
Thanks in advance!

Try like this
select PARENTS.id,PARENTS.name,count(CHILDRENS.name)
from PARENTS left join CHILDRENS on PARENTS.id=CHILDRENS.parent_id
group by PARENTS.id,PARENTS.name

As mentioned by Jarlh in comments, Use LEFT OUTER JOIN + Group by
SELECT p.id,
p.name,
Count(parent_id) as number_of_childrens
FROM PARENTS p
LEFT OUTER JOIN CHILDRENS c
ON c.parent_id = p.ID
Group by p.id,
p.name

select p.id, p.name, count(p.id) as number_of_childrens
from parents p
left join childrens c
on p.parent_id = c.parent_id
group by p.id, p.name

SELECT A.ID
, A.name AS parent_name
, COUNT(B.children_id) AS number_of_children
FROM PARENTS AS A
LEFT JOIN CHILDRENS AS B
ON A.ID = B.parent_id
GROUP BY A.ID
, A.name
A left join, count and group by.

Related

how to get the whole name of person from database

I have these tables
person table with following data
person_id description
1 first in the family
2 second in the family
3 third in the family
4 fourth int the family
5 fifth in the family
person_name table with following data
person_id first_name
1 Santiago
2 Lautaro
3 Lucas
4 Franco
5 Agustín
father table with following data
person_father_id description
1 father of Lautaro
2 father of Lucas
3 father of Franco
4 father of Agustín
children table with following data
person_child_id person_father_id
2 1
3 2
4 3
5 4
how to get full name (Agustín Franco Lucas Lautaro Santiago) of person where person_id 4 in select pl/sql query. the core table is person
You could use a hierarchical query with an inline view that joins the relevant tables together first. The query for the inline view could be:
select p.person_id, pn.first_name, c.person_father_id
from person p
join person_name pn on pn.person_id = p.person_id
left join children c on c.person_child_id = p.person_id;
PERSON_ID FIRST_NAME PERSON_FATHER_ID
---------- ---------- ----------------
2 Lautaro 1
3 Lucas 2
4 Franco 3
5 Agustín 4
1 Santiago
and with that as the basis for a hierarchical query:
select trim(sys_connect_by_path(first_name, ' ')) as whole_name
from (
select p.person_id, pn.first_name, c.person_father_id
from person p
join person_name pn on pn.person_id = p.person_id
left join children c on c.person_child_id = p.person_id
)
where connect_by_isleaf = 1
start with person_id = 4
connect by person_id = prior person_father_id;
WHOLE_NAME
--------------------------------------------------
Franco Lucas Lautaro Santiago
Or you could make the hierarchical query itself a further subquery, and then join to the names afterwards and aggregate that:
select listagg(pn.first_name, ' ') within group (order by lvl) as whole_name
from (
select person_id, level as lvl
from (
select p.person_id, c.person_father_id
from person p
left join children c on c.person_child_id = p.person_id
)
start with person_id = 4
connect by person_id = prior person_father_id
) t
join person_name pn on pn.person_id = t.person_id;
WHOLE_NAME
--------------------------------------------------
Franco Lucas Lautaro Santiago
Notice that with both of those you have to join the tables before you can filter based on your starting ID (start with rather than where, as this is hierarchical). That means that with larger tables it could end up doing more work than you really need or expect.
Or you could do the same thing with recursive subquery factoring (a recursive CTE) if you prefer, and you are using Oracle 11gR2 or higher:
with r (person_id, person_father_id, lvl) as (
select p.person_id, c.person_father_id, 1
from person p
left join children c on c.person_child_id = p.person_id
where p.person_id = 4
union all
select p.person_id, c.person_father_id, r.lvl + 1
from r
join person p on p.person_id = r.person_father_id
left join children c on c.person_child_id = p.person_id
)
select listagg(pn.first_name, ' ') within group (order by lvl) as whole_name
from r
join person_name pn on pn.person_id = r.person_id;
WHOLE_NAME
--------------------------------------------------
Franco Lucas Lautaro Santiago
which looks more complicated but can at least put the filter in the anchor member of the recursive CTE.
Read more about hierarchical queries and recursive subquery factoring.

Select all categories with COUNT of sub-categories

I need to select all categories with count of its sub-categories.
Assume here are my tables:
categories
id | title
----------
1 | colors
2 | animals
3 | plants
sub_categories
id | category_id | title | confirmed
------------------------------------
1 1 red 1
2 1 blue 1
3 1 pink 1
4 2 cat 1
5 2 tiger 0
6 2 lion 0
What I want is :
id | title | count
------------------
1 colors 3
2 animals 1
3 plants 0
What I have tried so far:
SELECT c.id, c.title, count(s.category_id) as count from categories c
LEFT JOIN sub_categories s on c.id = s.category_id
WHERE c.confirmed = 't' AND s.confirmed='t'
GROUP BY c.id, c.title
ORDER BY count DESC
The only problem with this query is that this query does not show categories with 0 sub categories!
You also can check that on SqlFiddle
Any help would be great appreciated.
The reason you don't get rows with zero counts is that WHERE clause checks s.confirmed to be t, thus eliminating rows with NULLs from the outer join result.
Move s.confirmed check into join expression to fix this problem:
SELECT c.id, c.title, count(s.category_id) as count from categories c
LEFT JOIN sub_categories s on c.id = s.category_id AND s.confirmed='t'
WHERE c.confirmed = 't'
GROUP BY c.id, c.title
ORDER BY count DESC
Adding Sql Fiddle: http://sqlfiddle.com/#!17/83add/13
I think you can try this too (it evidence what column(s) you are really grouping by):
SELECT c.id, c.title, RC
from categories c
LEFT JOIN (SELECT category_id, COUNT(*) AS RC
FROM sub_categories
WHERE confirmed= 't'
GROUP BY category_id) s on c.id = s.category_id
WHERE c.confirmed = 't'
ORDER BY RC DESC

PostgreSQL - Selecting count of unique values in one and two columns

Firstly, I'd like to apologise for the ambiguous title (I promise to revise it once I'm actually aware of the problem I'm trying to solve!)
I have two tables, player and match, which look like the following:
player:
id name
-- ----
1 John
2 James
3 April
4 Jane
5 Katherine
match:
id winner loser
-- ------ -----
1 1 2
2 3 4
Records in the match table represent a match between two players, where the id column is generated by the database, and the values in the winner and loser columns reference the id column in the player table.
I want to run a query which spits out the following:
player.id player.name total_wins total_matches
--------- ----------- ---------- -------------
1 John 1 1
2 James 0 1
3 April 1 1
4 Jane 0 1
5 Katherine 0 0
I currently have a query which retrieves total_wins, but I'm not sure how to get the total_matches count on top of that.
select p.id, p.name, count(m.winner)
from player p left join match m on p.id = m.winner
group by p.id, p.name;
Thanks for your help!
Try
select p.id, p.name,
sum(case when m.winner = p.id then 1 end ) as total_wins,
count(m.id) as total_matches
from player p
left join match m on p.id in ( m.winner, m.loser )
group by p.id, p.name;
One method splits the match match table, so you have a single row for each win and loss. The rest is just a left join and aggregation:
select p.id, p.name, coalesce(sum(win), 0) as win, count(m.id) as total_matches
from player p left join
(select match, winner as id, 1 as win, 0 as loss from match
union all
select match, loser as id, 0 as win, 1 as loss from match
) m
on p.id = m.id
group by p.id, p.name;

Select the value of a sibling row that can only be found through a parent row in SQL Server

I only have 1 table called Posts with 5 columns (much like StackExchange Data Explorer)
Id, Score, PostTypeId, ParentId, AcceptedAnswerId
1 5 1 null 3
2 3 1 null 5
3 9 2 1 null
4 3 2 1 null
....
As you can see, the rows have two levels of hierarchy (Parents, Childs). Let's say I want to select the childs (PostTypeId = 2) and for each one I would like to left join the score of the sibling that is currently accepted by the parent. The accepted sibling Id can only be found in the AcceptedAnswerId column of the parent row. The parent row can be found through the ParentId of the child row I am selecting. Is there an elegant way to do this?
The expected results is the following
Id, Score, AcceptedScore
3 9 9
4 3 9
....
My attempt (although it doesn't work and hurts my eyes as well as my brain):
select top 50 a.Id, a.Score, b.Score as AcceptedScore
from Posts as a
left join (
select c.Score from Posts as c
where c.Id = (
select d.AcceptedAnswerId from Posts as d
where d.Id = a.ParentId
)
) as b
on b.ParentId = a.ParentId
where a.PostTypeId = 2
The error I'm getting:
The multi-part identifier "a.ParentId" could not be bound. Invalid column name 'ParentId'.
Here's one that works:
SELECT p1.Id, p1.Score, p3.Score AS AcceptedScore
FROM Posts p1 LEFT JOIN Posts p2
ON p1.ParentId = p2.Id LEFT JOIN Posts p3
ON p2.AcceptedAnswerId = p3.Id
WHERE p1.PostTypeId = 2
See the SQLFiddle

Return 1 result per left join

Currently I am performing a left join on two tables. The first table has an id and a persons name, the second table has an id, the id of a person from table 1, and then a timestamp (of the last flight they had).
People Flights
id | name id | person_id | time
------------ ---------------------------
1 Dave 1 1 1284762115
2 Becky 2 1 1284787352
3 2 1284772629
4 2 1286432934
5 1 1289239480
When I perform my left join, I get a list of people and their flight times, but what I would like is just the list of people with their last flight times.
So SELECT p.id, p.name, f.time FROM People p LEFT JOIN Flights f ON p.id = f.person_id
Returns
1 Dave 1284762115
1 Dave 1284787352
1 Dave 1289239480
2 Becky 1284772629
2 Becky 1286432934
I would like to see just:
1 Dave 1289239480
2 Becky 1286432934
So I need to return only the match with the highest f.id or the highest f.time
SELECT
p.id, p.name, MAX(f.time) AS LastFlight
FROM
People p
LEFT JOIN Flights f ON p.id = f.person_id
GROUP BY
p.id, p.name
SELECT p.id, p.name, f.time FROM People p LEFT JOIN
(select person_id, max(time) time from flights group by person_id) f
ON p.id = f.person_id
Try this:
;with LastFlightTimes as
(
select person_id, max(id) maxid
from Flights f
group by person_id
)
SELECT p.id, p.name, f.time FROM People p
LEFT JOIN LastFlightTimes lft ON p.id = lft.person_id
left join Flights f on f.id = lft.maxid