three tables relations SQL select - sql

I'm learning SQL, so help is greatly appreciated.
I have three tables: league, player and team
league and player is many to many (as a player can be in more than one league)
team and player is many to many (as a player can be on a team in multiple leagues)
league and team is one to many.
I have the following table ids:
league.id ----. league_has_player (league_id, player_id) .------ player.id
team.id ----. team_has_player (team_id, player_id) .----- player.id
league ----. team.id (team.league_id)
I want to get all the players in the league who are not on a team in the league.
Here's my broken attempt.
Select *
from player p,
join team t on t.league_id = l.id
join league l on league.id = 2
where p.id = league_has_player.player_id and
not in (select team_has_player.player_id from team_has_player)

I think you're trying more for something like this. As a couple comments have pointed out, your table schema isn't clear. But this is the impression I get from your attempt.
DECALRE #leagueId...
SELECT *
FROM player
WHERE player.id IN (SELECT player_id
FROM league_has_player
WHERE league_id = #leagueId) AND
player.id NOT IN (SELECT player_id
FROM team_has_player
INNER JOIN team ON team.Id = team_has_player.team_id
WHERE team.league_id = #leagueId)

Get all the players registered for the league, where they don't belong to a team in the league.
SELECT p.*, lp.league_id
FROM player p
JOIN league_has_player lp ON lp.player_id = p.id
WHERE NOT EXISTS (select 1
FROM team_has_player tp
JOIN team t ON t.id = tp.team_id
AND tp.player_id = p.player_id
AND t.league_id = lp.league_id)

Related

New to SQL, having trouble with table with two IDs

I have a DB of scores over the course of a season and I am having trouble with a query to return the season results for a given team. Here is a snapshot of the table involved:
Teams (tid, team_name)
Games (home_team_id, road_team_id, game_date, home_score, road_score)
What would the SQL look like to return all games where home_team_id or road_team_id is 1 and include the team_name of the opponent?
Not clearly stated in question, but I guess your main confusion is on how to get team_name from both home_team_id and road_team_id. You can do twice INNER JOIN for that purpose, for example :
select
g.*
, home.team_name as home_team_name
, road.team_name as road_team_name
from Games g
inner join Teams home on home.tid = g.home_team_id
inner join Teams road on road.tid = g.road_team_id
where g.home_team_id = 1
or g.road_team_id = 1

Complicated SQL Query involving multiple tables

In this Query, I have to list pair of players with their playerID and playerName who play for the exact same teams.If a player plays for 3 teams, the other has to play for exact same 3 teams. No less, no more. If two players currently do not play for any team, they should also be included. The query should return (playerID1, playername1, playerID2, playerName2) with no repetition such as if player 1 info comes before player 2, there should not be another tuple with player 2 info coming before player 1.
For example if player A plays for yankees and redsox, and player b plays for Yankees, Red Sox, and Dodgers I should not get them. They both have to play for Yankees, and Red Sox and no one else. Right now this query finds answer if players play for any same team.
player(playerID: integer, playerName: string)
team(teamID: integer, teamName: string, sport: string)
plays(playerID: integer, teamID: integer)
Right now the Query I have is
SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName
FROM player p1, player p2, plays
WHERE p1.teamID = p2.teamID AND teamID in.....
I am stuck on how to approach it after this. Any hints on how to approach this problem. Thanks for your time.
I think the easiest approach is to concatenate the teams together and just join on the results. Postgres provides the function string_agg() to aggregate strings:
select p1.playerId, p1.playerName, p2.playerId, p2.playerName
from (select p.playerId, string_agg(cast(p.TeamId as varchar(255)), ',' order by TeamId) as teams,
pp.PlayerName
from plays p join
players pp
on p.playerId = pp.playerId
group by p.playerId
) p1 join
(select p.playerId, string_agg(cast(p.TeamId as varchar(255)), ',' order by TeamId) as teams,
pp.PlayerName
from plays p join
players pp
on p.playerId = pp.playerId
group by p.playerId
) p2
on p1.playerid < p2.playerid and p1.teams = p2.teams;
EDIT:
You can do this without string_agg. The idea is to start with a list of all possible player combinations.
Then, join in the teams for the first player using left outer join. And join in the teams for the second by using full outer join and matching on the team and driver name. The reason you need the driver table is to be sure that the id/name does not get lost in the full outer join:
select driver.playerid1, driver.playerid2
from (select p1.playerId as playerId1, p1.playerName as playerName1,
p2.playerId as playerId2, p1.playerName as playerName2
from players p1 cross join
players p2
where p1.playerId < p2.playerId
) driver left outer join
plays p1
on p1.playerId = driver.playerId full outer join
plays p2
on p2.playerId = driver.playerId and
p2.teamid = p1.teamid
group by driver.playerid1, driver.playerid2
having count(p1.playerid) = count(*) and
count(p2.playerid) = count(*);
This joins two players on the team id (with ordering so a pair only gets considered once). It then says there is a match when all the rows for the two players have non-NULL team values. This is perhaps more clear with the equivalent having clause:
having sum(case when p1.playerid is null then 1 else 0 end) = 0 and
sum(case when p2.playerid is null then 1 else 0 end) = 0;
The full outer join will produce NULL values when two players have teams that don't match. So, no NULL values mean that all the teams match.
This is an adaptation of my answer to a previous question of yours.
Get all unique combinations of players using a triangular join:
SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName
FROM player p1
INNER JOIN player p2 ON p1.playerID < p2.playerID
Subtract the second player's team set from that of the first player and check if there are no rows in the result:
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
)
Swap the sets, subtract and check again:
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
)
Finally, apply both conditions to the result of the triangular join in Step 1.
SELECT p1.playerID, p1.playerName, p2.playerID, p2.playerName
FROM player p1
INNER JOIN player p2 ON p1.playerID < p2.playerID
WHERE
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
)
AND
NOT EXISTS (
SELECT teamID
FROM plays
WHERE playerID = p2.playerID
EXCEPT
SELECT teamID
FROM plays
WHERE playerID = p1.playerID
)
;

SQL, Struggling to believe HAVING can be more efficient than a join

I Am trying to find Players that have played in more than one game for a team in the following tables (** denotes private key), and find it hard to believe the best query I can come up with (below) is the most efficient. Ideas on how to improve it, and explanations as to why would be much appreciated (Trying to learn SQL)
Team (*tid*, name)
Game (*gid*, tid)
Player (*gid*, *name*)
SELECT Team_Name, Player_Name
FROM (SELECT GID, TID FROM GAME) G
,(SELECT NAME AS Player_Name, GID FROM PLAYER) P
,(SELECT NAME AS Team_Name, TID FROM TEAM) T
WHERE ( G.GID = P.GID
AND Player_Name IN (SELECT P.NAME
FROM GAME G
,PLAYER P
WHERE G.GID = P.GID
GROUP BY P.NAME
HAVING COUNT(P.NAME) > 1)
AND T.TID = G.TID
)
GROUP BY Team_Name, Player_Name
HAVING COUNT(Player_Name) > 1
ORDER BY Team_Name
You're asking which players have played in more than a single game.
SELECT P.Player_Name
FROM Player P
GROUP BY P.Player_Name
HAVING COUNT(DISTINCT P.GID) > 1
That will return all players who have played in more than 1 game (GID).
If you'd like to also GROUP BY team, then do this:
SELECT P.Player_Name, T.Team_Name
FROM Player P
JOIN Game G ON P.GID = G.GID
JOIN Team T ON G.TID = T.TID
GROUP BY P.Player_Name, T.Team_Name
HAVING COUNT(DISTINCT G.GID) > 1
It seems odd to have the GID in the Player table. Perhaps having a PlayerGames table would make more sense that stored the PlayerId and GameId -- better for database normalization. The Player table should only store a single record for each player.
Also, what is the real association between the player and the team. In this scenario, you're saying a player has to play a game, and a game has to have a team (or should a game have 2 or more teams). Let us know what you're going for, and we could help present your best option.
Good luck.

Trouble With GROUP BY Learning Derby SQL

I have formed this query to produce the pitcher from each team who has the most wins. My trouble comes in that I need to group them a certain way and I keep having scoping issues when trying to do so. W is the number of wins for each pitcher. Here is my pre-grouped statement...
SELECT
(SELECT p1.nameFirst FROM Players p1 Where (one.playerID = p1.playerID)),
(SELECT p1.nameLast FROM Players p1 Where (one.playerID = p1.playerID)),
one.W, (SELECT t1.name FROM Teams t1 Where(one.teamID = t1.teamID))
FROM Pitching one
Where (one.W >= ALL
(SELECT two.W
FROM Pitching two
Where (two.teamID = one.teamID)));
I need to group the tuples by league and within the leagues group by division. League (lgID) and division (divID) exist in the Teams table. Can someone point me in the right direction? Thank you.
This is top six rows of what is currently output...
Zach Britton 11 Baltimore Orioles
Mark Buehrle 13 Chicago White Sox
Madison Bumgarner 13 San Francisco Giants
Jhoulys Chacin 11 Colorado Rockies
Bruce Chen 12 Kansas City Royals
Kevin Correia 12 Pittsburgh Pirates
My desired output is to have these teams sorted by league (NL/AL) and within the leagues have them sorted by division.
Based on your updated comment. I think this is what you want.
SELECT
pl.nameFirst
, pl.nameLast
, p.W
, t.name
FROM
(
SELECT
MAX(p1.W) AS W
, p1.teamId
FROM
Pitching p1
GROUP BY
p1.teamId
) t1
JOIN
Pitching p
ON t1.W = p.W
AND t1.teamId = p.teamId
JOIN
Players pl
ON p.playerID = pl.playerID
JOIN
Teams t
ON p.teamID = t.teamID
ORDER BY
t.lgID
, t.divID
I do agree with swasheck, there are some opportunities for improvement in your schema. As swascheck said, teamId should be in Players. Not in Pitching.
This may be an issue of structure. Using what I believe to be your structure we could probably narrow it down like this:
select Players.nameFirst, Players.nameLast, TopPitcher.Winner,
Teams.name, League.Name, Division.Name
from (select playerID, max(Wins) as Winner
from (select playerID, teamID, count(W) as Wins
from Pitching
group by playerID, teamID ) PitchingWins) TopPitcher
join Players
on TopPitcher.playerID = Players.playerID
join Teams
on Teams.teamID = Players.teamID
join League
on Teams.leagueID = League.leagueID
join Division
on League.divisionID = Division.divisionID
order by League.Name, Division.Name
Now. Having said that, this is only for the structure you've given (with some other interpolation). Your overall structure is faulty as I would probably relate Player to Teams and not Pitching to Teams since you might get some sort of data errors regarding team wins vs. pitcher wins.

How to search for multiple values on inner join

I've got two tables, one with games and one with participants.
A game can have multiple participants. Now I need to search if a game is already inserted. I made a query with an inner join.
SELECT game.gameId
FROM game
INNER JOIN participants
WHERE game.gameId = participants.gameId
AND participants.name = 'Team1'
AND participants.name = 'Team2'
This isn't working the way I expected, is there a way to check if there is a match between teams 1 and 2 in one query?
Thanks!
edit
tablelayout:
**game**
PK gameId
date
**participants**
PK id
FK gameId
name
type //home or visiting
The following should work. It will JOIN games and participants using the gameID and ensure that the names are Team1 and Team2. This assumes that participants is a many to many with games and that there are only two teams per game.
SELECT participants.gameId
FROM game
JOIN participants
ON game.gameId = participants.gameId
WHERE participants.name = 'Team1'
OR participants.name = 'Team2'
GROUP BY participants.gameID
HAVING COUNT(*) = 2
You're not trying to relate game to participants, but participants to itself.
select
p1.gameId
from
participants as p1, participants as p2
where
p1.name = 'Team1' and p2.name='Team2' and p1.gameId = p2.gameId
you need to join twice with the participants table:
SELECT game.gameId
FROM game
INNER JOIN participants p1
ON game.gameId = p1.gameID
INNER JOIN participants p2
ON game.gameId = p2.gameID
WHERE (p1.name = 'Team1' AND p2.name = 'Team2')
OR (p2.name = 'Team1' AND p1.name = 'Team2')