How can I join 3 tables with mysql & php? - php

I have a page that pulls the users Post,username,xbc/xlk tags etc which is perfect... BUT since I am pulling information from a MyBB bulletin board system, its quite different. When replying, people are are allowed to change the "Thread Subject" by simplying replying and changing it.
I dont want it to SHOW the changed subject title, just the original title of all posts in that thread.
By default it repies with "RE:thread title". They can easily edit this and it will show up in the "Subject" cell & people wont know which thread it was posted in because they changed their thread to when replying to the post.
So I just want to keep the orginial thread title when they are replying.
Make sense~??
Tables:mybb_users
Fields:uid,username
Tables:mybb_userfields
Fields:ufid
Tables:mybb_posts
Fields:pid,tid,replyto,subject,ufid,username,uid,message
Tables:mybb_threads
Fields:tid,fid,subject,uid,username,lastpost,lastposter,lastposteruid
I haev tried multiple queries with no success:
$result = mysql_query("
SELECT * FROM mybb_users
LEFT JOIN (mybb_posts, mybb_userfields, mybb_threads)
ON (
mybb_userfields.ufid=mybb_posts.uid
AND mybb_threads.tid=mybb_posts.tid
AND mybb_users.uid=mybb_userfields.ufid
)
WHERE mybb_posts.fid=42");
$result = mysql_query("
SELECT * FROM mybb_users
LEFT JOIN (mybb_posts, mybb_userfields, mybb_threads)
ON (
mybb_userfields.ufid=mybb_posts.uid
AND mybb_threads.tid=mybb_posts.tid
AND mybb_users.uid=mybb_posts.uid
)
WHERE mybb_threads.fid=42");
$result = mysql_query("
SELECT * FROM mybb_posts
LEFT JOIN (mybb_userfields, mybb_threads)
ON (
mybb_userfields.ufid=mybb_posts.uid
AND mybb_threads.tid=mybb_posts.tid
)
WHERE mybb_posts.fid=42");

Your syntax isn't appropriate for carrying out multiple LEFT JOINs. Each join needs its own ON clause.
SELECT
*
FROM
mybb_users
LEFT JOIN mybb_userfields ON mybb_users.uid = mybb_userfields.ufid
LEFT JOIN mybb_posts ON mybb_userfields.ufid = mybb_posts.uid
LEFT JOIN mybb_threads ON mybb_posts.tid = mybb_threads.tid
WHERE
mybb_posts.fid = 42
This query should give the results you want. But it may not be the most efficient query for getting those results. Check the output of EXPLAIN as part of testing, to make sure it is not using table scans or anything like that.
Do all of these joins need to be LEFT JOINs? LEFT JOIN forces MySQL to join the tables in the indicated order, rather than allowing the query optimiser to determine the best order in which to join them. That's why you might need to be careful about the query execution plan. The main difference between JOIN and LEFT JOIN as far as query output is concerned is that LEFT JOIN resultsets will contain at least one row for each row of the table on the left-hand side of the join, whereas a regular JOIN will not contain a row if there aren't matches on the right-hand side of the join.
Edit: Also, you say that "I don't want it to SHOW the changed subject title, just the original title of all posts in that thread." This suggests that you only want a subset of the columns from these tables, in which case SELECT * is inappropriate.

Related

SQL/PHP How can I filter this with the use of a LEFT JOIN?

This database is a very basic concept for a video rental store. The database is displayed with the use of PHP (and HTML, CSS, bootstrap, etc).
I have three tables:
tl_dvd: which holds the basic info about the dvd, duration date, name, language, etc.
tl_order: which holds the name of the person who rented a movie and the startdate.
tl_client: which isn't really relevant to my question/problem, but just holds the first and last name of the clients.
When I'm on the order page it displays all the orders which are currently running. I have one column called returned in tl_order with a tinyint where 0 stands for not returned and 1 stands for returned.
There's a button which says 'terug gebracht' (='received back' in English) which will set the selected order to 1. The tl_order only displays the orders which are not returned yet (returned set on 0). On the end of the SQL query I've set something like ... AND returned = 0';
Here is the part where I'm supposed to use the LEFT (OUTER?) JOIN. If an order is not back (so that means returned is still on 0 in tl_order) the dvd should not show up in my tl_dvd (think of it like we only have one dvd copy for every movie). I've tried these LEFT JOINs in tl_dvd:
'SELECT * FROM tl_dvd LEFT JOIN tl_order WHERE tl_order.returned = 1';
Breaks my page with 500 error.
'SELECT * FROM tl_dvd LEFT JOIN tl_order ON tl_order.returned = 1';
Doesn't give me an error but spams every title like 3 times.
Can someone explain me how to tackle this issue or what I'm doing wrong?
The problem with your queries is that you're not specifying how the tables should be joined (which columns to join on), and so it's returning everything from both tables.
Try this:
select
*
from
tl_dvd left join tl_order on (tl_dvd.titel=tl_order.titel)
where
tl_order.returned = 1
You don't clearly explain or give an example of what you want in your query result. But from your examples you might want
SELECT * FROM tl_dvd
LEFT JOIN tl_order
ON tl_dvd.titel = tl_order.titel
WHERE tl_order.returned = 1
OUTER JOIN without ON is not standard SQL but MySQL treats absent ON in INNER and OUTER JOIN like ON 1=1, which in either case is equivalent to CROSS JOIN.
But it doesn't seem like you really want LEFT JOIN rather than (INNER) JOIN. LEFT JOIN returns (INNER) JOIN rows plus unmatched left table rows extended by nulls. So here besides a row for every dvd order you get row for every nordered dvd, with order info null. Since "If an order is not back [...] the dvd should not show up" seems to say you don't want those extra rows. You'd want the above without 'LEFT' or equivalently (but less clearly)
SELECT * FROM tl_dvd
CROSS JOIN tl_order
WHERE tl_dvd.titel = tl_order.titel
AND tl_order.returned = 1
You may want to rewrite the query to user inner join like the following
SELECT * FROM tl_dvd INNER JOIN tl_order ON
tl_dvd.id=tl_order.id //replace the id with the id you would like to join
WHERE tl_order.returned = 1

How to join all three tables in MYSQL?

I have a MySQL database that has three tables holding information about uploaded photos by users. I have a PHP page that displays all the photos in the database (tbl_uploads) and who uploaded them (tbl_users). To display the photos and who uploaded them I have a join in the MySQL query.
SELECT *
FROM tbl_uploads, tbl_users
WHERE tbl_uploads.user_id = tbl_users.user_id
ORDER BY date DESC
I now want to join a third table tbl_collab to the MySQL query that allows me to display all the users that collaborated with the photo (a form allows them to post the $file and their $user_id to tbl_collab). I guess I need to add a join from tbl_uploads.file with tbl_collab.user_id but I'm not sure how.
tbl_users
|//**user_id**//|int(11)|No|
|user_name|varchar(25)|No|
|user_email|varchar(60)|No|
|user_password|varchar(255)|No|
|joining_date|datetime|No|
tbl_uploads
|//**id**//|int(10)|No|
|file|varchar(100)|No|
|type|varchar(30)|No|
|size|int(11)|No|
|user_id|int(11)|No|
|user_name|varchar(25)|No|
tbl_collab
|//**id**//|int(11)|No|
|user_name|varchar(100)|No|
|user_id|int(11)|No|
|file|varchar(255)|No|
I have been trying your various suggestions and I can't really get them to work as I would hope so I have made a mysql fiddle that might be help me.
The problem is that when I loop through the rows that the query throws up in PHP I ether get just the rows where there is join with tbl_uploads.file and tbl.collab.file or I get the multiple rows duplicating themselves.
I'd suggest preferring ANSI SQL syntax for joins (over mentioning multiple tables in the "from" clause) as once the queries get complex I find the ANSI syntax easier to follow. Using that syntax, joining multiple tables is no big deal. e.g.,
SELECT uploads.<column>, users.<column>, collabs.<column>
FROM tbl_uploads uploads
JOIN tbl_users users ON users.user_id=uploads.user_id
JOIN tbl_collabs collabs ON collabs.file=uploads.file
ORDER BY uploads.date DESC
(Note, replace <column> above with the names of columns you want to select from the respective tables, using AS syntax to provide unique names where necessary.)
Consider that you will probably want to create indexes over the fields in the join conditions for performance if you expect the database will become large. You may also want to use left joins when joining, e.g., tbl_collabs if it is possible an upload will have no collaborators, otherwise the query will return no data if there are no matching rows in tbl_collabs.
The first thing to do is to normalize your data. If you look closely, username appears in all three tables. It shouldn't. It belongs only in the users table. Then your other tables need to have a user_id field instead of the username.
tbl_uploads
|//**id**//|int(10)|No|
|file|varchar(100)|No|
|type|varchar(30)|No|
|size|int(11)|No|
|user_id|int(11)|No|
tbl_collab
|//**id**//|int(11)|No|
|user_id|int(11)|No|
|file|varchar(255)|No|
In both cases the user_id is a foreign key to the id field in the users table. Now we have something consistent to join on.
SELECT * FROM tbl_uploads
INNER JOIN tbl_users ON tbl_uploads.user_id = tbl_users.user_id
INNER JOIN tbl_collab ON tbl_collab.file = tbl_uploads.file
Whether you should use INNER JOIN or LEFT JOIN depends on exactly what you need to do with your data, but INNER JOIN seems more appropriate based on information provided.
Update: As #drew pointed out, none of your tables have a column named date did you perhaps intend to sort by tbl_users.joining_date?
Seems the join is on file to me
SELECT *
FROM tbl_uploads
inner join tbl_users on tbl_uploads.user_id = tbl_users.user_id
inner join tbl_collab on tbl_collab.file = tbl_uploads.file
ORDER BY date DESC
You can just add another join condition. Also, note that implicit joins (having multiple tables in the from clause) isn't considered a good practice, and you should probably use explicit join clauses:
SELECT *
FROM tbl_uploads up
JOIN tbl_users us ON up.user_id = us.user_id
JOIN tbl_collab c ON c.user_id = up.user_id
ORDER BY date DESC

Is it possible with MySQL to generate a query to fetch from several tables even if one has no results?

I'm facing a problem here:
I'm building a forum, this forum has several tables and I'm trying to fetch the comments and user info in a single query.
So far, it should be easy, the problem is that I can't change the structure and with the following query I get a perfect result IF there is a like to the answer. If no one likes the answer it fails.
Select
mfr.mfr_forum_answers.id,
mfr.mfr_forum_answers.date_created,
mfr.mfr_forum_answers.last_updated,
mfr.mfr_forum_answers.content,
mfr.mfr_forum_answers.accepted,
mfr.mfr_forum_answers.user_id,
mfr.mfr_users.level,
mfr.mfr_users.avatar,
mfr.mfr_forum_likes.subject_id,
mfr.wp_users.ID As ID1,
mfr.mfr_forum_topics.user_id As owner_id,
(SELECT count(mfr.mfr_forum_likes.id) FROM mfr.mfr_forum_likes WHERE mfr.mfr_forum_likes.subject_id = :id AND mfr.mfr_forum_likes.type = 'answer') as likes,
(SELECT count(mfr.mfr_forum_likes.id) FROM mfr.mfr_forum_likes WHERE mfr.mfr_forum_likes.subject_id = :id AND makefitreal.mfr_forum_likes.type = 'answer' AND mfr.mfr_forum_likes.user_id = :sessionId ) as i_like,
mfr.wp_users.user_nicename
From
mfr.mfr_forum_likes Inner Join
mfr.mfr_forum_answers
On mfr.mfr_forum_answers.topic_id =
mfr.mfr_forum_likes.subject_id Inner Join
mfr.mfr_users
On mfr.mfr_forum_answers.user_id = mfr.mfr_users.id
Inner Join
mfr.wp_users
On mfr.mfr_users.id = mfr.wp_users.ID Inner Join
mfr.mfr_forum_topics
On mfr.mfr_forum_answers.topic_id = mfr.mfr_forum_topics.id
Where
mfr.mfr_forum_answers.topic_id = :id
And
mfr.mfr_forum_likes.type = 'answer'
So far as said it returns only if an answer has a like, I'm thinking on adding a add to the user who posts the answer by default but I'm trying to improve my skills by solving new issues.
If someone has a suggestion in how I could overcome the fact that if a table is empty, the query continues I'd be really thankfull.
Thanks in advance-
Pihh
Yes. What you are looking for are called left and right joins. According to the documentation, with a LEFT JOIN you still join two tables as normal but
If there is no matching row for the right table in the ON or USING part in a LEFT JOIN, a row with all columns set to NULL is used for the right table.
This means that you can try to join two tables, but if a row does not have any results it will still return the results from the first table. The same is true for a RIGHT JOIN only it works the opposite way: it will return results if the tabled being joined to has results, but the original table does not.
It looks like you have 3 tables for 3 relationships: there are answers, a user gives an answer, and an answer might or might not have like. To grab this data, I would suggest starting from your answers table, performing an INNER JOIN on your users table (assuming there are always users), and a LEFT JOIN on your likes table. Here is a simple example:
SELECT *
FROM answers
INNER JOIN users ON users.id = answers.user_id
LEFT JOIN likes ON likes.answer_id = answer.id
WHERE answers.id = :id
AND likes.type = 'answers'
Of course, if for some unknown reason you need to start from your likes table, then you'd have to RIGHT JOIN the other tables. I hope that gives you a good idea of how you'd make your query.

mysql/php: show posts and for each post all comments

I know this question has been asked multiple times (however, I could still not find a solution):
PHP MYSQL showing posts with comments
mysql query - blog posts and comments with limit
mysql structure for posts and comments
...
Basic question: having tables posts, comments, user... can you with one single select statement select and show all posts and all comments (with comment.user, comment.text, comment.timestamp)? How would such a select statement look like? If not, what is the easiest solution?
I also tried to JOIN the comments table with the posts table and use GROUP BY, but I got either only one comment in each row or each comment but also those posts multiple times!?
I tried the solution of the first link (nested mysql_query and then fetch) as well as the second link (with arrays). However, the first caused a bunch of errors (the syntax in that post seems to be not correct and I could not figure out how to solve it) and in the second I had problems with the arrays.
My query looks like this till now:
SELECT p.id, p.title, p.text, u.username, c.country_name, (SELECT SUM(vote_type) FROM votes v WHERE v.post_id = p.id) AS sum_vote_type FROM posts p LEFT JOIN user u ON ( p.user_id = u.id ) LEFT JOIN countries c ON ( c.country_id = u.country_id ) ORDER BY $orderby DESC
I was wondering if this issue was not very common, having posts and comments to show...?
Thank you for every help in advance!
Not knowing your database structure, it should look something like this. Note that you should replace the * characters with more explicit lists of columns you actually need.
SELECT p.*, c.*, u.* FROM posts p
LEFT JOIN comments c ON c.post_id = p.id
LEFT JOIN users u ON u.id = p.author_id
Note that if you're just trying to get counts, sums and things like that it's a good idea to cache some of that information. For instance, you may want to cache the comment count in the post table instead of counting them every query. Only count and update the comment count when adding/removing a comment.
EDIT:
Realized that you also wanted to attach user data to each comment. You can JOIN the same table more than once but it gets ugly. This could turn into a really expensive query. I also am including an example of how to alias columns so it's less confusing:
SELECT p.*, c.*, u.name as post_author, u2.name as comment_author FROM posts p
LEFT JOIN comments c ON c.post_id = p.id
LEFT JOIN users u ON u.id = p.author_id
LEFT JOIN users u2 ON u2.id = c.author_id

Mysql - Join matches and non-matches

This is related to my other question:
Managing Foreign Keys
I am trying to join the table of matches and non-matches.
So I have a list of interests, a list of users, and a list of user interests.
I want the query to return all interests, whether the user has the interest or not (should be null in that case), only where the user = x. Every time I get the query working its only matching interests that the user specifically has, instead of all interests whether they have it or not.
You should rather use LEFT JOINS
Something like
SELECT *
FROM interests i LEFT JOIN
userinterests ui ON i.interestID = ui.interestID LEFT JOIN
users u ON ui.userID = u.uiserID
WHERE userID = ?
where is the user id you are looking for.
SELECT *
FROM interests i
LEFT JOIN userinterests ui ON i.interestID = ui.interestID
LEFT JOIN users u ON ui.userID = u.uiserID
and u.userID = ?
If you put a where condition on a table that should have no records inteh main tbale, you convert the join from a left join to an inner join. The only time you should ever have a condition inthe where clasue for something one the right side of a left join is when you are searching for records that don't match (where u.userid is null, for instance)
Of course you should define the fields to be selected and never use select * in production code especially not when you have a join as it sends repeated information across the network (the data inteh join feilds is repeated) and is a waste of resources and poor prgramming practice for multiple reasons.

Categories