Double inner join with 3 tables - sql

Update years later:
I came back to answer my own question. I had no idea what an inner join was and apparently completely misused the term here.
I currently have the following 3 tables:
Team
TeamID
Player
PlayerID | TeamID
Match
Player1 | Player2 | Round
I want to write a query showing a match like:
Team1 | Team2
I figured out how to join my tables player and match, so far that works too, but I can't figure out how to replace the player name with the team name. Any idea? I have the following:
SELECT P1.PlayerID AS Player1, P2.PlayerID AS Player2
FROM MATCH
INNER JOIN PLAYER AS P1 ON MATCH.Player1 = P1.PlayerID
INNER JOIN PLAYER AS P2 ON MATCH.Player2 = P2.PlayerID

I've come back to this question years later.
Assuming the following tables:
Team
id | name
Player
id | team_id
Match
player1_id | player2_id | round
The result I wanted was something like
player1's team name | player2's team name | round
I used distinct originally, but distinct is not the correct way to remove duplicates, as Turophile correctly points out.
A better query would be something like
select t1.name, t2.name, m.round
from match as m
left join player as p1 on m.player1_id = p1.id
left join team as t1 on p1.team_id = t1.id
left join player as p2 on m.player2_id = p2.id
left join team as t2 on p2.team_id = t2.id

Related

SQL Group rows in left join into one

i try create query with left join when i combine multiple rows into one. I try with GROUP_CONTENT function but when i try use it my db server is going down. I use MariaDB 10.3.17. I have tables like:
Games:
game_id game_name
1 Test
2 Stack
3 Other
data_developers:
dev_id dev_name
1 Electronic Arts
2 BioWare
3 2K Games
game_developers
developer_id game_id
1 1
2 1
2 3
Result i want:
game_id game_name devs
1 Test Electonics Arts, BioWare
2 Stack 2K Games
my two sql (but didnt work)
SELECT games.*, GROUP_CONCAT(data_developers.dev_name)
FROM games
LEFT JOIN game_developers ON game_developers.game_id = games.game_id
LEFT JOIN data_developers ON data_developers.dev_id = game_developers.dev_id
LIMIT 500
and second query
SELECT games.*
FROM games
LEFT JOIN game_developers ON game_developers.game_id = games.game_id
LEFT JOIN
(SELECT GROUP_CONCAT(data_developers.developer_name) as developers,
data_developers.developer_id FROM data_developers) x
ON x.developer_id = game_developers.developer_id
But of course, also dont work :(
Your query should be something like below-
SELECT A.game_id,B.Game_name,GROUP_CONCAT(C.dev_name)
FROM game_developers A
INNER JOIN Games B ON A.game_id = B.game_id
INNER JOIN data_developers C ON A.developer_id = C.dev_id
GROUP BY A.game_id,B.Game_name

Database multiple selection

I've the following 2 tables:
Players
id name
p1 name1
p2 name2
p3 name3
p4 name4
Matches
id winner loser
m1 p1 p2
m2 p3 p4
what i want is to write a select statement that will return the following:
id name matches wins
p1 name1 1 1
p3 name3 1 1
p2 name2 1 0
p4 name4 1 0
this is basically to return the tournament standings, ordered by number of wins.
One method uses correlated subqueries:
select p.*,
(select count(*) from matches m where p.id in (m.winner, m.loser)
) as matches,
(select count(*) from matches m where p.id = m.winner
) as wins
from players p
order by wins desc;
Another way is to use two outer joins:
select p.id,
p.name,
count(distinct m.id) as matches,
count(distinct w.id) as wins
from players p
left join matches m on p.id in (m.winner, m.loser)
left join matches w on w.winner = p.id
group by p.id, p.name
order by wins desc, p.name
You can add 2 columns to Players table or create new table Tournament named. You can register Tournament Players with your hand(manually) on your admin panel.
If will you add 2 columns to Players table. U will query Players win scores, after query get your MySQL number of rows function and you will get hoe much win The Player. After writenitnto Tournament page. (Note: Sorry for bad English, im trying to speak English. Please try to understand me.)

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

Query on table joined with itself

Today I have a final Exam. I approved, happily :D but one of the problems is really blowing my mind.
I need help, so I can rest in peace.
THE PROBLEM
We have a table "People"
(PK)id | name | fatherID
---------------------
1 | gon | 2
2 | cesar| 6
3 | luz | 2
4 | maria| 5
5 | diego| 6
6 | john | -
this is only an example of data.
This table has a relation with itself, on table fatherId(FK) with table id(PK)
I need to do a query that show me 2 columns, in one the name of a person, and in the another one, his/her cousin.
Pretty simple until here, right?
The problem is that I have some restrictions
ONLY ANSI allowed. NO T-sql, or another one. Also, ANSI 99 standard, not 2003 or higher
subquerys are not allowed. And the worst:
NO relations repeated.
For example, considering in this example, gon and maria are cousins.
If I show, gon | maria in the results, I can't show maria | gon.
SO, how I can do this?
Is really burning my head.
What I tried?
Well, the big problem was in the last requisite, the repetition of data. Ignoring that, I put this on my exam (knowing is wrong..)
select p3.name as OnePerson, p4.name as Cousin
from
people p1
inner join people p2 on p1.fatherid = p2.fatherid and p1.id != p2.id
inner join people p3 on p1.id = p3.fatherid
inner join people p4 on p1.id = p4.fatherid
of course, this is not solving the last requeriment, and I have a 4 in the test(we pass with 4) but anyway, my head is burning. So please, help me!
Another options explored
one of my friends, that also had the same exam said me
"Well, considering every relation is duplicated, I can use top
count(*) and an order by and get the half correct"
but.. Top is not ANSI!
You can add to your query WHERE p3.id < p4.id. This will eliminate duplicate results like gon | maria and maria | gon.
SELECT T1.id , T2.id FROM
(
SELECT A.id,A.fid FROM family A
WHERE a.fid IN
(
SELECT id FROM family
WHERE fid IN (SELECT id FROM family WHERE fid IS NULL)
)
)T1
JOIN
(
SELECT A.id,A.fid FROM family A
WHERE a.fid IN
(
SELECT id FROM family
WHERE fid IN (SELECT id FROM family WHERE fid IS NULL)
)
)T2
ON t1.fid<>t2.fid
AND t1.id<t2.id
This will give you the results in format you want.
SELECT TAB1.ID,TAB2.ID
FROM
(
SELECT * FROM people T1
WHERE fatherID IN ( SEL T1.ID FROM people T1 INNER JOIN people T2
ON( T1.id=T2.fatherID) WHERE T1.fatherID IS NOT NULL GROUP BY 1) ) TAB1
INNER JOIN
(
SELECT * FROM people T1
WHERE fatherID IN ( SEL T1.ID FROM people T1 INNER JOIN people T2
ON( T1.id=T2.fatherID)WHERE T1.fatherID IS NOT NULL GROUP BY 1) ) TAB2
ON( TAB1.fatherID<>TAB2.fatherID)
GROUP BY 1,2
WHERE TAB1.ID <TAB2.ID;

SQL - Left join 2 foreign keys to 1 primary key

I have two tables, Games and Teams. What should my sql statement look like to make a list of games that pulls in the TeamName that is linked to the TeamID1 and TeamID2 fields? I believe I could use a left join but I'm not sure what to do with two foreign keys that link to one primary key. Thank you very much for any help you could provide.
Games
GameID
TeamID1
TeamID2
Result
Teams
TeamID
TeamName
I often see folks struggle with the idea of joining a table unto itself or multiple times in the same query (as it were here). Once mastered, it's a great technique to use on tables that have a lot of relationships between rows (such as a list of teams that have to play each other!). As others have pointed out, you need to use two inner joins to accomplish this:
select
*
from
games g
inner join teams t1 on
g.teamid1 = t1.teamid
inner join teams t2 on
g.teamid2 = t2.teamid
So, if your games table looks like this:
GameID TeamID1 TeamID2
----------------------------
1 1 3
2 4 2
3 2 1
You will get the result set of:
g.GameID g.TeamID1 g.TeamID2 t1.TeamID t1.Name t2.TeamID t2.Name
----------------------------------------------------------------------------------
1 1 3 1 Lions 3 Bears
2 4 2 4 Oh My 2 Tigers
3 2 1 2 Tigers 1 Lions
Of course, I would alias these columns in the select statement, if I were me, for usability's sake:
select
g.GameID,
t1.Name as Team1,
t2.Name as Team2
from
...
This way, columns can be named appropriately, instead of having the t1 and t2 columns share the same names.
Now, to address the confusion about what a left join is. You see, a left join will take all of the rows from the first (or left) table, and then match up any rows on the join condition to the second (or right) table. For any rows from the left table, you will get null in all of the columns on the right table.
Delving into an example, let's say that somebody put in a null for TeamID2 on one of the rows for whatever reason. Let's also say that a team of TeamID 4 used to exist, but doesn't any more.
GameID TeamID1 TeamID2
----------------------------
1 1 3
2 4 2
3 1 null
Now, let's take a look at what a left join would be in terms of the query:
select
*
from
games g
left join teams t1 on
g.teamid1 = t1.teamid
left join teams t2 on
g.teamid2 = t2.teamid
Logically, this will grab all of our games, and then match them up to the respective teams. However, if a TeamID doesn't exist, we'll get nulls. It will look like so:
g.GameID g.TeamID1 g.TeamID2 t1.TeamID t1.Name t2.TeamID t2.Name
----------------------------------------------------------------------------------
1 1 3 1 Lions 3 Bears
2 4 2 null null 2 Tigers
3 1 null 1 Lions null null
Therefore, a left join will only be necessary if a team is optional.
In your case, you'll be using an inner join to join a table multiple times. This is a very common practice and is rather useful. It avoids some of the pitfalls of subqueries (especially on MySQL), while allowing you to grab data from the table for intratable comparisons. This is markedly useful when trying to find the order of something, or related rows.
Anyway, I hope this very rambling answer helps out somebody somewhere.
SELECT *
FROM Games G
INNER JOIN Teams T1
ON G.TeamID1 = T1.TeamID
INNER JOIN Teams T2
ON G.TeamID2 = T2.TeamID
I don't think you'll need a left outer join unless one of the two teams in a game is optional. Logically to me it seems that both would be required, so your query would look like this:
SELECT *
FROM Games AS G
INNER JOIN Teams AS T1 ON T1.TeamID = G.TeamID1
INNER JOIN Teams AS T2 ON T2.TeamID = G.TeamID2
If TeamID1 and TeamID2 are NOT NULL, then you want to use an INNER JOIN, otherwise you could use a LEFT JOIN.
You need to use alias:
SELECT GameID, team1.TeamName, team2.TeamName
FROM Games INNER JOIN teams team1 ON (Games.TeamID1 = team1.TeamID)
INNER JOIN teams team2 ON (Games.TeamID2 = team2.TeamID)