How to iterate through a select within another select - sql

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

Related

need help writing a sql query with multiple join conditions

i have a sql query currently with a multiple inner join
select game_id, start_time_utc, t.name from games g
inner join teams t
on g.home_team_id = t.team_id
or g.away_team_id = t.team_id
the problem is i want t.name for both the home_team_id and the away_team_id however it is just giving the first instance with his home_team_id
how do i modify the sql query so i get back both the home team name and the away team name
You need to join the table twice.
select game_id, start_time_utc, t.name as home_team_name, t2.name as away_team_name
from games g
inner join teams t on g.home_team_id = t.team_id
inner join teams t2 on g.away_team_id = t2.team_id
You could select each team name using a correlated subquery:
select g.game_id, g.start_time_utc,
(select name from teams t where t.team_id=g.home_team_id) HomeTeam,
(select name from teams t where t.team_id=g.away_team_id) AwayTeam
from games g;

Find youngest in each team in database

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
);

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

SQL query on table that has 2 columns with a foreign id on the same table

I have a table let's say in the form of: match(id, hometeam_id, awayteam_id) and team(id, name). How do I build my SQL query in order to get a result table in the form of (match_id, hometeam_name, awayteam_name), since they both (hometeam_id, awayteam_id) reference the same table (team)?
Thank you
You would just join to the team table multiple times:
SELECT m.id, away.name, home.name
FROM match m
INNER JOIN team away ON away.id = m.awayteam_id
INNER JOIN team home ON home.id = m.hometeam_id
You join to the team table twice.
select matchdate, t1.teamname, t2,teamname from
match m
join team t1 on m.hometeamId = t1.teamid
join team t2 on m.awayteamid = t2.teamid
Join to the team table twice, once for the Home Team, and again for the Away Team, using aliases after the table names in the query:
select m.match_id, homeTeam.name as HomeTeamName, awayTeam.name as AwayTeamName
from
team homeTeam join
match m on m.hometeam_id = homeTeam.hometeam_id join
team awayTeam on awayTeam.hometeam_id = m.awayteam_id
select m.id, h.name as hometeam_name, a.name as awayteam_name
from match m left join team h on m.hometeam_id = h.id
left join team a on m.awayteam_id = a.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