So I have a bit of a problem. Usually when I have two queries I would use JOIN to merge them together, but this time I can't seem to logically figure out how to do it the best since one of the queries also contain a subquery.
Background: I'm setting up a system where people can make posts. The users will then be able to follow other users and topics. So if a post gets tagged with "eduction" - the user will be able to follow the topic "education" - and see all the posts from this topic.
The two queries shows the desired content. But I would like to make a "feed" so the user will see the posts from the others users it follows and from the topics that it follows. I would really like to make it as effecient as possible and I would really like to learn how to handle this in the future myself, so if you could also explain how and why the "solution" would be the best - I would appreciate it.
Query that displays the posts from the users followed:
$query = "SELECT
t.t_id,
t.t_thought,
t.t_timestamp,
t.u_id AS t_u_id,
t.t_statistics,
t.t_reaction,
t.t_comments,
t.t_no_reactions,
t.t_no_comments,
u.u_id AS u_u_id,
u.u_username,
u.u_real_name,
u.u_profile_picture,
r.r_id
FROM thoughts AS t
INNER JOIN users AS u ON t.u_id = u.u_id
INNER JOIN followers AS f ON f.u_id = '".$u_id."'
LEFT OUTER JOIN reactions AS r ON r.u_id = ? AND r.t_id = t.t_id
WHERE t.u_id = f.u_id_to_follow
ORDER BY t_id DESC LIMIT ?, ?
";
Query that displays the posts from the topics followed
$queryGetThoughts = "SELECT
t.t_id,
t.t_thought,
t.t_timestamp,
t.u_id AS t_u_id,
t.t_statistics,
t.t_reaction,
t.t_comments,
t.t_no_reactions,
t.t_no_comments,
u.u_id AS u_u_id,
u.u_username,
u.u_real_name,
u.u_profile_picture,
r.r_id
FROM thoughts AS t
INNER JOIN
users AS u ON t.u_id = u.u_id
INNER JOIN
thoughts_tags AS tt ON tt.t_id = t.t_id
INNER JOIN
tags AS tags ON tt.tag_id = tags.tag_id
LEFT OUTER JOIN reactions AS r ON r.u_id = ? AND r.t_id = t.t_id
WHERE tt.tag_id IN( SELECT tag_id FROM tags_followers WHERE u_id = '".$u_id."')
GROUP BY t.t_id
ORDER BY t.t_id DESC LIMIT ?, ?
";
I appreciate your help and knowledge.
Thank you.
Related
So I have a webpage that shows an image and information about it, and I also have three tables.
IMAGES USERS COMMENTS
id user_id id
user_id username user_id
username password comment
title isadmin date
image (location) points
description signup_date
points email
category city
bio
profile_image
I need to use all the information from the images and comments table. But I also need to retrieve the profile_image of the user that posted the comment and image. I cannot figure out how to do this: here is what I have so far.
$conn = new PDO ("mysql:host=localhost;dbname=project", "root", "pass");
$stmt = $conn->prepare("
SELECT images.*, users.profile_image, comments.*
FROM (images.id JOIN comments ON images.id = comments.id)
LEFT JOIN users
ON images.user_id = user.profile_image
");
Am I close? or trying the impossible here!?
it's a lot simpler then you'd think :)
the tables are NOT unrelated, they all have the same key - user_id and that's how you join them.
your query should look like this:
SELECT images.*, users.profile_image, comments.*
FROM images
JOIN users ON images.user_id = users.user_id
JOIN comments ON comments.user_id = users.user_id AND images.id = comments.id
this is assuming the images.id and comments.id match (I suspect they do not - but you did not give us enough info)
looking at your table structure, comments and images appear to have no connection, so maybe query them separately to avoid duplicated & confusing data
---UPDATE---
assuming you've added image_id to comments this is your query:
SELECT images.*, users.profile_image, comments.*
FROM images
LEFT JOIN users ON images.user_id = users.user_id
LEFT JOIN comments ON comments.user_id = users.user_id AND images.id = comments.image_id
what LEFT JOIN does is return 'images' that don't necessarily have 'users' or 'comments' connected to them.
I guess this will do the trick:
SELECT i.*, u.profile_image, c.*
FROM images i
LEFT JOIN comments c ON i.user_id = c.user_id
LEFT JOIN users u ON i.user_id = u.user_id
I think it's:
> SELECT images.*, users.profile_image, comments.*
> FROM images
> INNER JOIN users ON images.user_id= users.user_id
> INNER JOIN comments ON users.user_id= comments.user_id
Well let's see, the query I have is working fine, as soon as a friendpost is done, however. If the user has no friends, no result will be returned, and that's what I am trying to get a hang of...
$query = "SELECT DISTINCT m.id, p.byuser, p.`newpost`, p.`id`, p.`postdate`
FROM users m
JOIN pinnwand p
ON m.id = p.byuser
JOIN friends f
ON f.`userid` = m.id OR f.`friendid` = m.id
WHERE (f.`userid` = $myId OR f.`friendid`= $myId)
AND (p.`touser` = p.`byuser` OR p.`touser` = $myId)
AND p.`publicp` < 3
AND p.`typ` = 2
ORDER BY p.id DESC LIMIT $limit, 10";
I hope somebody can help me, maybe I am just blind for nao...
Edit
As Steven helped me out quite a lot, maybe somebody finds the last bit missing: It's just showing the posts made for the specific user. Even though as I understand the query it should get the posts made by friends on their pinboard as well? After all the m.id should get the friendtables value as well, or am I wrong?
Edit 2
So as I went with the UNION and Subquery Method for now, I still want to describe what the result should look like:
Show: Userposts made whereever, Posts by whomever made on the Userboard, Friendposts made on their own board! Not the posts made by people on friends boards.
There are 2 problems:
You need a LEFT JOIN on friends. A LEFT JOIN says to return all records from the first table in the join even if there are no results found in the second table in the join.
You also should the WHERE clause conditions relating to friends into the LEFT JOIN clause, so that the conditions occur at the join. You should also be using m.id wherever possible in your joins instead of $myId to eliminate redundancy.
Your WHERE clause is too restrictive (redundant conditions). Always use the simplest set of conditions possible, and put as many as appropriate at the JOIN so they are easier to read.
Example (Edited to add posts from friends, as well):
$query = "SELECT DISTINCT `u`.`id`, `p`.`byuser`, `p`.`newpost`, `p`.`id`, `p`.`postdate`
FROM `users` AS `u`
LEFT JOIN `friends` AS `f`
ON `f`.`userid` = `u`.`id`
OR `f`.`friendid` = `u`.`id`
JOIN `pinnwand` AS `p`
/* This will get all posts made by the user */
ON `p`.`byuser` = `u`.`id`
/* This will get all posts made TO the user by friends */
OR (`p`.`byuser` IN (`f`.`userid`, `f`.`friendid`)
AND `p`.`touser` = `u`.`id`)
WHERE `u`.`id` = {$myId}
AND `p`.`publicp` < 3
AND `p`.`typ` = 2
ORDER BY `p`.`id` DESC
LIMIT {$limit}, 10";
OK, so in the end i ended up using an Union and a subquery... It's probably suboptimal, but if somebody has a good suggestion what to improve, please give me your opinion! Otherwhise I hope that this post will help people with simmilar problems.
$query = "SELECT DISTINCT `p`.`byuser`, `p`.`newpost`, `p`.`id`, `p`.`postdate`
FROM `pinnwand` AS `p`
JOIN `users` AS `u` ON `u`.`id` = `p`.`byuser`
LEFT JOIN `friends` AS `f` ON (`f`.`friendid` = `u`.`id` OR `f`.`userid` = `u`.`id`)
WHERE (f.userid = {$myId} OR f.friendid = {$myId})
AND `p`.`publicp` < 3
AND `p`.`typ` = 2
AND `p`.`byuser` <> {$myId}
UNION ALL
SELECT DISTINCT `p`.`byuser`, `p`.`newpost`, `p`.`id`, `p`.`postdate`
FROM `pinwand` AS `p`
JOIN `users` AS `u` ON `u`.id = `p`.`byuser`
WHERE `u`.`id` = {$myId}
AND `p`.`publicp` < 3
AND `p`.`typ` = 2
ORDER BY `postdate` DESC
LIMIT 0,10";
I have this diagram
What I wanna do is have this output:
How do you manage to do the query of this one?
I have this code
SELECT users.firstname, users.lastname,
users.screenname, posts.post_id, posts.user_id,
posts.post, posts.upload_name,
posts.post_type, posts.date_posted
FROM website.users users
INNER JOIN website.posts posts ON (users.user_id = posts.user_id)
ORDER BY posts.pid DESC
//PROBLEM with this one is that it only views the post from all users.
//SO I added
SELECT COUNT(user_id) AS friends, SUM(user_id = ?) AS you, user_id
FROM feeds WHERE post_id = ?
//This one will give you two fields containing how many different users **feeds** the
post
Please help guys. Actually this one I am just following Facebook's "LIKE" status
the only thing is I'm not an amateur with this kind of stuff so I'd be glad to hear all your answers. I really need your help
If I've understood you correctly, you want an outer join with the feeds table (in order to retain all posts even if there are no associated feeds), then GROUP BY post.pid in order to amalgamate together all such feeds for each post, and SELECT the desired information.
I use MySQL's GROUP_CONCAT() function to obtain a comma-separated list of all users (up to group_concat_max_len) who have
a "feed" for the given post (you can change the delimiter with the SEPARATOR modifier, if so desired).
SELECT users.firstname, users.lastname,
users.screenname, posts.post_id, posts.user_id,
posts.post, posts.upload_name,
posts.post_type, posts.date_posted,
COUNT(feeds.user_id) AS friends, -- number of "likes"
SUM(feeds.user_id = ?) AS you, -- did I like this?
GROUP_CONCAT(feeds.user_id) -- who likes it?
FROM website.users users
INNER JOIN website.posts posts ON (users.user_id = posts.user_id)
LEFT JOIN website.feeds feeds ON (posts.post_id = feeds.post_id)
GROUP BY posts.pid
ORDER BY posts.pid DESC
UPDATE
To obtain the full name of users who have "liked" the post, excluding oneself, one needs to join the users table a second time:
SELECT users.firstname, users.lastname,
users.screenname, posts.post_id, posts.user_id,
posts.post, posts.upload_name,
posts.post_type, posts.date_posted,
COUNT(feeds.user_id) AS friends, -- number of "likes"
SUM(feeds.user_id = ?) AS you, -- did I like this?
GROUP_CONCAT(
CASE WHEN NOT likes.user_id = ? THEN -- exclude self
CONCAT_WS(' ', likes.firstname, likes.lastname) -- full names
END
)
FROM website.users users
INNER JOIN website.posts posts ON (users.user_id = posts.user_id)
LEFT JOIN website.feeds feeds ON (posts.post_id = feeds.post_id)
LEFT JOIN website.users likes ON (feeds.user_id = likes.user_id)
GROUP BY posts.pid
ORDER BY posts.pid DESC
If You want to do it for all the users and simultaneously get the feeds, You have to join this feed table:
SELECT u.firstname, u.lastname,
u.screenname, p.post_id, p.user_id,
p.post, p.upload_name,
p.post_type, p.date_posted,
COUNT(f.user_id) AS friends, SUM(f.user_id = ?) AS you
FROM website.users u
INNER JOIN website.posts p ON (u.user_id = p.user_id)
LEFT JOIN website.feeds f ON (p.post_id = f.post_id)
GROUP BY p.pid
ORDER BY p.pid DESC
This one should do the trick...
I'm not quite sure if this is the right approach, this is my situation:
I'm currently trying to select 15 galleries and then left join it with the user table through the id but I also want to select one random picture from each gallery however from what I know you can't limit the left join (picture) to only pick up one random picture without doing a subquery.
Here is what I got so far but its not working as it should:
SELECT galleries.id, galleries.name, users.username, pictures.url
FROM galleries
LEFT JOIN users ON users.id = galleries.user_id
LEFT JOIN pictures ON (
SELECT pictures.url
FROM pictures
WHERE pictures.gallery_id = galleries.id
ORDER BY RAND()
LIMIT 1)
WHERE active = 1
ORDER BY RAND()
LIMIT 15
I also tried to do this with Active Record but I got stuck after doing two left joins, is it possible to do get a subquery in here:
$this->db->select('galleries.id, galleries.name, users.id as user_id, users.username');
$this->db->from('galleries');
$this->db->join('users', 'users.id = galleries.user_id','left');
$this->db->join('pictures','pictures.gallery_id = galleries.id AND','left');
$this->db->where('active',1);
I hope its not to messy but I'm really starting to get confusing by all the sql queries..
Edit:
Active Record with CodeIgniter
You could fetch a random picture in a subquery:
select
g.name, u.username,
(select url from pictures p where p.gallery_id = g.gallery_id
order by rand() limit 1) as url
from galleries g
left join users u on g.user_id = u.id
where g.active = 1
Based on your comment, you could select a picture for each gallery in a subquery. This is assuming the picture table has an ID column.
select
g.name, u.username, p.url, p.name
from (
select id, user_id, name,
(select id from pictures p
where p.gallery_id = g.gallery_id
order by rand() limit 1) as samplepictureid
from galleries
where g.active = 1
) g
left join users u on g.user_id = u.id
left join pictures p on p.id = g.samplepictureid
SELECT
g.id,
g.name,
u.username,
p.url
FROM
galleries g
INNER JOIN (SELECT DISTINCT
gallery_id,
(SELECT url FROM pictures ss WHERE ss.gallery_id = s.gallery_id
ORDER BY RAND() LIMIT 1) AS url
FROM
pictures s) p ON
g.id = p.gallery_id
LEFT OUTER JOIN users u ON
g.user_id = u.id
WHERE
g.active = 1
This query will go out and select a gallery, then it will find any gallery with a picture (if you want to return galleries without a picture, change INNER JOIN to LEFT OUTER JOIN, and you'll be fine). After that, it joins it up with users. Now, of course, this puppy is going to return every frigging gallery for however many users you have (hoorah!). You may want to limit the user in the WHERE clause (e.g.-WHERE u.id = 123). Otherwise, you're going to get more results than you'd expect. That, or do an INNER JOIN on it.
I need to alter my existing JOIN query below to also include the data from users.image correlating to the UserID of the post maker. Something like:
users.image WHERE users.UserID = posts.userid
I am not very good with join queries yet. How would I do this?
Existing Query:
$result = mysql_query("SELECT posts.* FROM listen JOIN posts ON posts.userid = listen.listenid WHERE listen.userid = '$user_id' ORDER BY DATE desc") or die(mysql_error());
Just add another JOIN clause:
SELECT posts.*
FROM listen
JOIN posts ON (posts.userid = listen.listenid)
JOIN users ON (users.UserID = posts.userid)
WHERE listen.userid = '$user_id'
ORDER BY DATE desc
You may need to change the JOIN to a specific join such as LEFT JOIN, depending on what you're after.
Btw, it is easier to see the query on multiple lines.
Edit: You'll probably want to add additional items that you are selecting with your fields, such as SELECT posts.*, users.*