I'm trying to get the users full activity throughout the website.
I need to Join many tables throughout the database, with that condition that it is one user.
What I currently have written is:
SELECT * FROM
comments AS c
JOIN rphotos AS r
ON c.userID = r.userID
AND c.userID = '$defineUserID';
But What it is returning is everything about the user, but it repeats rows.
For instance, for one user he has 6 photos and 5 comments
So I expect the join to return 11 rows.
Instead it returns 30 results like so:
PhotoID = 1;
CommentID = 1;
PhotoID = 1;
CommentID = 2;
PhotoID = 1;
CommentID = 3;
and so on...
What am i doing wrong?
What I'm trying to achieve (example)
If you're a facebook user, every profile has a 'wall' which states the user's activity on the website in chronological order. I'm trying to make something similar.
What am i doing wrong?
you are using one complex query when you could use two simple ones.
You should do it as follows:
SELECT * FROM user AS u
LEFT JOIN rphotos AS r ON u.userId = r.userID
LEFT JOIN comments AS c ON u.userId = c.userID
WHERE u.userId = '$defineUserID'
Updated to fix silly mistakes
What this does is select all relevant users from the user table (1 in this case) then join in the other tables where necessary and shouldnt repeat rows.
The query also makes more sense when you think about it logically.
That you get 30 results if a specific user has 6 photos and 5 comments is quite normal, since you're just fetching the cartesian product of all photos and comments based on user ID. The table structure could shed some light onto possible solutions, but if the comments are related to the photos and you want to fetch all photos and the comments a specific user posted you might use something like :
SELECT * FROM rphotos p
LEFT JOIN comments c on c.photoID = p.photoID
WHERE p.userID = '$defineUserID' OR c.userID = '$defineUserID';
Personally I would split this into 2 queries and display the results separately because mixing them doesn't make any sense to me, ie. use
SELECT * FROM rphotos p
WHERE p.userID = '$defineUserID';
and
SELECT * FROM comments c
WHERE c.userID = '$defineUserID';
edit based on comment
If the ID fields are of the same type you could use something like
select actionID, relatedID, creationDate from
(
select 1 as actionID, photoID as relatedID, creationDate from rphotos
where userID = '$defineUserID'
union
select 2 as actionID, commentID as relatedID, creationDate from comments
where userID = '$defineUserID'
) actions
order by creationDate desc;
The actionID will be 1 for a photo, 2 for a comment and using the relatedID field you could lookup the linked data (if you need it, otherwise you could just drop it from the query).
BTW You probably want to filter the results further (ie. based on date) to prevent joining lots of rows in the union that you won't display...
Related
I'm kind of noobie to this, but I'm trying to learn, I have two tables, the first one (NEWS) has all the information about posts of a blog, it has the follow structure:
* NEWS (TABLE 1)
- id_new
- id_category
- date
- ...etc
- **likes**
and I have a second table:
* LIKES (TABLE 2)
- id_like
- id_new
- id_user
- date
- ip_user
So, I want to select all the rows from TABLE 1 to display all the news but also i want to count the likes and get the COUNT of each new as like column.
This approach left joins the NEWS table to a subquery which finds the number of likes for each news story.
SELECT
t1.*,
COALESCE(t2.likes, 0) AS likes
FROM NEWS t1
LEFT JOIN
(
SELECT id_new, COUNT(*) AS likes
FROM LIKES
GROUP BY id_new
) t2
ON t1.id_new = t2.id_new
Note that here a story having no likes would not appear at all in the LIKES table and would receive a count of zero. Also note that I assume that every record in the LIKES table corresponds to a logical like. If not, then the query could be modified to count something else.
You can do it like this
SELECT table1.*, table2.*, count(table2.id_like) as like FROM news AS table1
INNER JOIN likes AS table 2 ON table1.id_new = table2.id_new;
OR
SELECT table1.*, table2.*, count(table2.id_like) as like FROM news AS table1
LEFT JOIN likes AS table 2 ON table1.id_new = table2.id_new;
you can use prepared statement
for example
$stmt = $pdo->prepare("SELECT count(*) FROM TABLE_1);
$stmt2 = $pdo->prepare("SELECT count(*) FROM TABLE_2);
//then execute
just read more on prepared statement
Try this
SELECT n, (SELECT count(*) FROM like l WHERE l.id_new = n.id_new) FROM news n
Use something like :
SELECT *, (SELECT COUNT(*) FROM LIKES WHERE LIKES.id_new =id_new) AS newsLikesCount FROM NEWS ORDER BY date;
This query would return all news and their number of likes
select n1.* , numberOfLikes.number_of_likes
from news n1
left join
(select n.id_news, count(l.id_like) as number_of_likes
from news n
left join likes l on n.id_news = l.id_new
group by n.id_news) numberOfLikes on n1.id_news = numberOfLikes.id_news
The important concepts here is understanding how two tables are joined together (1), how group by works(2), and how to aggregate l.id_likes using count(3).
(1). Left join preserves everything in the NEWS table and join them
with news link to the news.
(2). Then we group the rows base on id_news from the news. However,
mysql gets confused because it doesn't know what to do with id_like
from the likes table that we included in our select clause. Don't
worry my friend, This is where count comes in.
(3). We count the number of id_likes base for each id_news since we
are grouping the rows base on id_news.
I hope this helps. and welcome to StackOverfow. If you find this answer of any other answer helpful please mark it as the solution. That way it will help the community and fellow programmers in the future if they run into the same problem as you. Cheers.
Edit: to include all columns from news table we simply join the result from above back to the news table itself. and we select everything from the news table n1 and only number_of_likes from the result we created above.
I have two tables in database, named users(store user details) and posts(store post details). Now i want to get data from both tables. Like user_image from users and post description from post.
I am using this query
SELECT * FROM `users`AS u,`posts` WHERE u.user_id IN (SELECT user_id FROM `posts`)
But it returns duplicate data. I have 2 users and 3 posts but it returns 6 posts.
Try something like:
Select a.user_image, b.post_description from users as a join posts as b on a.user_id = b.user_id
Do an inner join & you shall get the desired result
In the above query a & b are alias for the two different tables. I you do not want to use alias you can also write it as users.user_image in your select statement.
Write the fields you want from both the tables in your select statements.
The below image will help you understand the inner join
Inner Join Circle for understanding
Use group by as below:
SELECT * FROM `users`AS u,`posts` WHERE u.user_id IN (SELECT user_id FROM `posts`) group by u.user_id
What about
Select * FROM user u right join posts p on u.id = p.user_id
?
if you want to get data from both tables you need to use joins.and you make sure the two tables are interlinked by primary keys
so use this can help
select user_image,post_description from users join posts on users.user_id=posts.user_id;
I'm building a site that requires sharing with either group(s) or individual user(s). I know for a fact that google does not use mysql, but i was wondering how i could replicate such feature on my site. On g+, one can:
Share a post with the "public" (everyone can see it).
Share a post with "all circles" (everyone in your circles can see it).
Share a post with both circles and individual users. E.g. post = "my first post" and is shared with family,friends, user 1(Joey tribbiani), user 2 (Ross geller) etc.
Conditions:
If a post is shared with a circle and a new user is added to the circle, then (s)he should be able to see all the previous posts shared with that circle.
If a user is removed from a circle. (s)he cannot see posts shared with that circle except posts (s)he has commented on.
Currently my database tables look like this.
Circle_category
Cat_id
Cat_name
user_id
Posts
post_id
user_id
post
is_public
all_circle
Post_to_circle
entry_id
post_id
cat_id
Post_to_user
entry_id
post_id
user_id
Post a user in family circle(which is in Circle_category with cat_id of 1 ) can see
They can see posts that are public.
They can see posts shared with all circles.
They can see posts shared with family circle.
They can see posts shared with them (Individual user).
SQL
SELECT p.* FROM posts p
JOIN Post_to_circle pc
ON p.post_id = pc.post_id
JOIN Post_to_user pu
ON p.post_id = pu.post_id
WHERE p.is_public = 1
OR all_circle = 1
OR pc.cat_id = $cat_id
OR pu.user_id = $user_id
Quetions:
Firstly, I've been able to get posts from case 1(see all public post), case 2 (Posts shared with all circles) but the other 2 cases do not work. I thought about it and saw that the main problem is that i specified the where clause to get posts where p.is_public = 1 which means it neglets rows where p.is_public = 0. How do i update the query so it shows posts covering all four cases and also covers the conditions we talked about at the beginning.
Secondly, is there a better way to structure my tables? i'm not sure i'm doing it the right way.
From a quick read trough, all i can say is:
you are using a join statement instead of a left join statement.
using join means:
keep all rows from the table used in from-clause that validate true for the condition specified in that join clause.
since you are using 2 statements, the first join throws away all the records that dont have the needed join, the second join throws away all the records that dont have the needed join in the second one, but it only uses records that matched the first join.
you should use left join instead. this keeps all rows from the first table. all rows that didnt have a match, get the values NULL for the columns specified in the joined table(s)
simple example:
users table:
user_id
name
user_posts
post_id
user_id
content
created
related queries:
select *
from users u
JOIN user_posts up on up.user_id = u.user_id and up.created > date_sub(curdate(), interval 1 day)
this will use all users and make match with each post that was created less then a day ago by that user.
if the user didnt have a post in the last day, he will NOT be in the resultset.
change this example to
select *
from users u
LEFT JOIN user_posts up on up.user_id = u.user_id and up.created > date_sub(curdate(), interval 1 day)
this will use all users and make a match with each post that was created less then a day ago by that user
if the user hasn't posted in the last day, he will STILL be in the resultset, but all the columns from the posts table will be NULL
the where filters all the rows you have left after the joins. (mysql will use where clauses before joining, if they can speed up the query).
altering your query:
make sure the clauses in where statement are wrapped between () for all the different cases. ALSO this is NOT the complete answer, as there is info missing (example user tables, circle relation tables, friend relations)
also the all_circles option confuses me, so it's missing from the query, but this should get you on the right track
SELECT p.* FROM posts p
left JOIN Post_to_circle pc
ON p.post_id = pc.post_id and /* define statement for valid circles for user you're trying to get the posts for */
left JOIN Post_to_user pu
ON p.post_id = pu.post_id and /* define statement for valid friends for user you're trying to get the posts for */
WHERE
/* 1 day old */
p.created > date_sub(curdate(), interval 1 day)
AND (
/* is public */
p.is_public = 1 OR
/* or to friends */
pu.id is not null OR
/* or to circles */
pc.id is not null
)
Also, i'm suspecting you'll need 2 subqueries, which is not the best thing to do, and my advise would be to find all correct ids for the friends, and all ids for the valid circles and then using an IN clause in each join statement (part thats in comment)
I'm working on new script. It's something similar to 9gag, there are posts and people like/dislike them and so on.
I have 3 tables for posts, users, like_status
for example:
posts table structure:
postid, postdata , userid, ...
users table stucture:
userid, username , ....
like_status
postid,userid,liked, disliked
I used to use MySQL query
SELECT A.*, B.username FROM posts A, users B WHERE A.userid=B.userid
then I get the like status by assigning the session userid to a variable $SID and use this MySQL query
SELECT * FROM like_status WHERE userid=$SID
everything is working fine, but I'm a little concerned about the server load.
Which is better for server load and memory, to run joining query to be :
SELECT A.*, B.username, C.liked, C.disliked
FROM posts A
JOIN users B ON (A.userid = B.userid)
JOIN like_status C ON (C.userid = $SID)
And what about if I'm using that query to fetch number of posts like 10 posts for example
SELECT A.*, B.username, C.liked, C.disliked
FROM posts A
JOIN users B ON (A.userid = B.userid)
JOIN like_status C ON (C.userid = $SID)
ORDER BY A.postid limit 10
.. Will it cause any performance issues?
That's it.
Best Regards
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.