Find youngest in each team in database - sql

I have a players table and a teams table and I am trying to find the youngest player in each team. I can find the youngest player:
SELECT lname, fname, dob, position
FROM players
WHERE dob = (SELECT MAX(dob) FROM players);
When I try to include the teams table I am receiving the same data as above, which is the youngest player overall. I am very new to this so I am still trying to understand multiple row subqueries. If I order by team name, I will receive the same output. Do I need another SELECT statement for the teams table as well? How would I go about doing that if so? Changing operator from IN to ALL or ANY will also give me the same output.
SELECT lname, fname, dob, position, name
FROM players p JOIN teams t ON p.team_id = t.id
WHERE dob IN (SELECT MAX(dob) FROM players);
I am working in LiveSQL if that helps.

You don't even need the teams table to just get the player information. A correlated subquery is sufficient:
SELECT p.lname, p.fname, p.dob, p.position
FROM players p
WHERE p.dob = (SELECT MAX(p2.dob)
FROM players p2
WHERE p2.team_id = p.team_id
);
You can introduce a JOIN to get the team name if you like:
SELECT p.lname, p.fname, p.dob, p.position, t.name
FROM players p JOIN
teams t
ON p.team_id = t.id
WHERE p.dob = (SELECT MAX(p2.dob)
FROM players p2
WHERE p2.team_id = p.team_id
);

You can use NOT EXISTS as follows:
SELECT LNAME, FNAME, DOB, POSITION, NAME
FROM PLAYERS P
JOIN TEAMS T
ON P.TEAM_ID = T.ID
WHERE NOT EXISTS (
SELECT 1
FROM PLAYERS P1
WHERE P1.TEAM_ID = P.TEAM_ID
AND P1.DOB < P.DOB
);

Related

SQL JOIN COUNT and GROUP BY

I have three tables
1. players(id, first_name, last_name, age, position, team_id)
2. teams(id, team_name, stadium, wins, draws,defeats,goal_for,goal_against)
3. goals_scored(id, player_id, goal_time)
SQL statement
SELECT
players.first_name,
players.last_name,
teams.name,
players.position,
players.age,
COUNT(*) AS goals
FROM
players
JOIN goals_scored
ON players.id = goals_scored.player_id
JOIN teams
ON players.team_id = teams.id
GROUP BY players.id;
teams table
id team_name stadium wins draws defeats goal_for goal_against
1 APF Club Dasharath 7 2 7 29 25
players table
id first_name last_name position age team_id
4 Dipendra Shrestha forward 19 1
goals_scored table
id player_id goal_time
1 4 34
2 4 57
I want to group goals on players id so that I can get the count of goals of an individual player.
Like
first_name last_name team_name position age goals
Dipendra Shrestha APF Club forward 19 2
How can I do it?
Prefer to group on as few as possible columns, especially if multiple tables get involved so that a good index can be applied to handle the group by.
WITH GoalsPerPlayer (playerId, nrOfGoals)
AS
(
SELECT player_id, count(*)
FROM goals_scored
GROUP BY player_id
)
SELECT p.first_name, p.last_name, t.team_name, p.position, p.age, g.numberOfGoals as goals
FROM GoalsPerPlayer g
INNER JOIN players p ON p.id = g.player_id
INNER JOIN teams t ON t.id = p.team_id
Edit:
Fixed typo's in query as mentioned by PO in comment below.
WITH GoalsPerPlayer (playerId, nrOfGoals)
AS
(
SELECT player_id, count(*)
FROM goals_scored
GROUP BY player_id
)
SELECT p.first_name, p.last_name, t.team_name, p.position, p.age, g.nrOfGoals as goals
FROM GoalsPerPlayer g
INNER JOIN players p ON p.id = g.playerId
INNER JOIN teams t ON t.id = p.team_id
Your query basically looks fine. I would adjust the GROUP BY to be more complete:
SELECT p.first_name, p.last_name, t.name, p.position, p.age,
COUNT(*) AS goals
FROM players p JOIN
goals_scored gs
ON p.id = gs.player_id JOIN
teams t
ON p.team_id = t.id
GROUP BY p.first_name, p.last_name, t.name, p.position, p.age;

How to iterate through a select within another select

I have two tables Players(Name,Surname,Goals,TeamID) and Teams(Name,TeamID).
In the following query I get the average goals' number per team and select the players who have a number of goals greater than the average.
SELECT Players.Name, Players.Surname, Teams.Name, Players.Goals, (SELECT ROUND(AVG(Players.Goals)) FROM Players GROUP BY Players.TeamID) AS TeamAverage
FROM Players
JOIN Teams ON Players.TeamID = Teams.TeamID
WHERE Players.Goals > (SELECT ROUND(AVG(Players.Goals))
FROM Players
GROUP BY Players.TeamID)
In the TeamAverage column I want to display the average goals' number per team, but the query only gets the average number of goals for the first team.
Who can I iterate through this select
(SELECT ROUND(AVG(Players.Goals)) FROM Players GROUP BY Players.TeamID)
and get each result according to the teams' ID?
Try this, I'd personally have gone down the route of using a temp table but this should also work for you. I gave your main tables an alias of P and T to help your sub query know what to compare against:
SELECT
P.Name,
P.Surname,
T.Name,
P.Goals,
(SELECT ROUND(AVG(Players.Goals)) FROM Players WHERE Players.TeamID = P.TeamID) AS TeamAverage
FROM
Players P
JOIN Teams T ON P.TeamID = T.TeamID
WHERE
P.Goals > (SELECT ROUND(AVG(Players.Goals))
FROM Players
WHERE Players.TeamID = P.TeamID)
By using the query in the parentheses you are creating a sub query that gets evaluated first and independently from the row in every row. If this is not part of a view and a query that needs to be run as part of a data collection or visualization, then a temp table would work fine by itself or in a stored procedure. If it needs to be in a view, then using a join into a subquery would work.
Using a temp table:
SELECT Players.TeamID as TeamID
,ROUND(AVG(Players.Goals)) as PlayerAverage
INTO #temp
FROM Players
Group BY Players.TeamID
SELECT tea.Players.Name
,tea.Players.Surname
,tea.Teams.Name
,tea.Players.Goals
,t.PlayerAverage
FROM Players
JOIN Teams tea ON Players.TeamID = Teams.TeamID
left join #temp t on Players.TeamID = t.TeamID
WHERE Players.Goals > t.PlayerAverage
using a joined subquery:
SELECT tea.Players.Name
,tea.Players.Surname
,tea.Teams.Name
,tea.Players.Goals
,t.PlayerAverage
FROM Players
JOIN Teams tea ON Players.TeamID = Teams.TeamID
left join (SELECT Players.TeamID as TeamID
,ROUND(AVG(Players.Goals)) as PlayerAverage
FROM Players
Group BY Players.TeamID) t on Players.TeamID = t.TeamID
WHERE Players.Goals > t.PlayerAverage
Hope this helps!
You could do something like this using a temp table to help store the results for teamAverage
CREATE TABLE #players (
goals INT,
NAME NVARCHAR(200),
surname NVARCHAR(200),
teamid INT
)
CREATE TABLE #teams (
NAME NVARCHAR(200),
teamid INT
)
INSERT INTO #teams (
NAME,
teamid
)
VALUES
('team1',1),
('team2',2),
('team3',3)
INSERT INTO #players (
goals,
NAME,
surname,
teamid
)
VALUES
(10,'bob','bob',1),
(2,'bob1','bob',1),
(5,'bob2','bob',1),
(1,'bob3','bob',3),
(3,'bob4','bob',3),
(2,'bob5','bob',3),
(1,'bob6','bob',2),
(2,'bob7','bob',2)
--Create temp table to store team average and teamid
SELECT round(avg(p.Goals), 2) AS average,
t.teamid
INTO #TeamAverages
FROM #players p
LEFT JOIN #teams t
ON p.teamid = t.teamid
GROUP BY t.TeamID
--join on above table and compare to team average table
SELECT p.NAME AS playerName,
p.Surname,
t.NAME AS teamName,
p.Goals,
ta.average
FROM #players p
LEFT JOIN #teams t
ON p.TeamID = t.TeamID
LEFT JOIN #TeamAverages ta
ON ta.teamid = t.teamid
WHERE p.Goals > ta.average
DROP TABLE #players
DROP TABLE #TeamAverages
DROP TABLE #teams

How to select SQL results based on multiple tables

I need to select results from one table based on certain matching values in a couple of other tables. I have the following tables:
person: id, firstname, lastname
team: id, teamname
player: id, person_id(FK), team_id(FK)
coach: id, person_id(FK), team_id(FK)
I need to return all the coaches and players names for each team. I've only ever used inner joins, and it doesn't seem like I can use those here, so any idea how to do this?
This will give you the coach:
SELECT team.Teamname, person.Firstname, person.Lastname
FROM person
JOIN coach ON person.id = coach.person_id
JOIN team ON coach.team_id = team.id
And this will give you the players:
SELECT team.Teamname, person.Firstname, person.Lastname
FROM person
JOIN player ON person.id = player.person_id
JOIN team ON player.team_id = team.id
So, the non-elegant, simple answer is to just toss it all together with UNION.
Just use an OR in the join to Team
SELECT
P.firstname,
P.lastname,
T.teamname
FROM
person p id
LEFT JOIN player pl
ON p.id = pl.person_id
LEFT JOIN coach c
ON p.id = c.person_id
LEFT JOIN team t
ON pl.team_id = t.id
or.c.team_id = t.id
Or if you perfer if and your database has COALESCE
LEFT JOIN team t
ON COALESCE(pl.team_id,c.team_id) = t.id

How to write this query to display a COUNT with other fields

I have following two tables:
Person {PersonId, FirstName, LastName,Age .... }
Photo {PhotoId,PersonId, Size, Path}
Obviously, PersonId in the Photo table is an FK referencing the Person table.
I want to write a query to display all the fields of a Person , along with the number of photos he/she has in the Photo table.
A row of the result will looks like
24|Ryan|Smith|28|6
How to write such query in tsql?
Thanks,
You need a subquery in order to avoid having to repeat all the columns from Person in your group by clause.
SELECT
p.PersonId,
p.FirstName,
p.LastName,
p.Age,
coalesce(ph.PhotoCount, 0) as Photocount
FROM
Person p
LEFT OUTER JOIN
(SELECT PersonId,
COUNT(PhotoId) as PhotoCount
FROM Photo
GROUP BY PersonId) ph
ON p.PersonId = ph.PersonId
SELECT
p.PersonId,
p.FirstName,
p.LastName,
p.Age,
CASE WHEN
t.ThePhotoCount IS NULL THEN 0 ELSE t.ThePhotoCount END AS TheCount
--the above line could also use COALESCE
FROM
Person p
LEFT JOIN
(SELECT
PersonId,
COUNT(*) As ThePhotoCount
FROM
Photo
GROUP BY PersonId) t
ON t.PersonId = p.PersonID
SELECT P.PersonId, FirstName, LastName,Age, COUNT(PhotoId) AS Num
FROM Person P
LEFT OUTER JOIN PHOTO PH ON P.PersonId = PH.PersonId
GROUP BY P.PersonId, FirstName, LastName,Age
select Person.*, count(PhotoId) from Person left join Photo on Person.PersonId = Photo.PersonId
IMO GROUP BY should be the solution, something like this works for me even with other table joins:
SELECT meetings.id, meetings.location, meetings.date, COUNT( users.id ) AS attendees
FROM `meetings`
LEFT JOIN users ON meetings.id = users.meeting_id
WHERE meetings.moderator_id = 'XXX'
GROUP BY meetings.id

How to count number of different items in SQL

Database structure:
Clubs: ID, ClubName
Teams: ID, TeamName, ClubID
Players: ID, Name
Registrations: PlayerID, TeamID, Start_date, End_date, SeasonID
Clubs own several teams. Players may get registered into several teams (inside same club or into different club) during one year.
I have to generate a query to list all players that have been registered into DIFFERENT CLUBS during one season. So if player swapped teams that were owned by the same club then it doesn't count.
My attempts so far:
SELECT
c.short_name,
p.surname,
r.start_date,
r.end_date,
(select count(r2.id) from ejl_registration as r2
where r2.player_id=r.player_id and r2.season=r.season) as counter
FROM
ejl_registration AS r
left Join ejl_players AS p ON p.id = r.player_id
left Join ejl_teams AS t ON r.team_id = t.id
left Join ejl_clubs AS c ON t.club_id = c.id
WHERE
r.season = '2008'
having counter >1
I can't figure out how to count and show only different clubs... (It's getting too late for clear thinking). I use MySQL.
Report should be like: Player name, Club name, Start_date, End_date
This is a second try at this answer, simplifying it to merely count the distinct clubs, not report a list of club names.
SELECT p.surname, r.start_date, r.end_date, COUNT(DISTINCT c.id) AS counter
FROM ejl_players p
JOIN ejl_registration r ON (r.player_id = p.id)
JOIN ejl_teams t ON (r.team_id = t.id)
JOIN ejl_clubs c ON (t.club_id = c.id)
WHERE r.season = '2008'
GROUP BY p.id
HAVING counter > 1;
Note that since you're using MySQL, you can be pretty flexible with respect to columns in the select-list not matching columns in the GROUP BY clause. Other brands of RDBMS are more strict about the Single-Value Rule.
There's no reason to use a LEFT JOIN as in your example.
Okay, here's the first version of the query:
You have a chain of relationships like the following:
club1 <-- team1 <-- reg1 --> player <-- reg2 --> team2 --> club2
Such that club1 must not be the same as club2.
SELECT p.surname,
CONCAT_WS(',', GROUP_CONCAT(DISTINCT t1.team_name),
GROUP_CONCAT(DISTINCT t2.team_name)) AS teams,
CONCAT_WS(',', GROUP_CONCAT(DISTINCT c1.short_name),
GROUP_CONCAT(DISTINCT c2.short_name)) AS clubs
FROM ejl_players p
-- Find a club where this player is registered
JOIN ejl_registration r1 ON (r1.player_id = p.id)
JOIN ejl_teams t1 ON (r1.team_id = t1.id)
JOIN ejl_clubs c1 ON (t1.club_id = c1.id)
-- Now find another club where this player is registered in the same season
JOIN ejl_registration r2 ON (r2.player_id = p.id AND r1.season = r2.season)
JOIN ejl_teams t2 ON (r2.team_id = t2.id)
JOIN ejl_clubs c2 ON (t2.club_id = c2.id)
-- But the two clubs must not be the same (use < to prevent duplicates)
WHERE c1.id < c2.id
GROUP BY p.id;
Here's a list of players for one season.
SELECT sub.PlayerId
FROM
(
SELECT
r.PlayerId,
(SELECT t.ClubID FROM Teams t WHERE r.TeamID = t.ID) as ClubID
FROM Registrations r
WHERE r.Season = '2008'
) as sub
GROUP BY PlayerId
HAVING COUNT(DISTINCT sub.ClubID) > 1
Here's a list of players and seasons, for all seasons.
SELECT PlayerId, Season
FROM
(
SELECT
r.PlayerId,
r.Season,
(SELECT t.ClubID FROM Teams t WHERE r.TeamID = t.ID) as ClubID
FROM Registrations r
) as sub
GROUP BY PlayerId, Season
HAVING COUNT(DISTINCT sub.ClubID) > 1
By the way, this works in MS SQL.
SELECT p.Name, x.PlayerID, x.SeasonID
FROM (SELECT DISTINCT r.PlayerID, r.SeasonID, t.ClubID
FROM Registrations r
JOIN Teams t ON t.ID = r.TeamID) x
JOIN Players p ON p.ID = x.PlayerID
GROUP BY p.rName, x.PlayerID, x.SeasonID
HAVING COUNT(*) > 1