SQL Join query using three tables and some null values - php

I have three tables in a mySQL database that I am querying with PHP:
players
player_id | player_name
-----------------------
1 Tom
2 Dick
3 Harry
games
game_id | game_name
-------------------
1 Tennis
2 Cricket
3 Rugby
gamePlayerRel
game_id | player_id
-------------------
1 2
1 3
2 3
3 2
3 3
I want a query that will return the players id and name as well as the games that they play. If they play no games - as in the case of Tom (1) - then I want the id and name and a null or 0 value.
Desired Result
player_id | player_name | game_id | game_name
---------------------------------------------
1 Tom null null
2 Dick 1 Tennis
2 Dick 3 Rugby
3 Harry 1 Tennis
3 Harry 2 Cricket
3 Harry 3 Rugby
The closest I can get is with the following query:
SELECT players.player_id, players.player_name, gamePlayerRel.game_id, games.game_name
FROM players
INNER JOIN gamePlayerRel
INNER JOIN games
ON players.player_id = gamePlayerRel.player_id
AND gamePlayerRel.game_id=games.game_id
This gets close except that it does not return data for players with games assigned to them. In the above examples the result is as I want except that Tom (1) with his null values is left out.
I'm a super noob at this so if I'm coming at it from the wrong angle please say so and if you can suggest a query that will do what I'm after you would make my day.
Thanks in advance for any help and advice.

To return the result set as you've shown you need to actually do two outer joins
SELECT players.player_id, players.player_name, gamePlayerRel.game_id, games.game_name
FROM players
LEFT OUTER JOIN gamePlayerRel ON players.player_id = gamePlayerRel.player_id
LEFT OUTER JOIN games ON gamePlayerRel.game_id=games.game_id
In your case this should work just fine, however, what I really think you're trying to do is an inner join between games and gamePlayerRel and then a Left Outer Join between that virtual table and the players table. You can accomplish that via something like this:
SELECT players.player_id, players.player_name, playerGames.game_id, playerGames.game_name
FROM players
LEFT OUTER JOIN (Select gameplayerRel.player_id, games.game_id, games.game_name from games inner join gamePlayerRel on games.game_id = gamePlayerRel.game_id) playerGames
on players.player_id = playerGames.player_id
In your case you'd never need this alternative syntax, but there are some times when things can get messed up because the two left outer joins way is not exactly logically equivalent.
Here is a SQL Fiddle showing the results of the queries
I know you're using mysql, but this article does a good job of explaining the nuances of the joins, but it is written for MSSQL, although it should apply to you just the same. http://weblogs.sqlteam.com/jeffs/archive/2007/10/11/mixing-inner-outer-joins-sql.aspx

This should do the trick
SELECT players.player_id, players.player_name, gamePlayerRel.game_id, games.game_name
FROM players
INNER JOIN gamePlayerRel ON players.player_id = gamePlayerRel.player_id
LEFT OUTER JOIN games ON gamePlayerRel.game_id=games.game_id
You need to join each table individually and also need to use a LEFT OUTER JOIN for your games table. This will pull in at least one record for every match in the players/gamePlayerRel tables. If there isn't a match you'll be returned a NULL value.

After being nudged in the right direction by the helpful folk of SO I have got a query that does what I am after though I am still not 100% sure why it works.
SELECT players.player_id, players.player_name, gamePlayerRel.game_id, games.game_name
FROM players
INNER JOIN gamePlayerRel ON players.player_id = gamePlayerRel.player_id
LEFT OUTER JOIN games ON gamePlayerRel.game_id=games.game_id
Thanks to those who commented. Once again this site proves one of the most useful I have ever found :)

Related

Getting data from multiple tables into single row while

I'm trying to retrieve data from 2 tables and combine multiple rows into a single while loop
posts
post_id content
------ -------
1 content1
2 content2
3 content3
4 content4
comments
id post_id content
------ ------ ------
1 1 Wharton university
1 2 Yale University
sql code I write
mysqli_query( $connect, "SELECT * FROM `posts`
INNER JOIN comments ON posts.post_id = comments.post_id ORDER BY 1 DESC");
The problem I'm only getting post id 1 and 2. while there are over 30 posts
I want to get all posts and comments for each post in a single while loop.
How can I do it ?
Change your join to a LEFT join instead of an INNER join.
The difference being that a LEFT join will use null when there's no comments, where as INNER will only give you the rows that have comments.
Your INNER join is taking the union of both tables on the post_id field. You need a LEFT join instead of an INNER join. The LEFT join will give you all the results from the first table.

Alias Column After SQL Query

Evening, wasn't sure how to title my question but I have an issue that's been driving me mad and I can't seem to find an answer so I'm hoping you kind folks can point me in the right direction.
I'm currently making a sports predictions website on the side to learn more about PHP and SQL. So I have the following databases...
Teams
id | team_name | active
1 team 1 1
2 team 2 0
3 team 3 1
Matches
id | hometeam | homescore | awayteam | awayscore
1 3 1 4 0
2 5 2 1 3
3 1 0 3 2
To put it simply anyway. What I want is to lookup the team ids in hometeam and awayteam against the team name in the Teams table. So I've got this so far:
SELECT * FROM Matches LEFT JOIN Teams AS home ON Matches.hometeam = home.id LEFT JOIN Teams AS away ON Matches.awayteam = away.id
Which actually does do it except it leaves me with 2 columns called "team_name" one with the home team and one with the away team.
So my question is this, can I alias/rename the columns to 'home' and 'away' or is there a way that on my page I can distinguish between the two? For example I have $row['team_name'] but how do I point it to the right team_name column? If that makes sense.
Any help at all in pointing me in the right direction would be very much appreciated, thank you.
you can do it like this:
SELECT home.team_name as home_team, away.team_name as away_team FROM Matches LEFT JOIN Teams AS home ON Matches.hometeam = home.id LEFT JOIN Teams AS away ON Matches.awayteam = away.id
Specify the column names instead of using SELECT *
SELECT home.team_name AS home_team, away.team_name AS away_team, homescore, awayscore
FROM Matches
LEFT JOIN Teams AS home ON Matches.hometeam = home.id
LEFT JOIN Teams AS away ON Matches.awayteam = away.id
You should not use SELECT * in your queries, for many reasons explained thoroughly elsewhere.

MySQL join through relational table

Hi I can't seem to find the right way to write this query. I have two entities websites and clients, and a table that relates them through their id fields.
This is a many to many relationship. i.e. a website can have multiple clients and a client can have multiple websites.
I am trying to write a query that returns all the websites with the clients that belong to them. I want to return all the websites even if they have no clients associated with them. Here is the query that I am working with at the moment:
the three tables are ost_sites = websites, ost_site_auth = relational table, ost_clients = clients
SELECT
ost_sites.site_id,
ost_sites.name,
ost_sites.site_url,
ost_site_auth.site_id,
ost_site_auth.client_id
ost_clients.client_id,
CONCAT_WS(" ", ost_clients.lastname, ost_clients.firstname) as name,
FROM ost_sites
LEFT JOIN (ost_site_auth, ost_clients)
ON (ost_sites.site_id=ost_site_auth.site_id
AND ost_site_auth.client_id=ost_clients.client_id)
GROUP BY ost_sites.name
I get a result set but it doesn't return all the sites, and all of the rows don't have clients associated with them.
Thanks so much for any help!
Edit:
Here are the columns for the tables:
ost_site
site_id | name | site_url
1 facebook facebook.com
2 twitter twitter.com
3 tubmblr tumblr.com
4 google google.com
ost_site_auth
(notice no site_id = 3 in auth list)
id | site_id | client_id
1 1 1
2 1 2
3 2 1
4 2 2
5 4 1
6 4 4
ost_client
client_id | firstname | lastname
1 wilma flintstone
2 bam bam
3 fred flintstone
4 barney rubble
expected output:
site_id | name | site_url | client_name |
1 facebook facebook.com wilma flintstone
1 facebook facebook.com bam bam
2 twitter twitter.com wilma flintstone
2 twitter twitter.com bam bam
4 google google.com wilma flintstone
4 google google.com barney rubble
3 tumblr tumlr.com NULL
Your join looks a bit off... try this
SELECT
ost_sites.site_id,
ost_sites.name,
ost_sites.site_url,
ost_site_auth.site_id,
ost_site_auth.client_id
ost_clients.client_id,
CONCAT_WS(" ", ost_clients.lastname, ost_clients.firstname) as name
FROM ost_sites
LEFT OUTER JOIN ost_site_auth
ON ost_sites.site_id=ost_site_auth.site_id
LEFT OUTER JOIN ost_clients
ON ost_site_auth.client_id=ost_clients.client_id
ORDER BY ost_sites.name
Let me try to explain this a little for you...
We start with the ost_sites table and we want all the results from that regardless of if anything matches in the other tables.
Then, we do a left outer join to the table ost_site_auth. That means that if something from ost_site_auth does not match something in ost_sites, it will not be returned. However, something in ost_sites that doesn't match something in ost_site_auth will be returned because of the left outer part.
Next, we repeat the left outer join for the ost_clients.
Not sure what you want... Let's pretend we have this data represented in the tables:
Site #1 has no clients
Site #2 has one client: A
Site #3 has two clients: B, C
Site #4 has three clients: D, E, F
Site #5 has no clients
Clients G and H have no associated site
Query One
SELECT
ost_sites.site_id as SITE,
ost_clients.client_id as CLIENT
FROM ost_sites
LEFT OUTER JOIN ost_site_auth
ON ost_sites.site_id=ost_site_auth.site_id
LEFT OUTER JOIN ost_clients
ON ost_site_auth.client_id=ost_clients.client_id
ORDER BY ost_sites.site_id, ost_clients.client_id
That would return (basically)
SITE CLIENT
1 NULL
2 A
3 B
3 C
4 D
4 E
4 F
5 NULL
Query Two
SELECT
ost_sites.site_id as SITE,
ost_clients.client_id as CLIENT
FROM ost_sites
JOIN ost_site_auth
ON ost_sites.site_id=ost_site_auth.site_id
JOIN ost_clients
ON ost_site_auth.client_id=ost_clients.client_id
ORDER BY ost_sites.site_id, ost_clients.client_id
That would return (basically)
SITE CLIENT
2 A
3 B
3 C
4 D
4 E
4 F
Query three
SELECT
ost_sites.site_id as SITE,
ost_clients.client_id as CLIENT
FROM ost_sites
FULL OUTER JOIN ost_site_auth
ON ost_sites.site_id=ost_site_auth.site_id
FULL OUTER JOIN ost_clients
ON ost_site_auth.client_id=ost_clients.client_id
ORDER BY ost_sites.site_id, ost_clients.client_id
That would return (basically)
SITE CLIENT
1 NULL
2 A
3 B
3 C
4 D
4 E
4 F
5 NULL
NULL G
NULL H
Query four
SELECT DISTINCT ost_sites.site_id as SITE
FROM ost_sites
LEFT OUTER JOIN ost_site_auth
ON ost_sites.site_id=ost_site_auth.site_id
LEFT OUTER JOIN ost_clients
ON ost_site_auth.client_id=ost_clients.client_id
ORDER BY ost_sites.site_id
ORDER BY ost_sites.site_id
That would return (basically)
SITE
2
3
4
Query five
SELECT
ost_sites.site_id as SITE,
count(ost_clients.client_id) as CLIENT_COUNT
FROM ost_sites
JOIN ost_site_auth
ON ost_sites.site_id=ost_site_auth.site_id
JOIN ost_clients
ON ost_site_auth.client_id=ost_clients.client_id
GROUP BY ost_sites.site_id
ORDER BY ost_sites.site_id
That would return (basically)
SITE CLIENT_COUNT
2 1
3 2
4 3
Query five
SELECT
ost_sites.site_id as SITE,
count(ost_clients.client_id) as CLIENT_COUNT
FROM ost_sites
LEFT OUTER JOIN ost_site_auth
ON ost_sites.site_id=ost_site_auth.site_id
LEFT OUTER JOIN ost_clients
ON ost_site_auth.client_id=ost_clients.client_id
GROUP BY ost_sites.site_id
ORDER BY ost_sites.site_id
That would return (basically)
SITE CLIENT_COUNT
1 0
2 1
3 2
4 3
5 0
Check out
Full Outer Join in MySQL
I think all you really need to do is do the same query as a right outer join as well and union them.

Count users from table 2 in request to table 1

I have table with positions
tbl_positions
id position
1 Driver
2 Lobby
3 Support
4 Constructor
and in other table i have users
tbl_workers
id name position
1 John 2
2 Mike 3
3 Kate 2
4 Andy 1
i do request of positions
SELECT position FROM tbl_positions
but i also need to show how many workers are assigned to each position i tried to do separate request
SELECT id FROM tbl_workers WHERE position = 2
but cannot display all together in table cannot bind number of users to position.
How can i make join this queries into one, so it also show positions without workers assigned?
join and group by
SELECT p.id, p.position, count(*) FROM tbl_positions as p
inner join tbl_workers as w on w.position=p.id
group by p.id, p.position

Problem with left outer join

I have 2 tables.
Table 1 : t_atc_list
id | a_name | s_title | r_name
------------------------------
80 | ss | 128 | 5
Where s_title & r_name is foreign key.
Table 2 : t_s_list
s_id | title
-------------
128 | Song Title
I want have used left join query on this..to select all values of 't_atc_list' if it mightbe in 't_s_list'..
$query=mysql_query("SELECT t.s_title, s.title from t_atc_list t LEFT OUTER JOIN t_s_list s ON t.s_title=s.s_id");
$row=mysql_fetch_array($query);
While if I use right join its working..
$query=mysql_query("SELECT t.s_title, s.title from t_s_list s RIGHT OUTER JOIN t_atc_list t ON t.s_title=s.s_id");
$row=mysql_fetch_array($query);
Whats the reason left join is not working but right join is going well? And I think both the queries are identical than whats the problem?
This is a strictly data related issue you have there, your SQL syntax is correct, but your data must not line up.
For a visualization of SQL, look here: http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
Also if you do a right join, that means you are simply getting data from the 2nd table, and nulls in the first. I am just guessing here as I see no real data examples.

Categories