SELECT *
FROM post p
JOIN user u ON p.user_id = u.id
JOIN friendships f ON f.friend_id = u.id
WHERE f.user_id = 1 OR u.id = 1
ORDER BY p.created_at DESC;
working on a projects where I'm trying to get all the post of the user as well as the user currently on.
So far i have this query working but is giving me duplicate posts of users.id = 1
is a user self join many to many where each user become friends and each user has their posts
The problem is probably because you select all columns from all tables involved.
Now for every new friendship with friend_id=1 you will receive a new record, with post duplicated. I guess you need:
select distinct p.*
Related
Hello dear SO community,
I have been struggling with this for some time now and since time is running out I turn to you.
I have these tables:
USERS:
user_id (PK)
user_name
PROJECTS:
project_id (PK)
project_name
user_id (FK) - referring to the creator of the project
Users and projects are in M:N relationship, which is captured in table PERMISSIONS.
PERMISSIONS:
project_id (FK)
user_id (FK)
Permissions row holds a access rule for single user and single project. If a pair for given user and project does not exist in the table, that user cannot access that project.
I am trying to query all projects that current user should have access to, meaning those that he created and those that he is associated with in the PERMISSIONS table but with every project I want the user_name of its creator. I supply current user ID to a prepared statement in PHP, which works fine, but I cannot seem to get the query right. I have been able
I was able to to get projects that were created or accessible by certain user, but I cannot figure out how to join USERS, to get the user_name of user_id in projects (username of the project creator) that I query.
I am querying something along the lines of:
SELECT projects.name as name, projects.project_id as project_id, projects.user_id as user_id, users.name as user_name
FROM projects JOIN permissions USING (project_id) JOIN users USING (user_id) WHERE permissions.user_id=:user_id OR
projects.user_id=:user_id ORDER BY name
There are three possibilities:
Have a join on projects with or, to fetch both creators and those with permission. A join with or is generally not advisable because it leads to table scans. Distinct is needed
select distinct u.user_name, p.project_name
from users u
left join permissions pm on pm.user_id = u.user_id
left join projects p on p.user_id = u.user_id or p.project_id = pm.project_id
where u.user_id = 2;
Cross join projects and have the 'or's in the where clause. Need distinct.
select distinct u.user_name, p.project_name
from users u
cross join projects p
left join permissions pm on pm.user_id = u.user_id
where u.user_id = 2 and ( p.project_id = pm.project_id or p.user_id = u.user_id );
Use union, one select for creators and one for those with permission. Distinct is not needed because union does distinct
select u.user_name, p.project_name
from users u
inner join projects p on p.user_id = u.user_id
where u.user_id = 2
union
select u.user_name, p.project_name
from users u
inner join permissions pm on pm.user_id = u.user_id
inner join projects p on p.project_id = pm.project_id
where u.user_id = 2
online editor here: https://onecompiler.com/mysql/3y64ukxtp
The issue in your query is that the user_id field should be both matched:
between users and permissions, to catch projects the user is allowed to work on
between users and projects, to catch projects the user has created
One way of solving this issue is to have a UNION operation between these two result sets. In order to make the joins more efficient, it's better to move the filtering operation (finding the specific user) before the join operation, that's the reason why I've moved it into a subquery (under the shape of common table expression - cte).
WITH user_details AS(
SELECT user_id,
user_name
FROM users
WHERE user_id = 501
)
SELECT user_details.*,
projects.project_id,
projects.project_name,
projects.user_id AS project_creator
FROM user_details
INNER JOIN projects
ON user_details.user_id = projects.user_id
UNION
SELECT user_details.*,
projects.project_id,
projects.project_name,
projects.user_id AS project_creator
FROM user_details
INNER JOIN permissions
ON user_details.user_id = permissions.user_id
INNER JOIN projects
ON permissions.project_id = projects.project_id
You can try it in a sample environment here.
With this query:
SELECT project_id FROM projects WHERE user_id = :user_id
UNION ALL
SELECT project_id FROM permissions WHERE user_id = :user_id
you get the ids of all projects that :user_id created or has access to.
Use it with the operator IN:
SELECT p.*, u.user_name
FROM projects p INNER JOIN users u
ON u.user_id = p.user_id
WHERE p.project_id IN (
SELECT project_id FROM projects WHERE user_id = :user_id
UNION ALL
SELECT project_id FROM permissions WHERE user_id = :user_id
);
I have 3 relational tables : users, posts, and reposts.
In reposts table, I have fields: user_id (the user who reposted the post) and, post_id.
In posts table, I have fields: id, posts, and user_id.
In users table, I have field: id and first_name.
Here are what the data I wanted to get:
The post
The original user who posted
The reporter or the user who reposted the post.
So far i only have this query:
SELECT
p.post,
u.first_name as 'Retweeter'
FROM reposts r
LEFT JOIN users u
ON u.id = r.user_id
LEFT JOIN posts p
ON p.id = r.post_id
In that code I can get the post and the user who reposted the post. But how can I get the original user or the details of the original user who posted the post, using the post_id field from the reposts table?
Thank you very much!
you need one more join on users table for getting original poster name
SELECT
ou.first_name originalPoster,
p.post,
u.first_name as 'Retweeter'
FROM reposts r
LEFT JOIN users u
ON u.id = r.user_id
LEFT JOIN posts p
ON p.id = r.post_id
LEFT JOIN users ou
on p.user_id = ou.id
You have to join users twice, once against posts and once against reposts. If you want to include posts that haven't been reposted, you should start the FROM clause with posts and then do the left joins.
SELECT p.post,
up.first_name,
urp.first_name
FROM posts p
LEFT JOIN users up
ON up.id = p.user_id
LEFT JOIN reposts rp
ON rp.post_id = p.id
LEFT JOIN users urp
ON urp.id = rp.user_id;
My query looks like:
SELECT *
FROM users U
LEFT JOIN posts P ON P.userId = U.id AND P.userId IS NOT NULL;
Why the query also return result where userId is null ?
I know that for my needs I can use INNER JOIN to get only posts related to user but is so strange that LEFT JOIN support multiple conditions, but not work with NOT NULL conditions.
This is because "posts" does not contain the null-values and hence they can´t be filtered at that stage. The Null-values are only generated trough the join, when the server can´t find a corresponding row on the right table. So just put the not null in the where clause and it will work:
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id WHERE userId IS NOT NULL;
(EDIT: You should use an inner join for productive work though, as it is the proper way and will give you much greater performance.)
You can also see all users who don´t have posts by inverting that:
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id WHERE userId IS NULL;
You are outer joining the posts table. This means for every users record that has no match in posts you still get this record with all posts columns null.
So say you have a users record with userid = 5 and there is no posts record with id = 5.
ON P.userId = U.id AND P.userId IS NOT NULL
The two combined conditions are not met (there is no record with userid 5), so you get the users record with all posts columns set to null in your results.
Maybe you are simply looking for an inner join? All users records with their posts data?
This query:
SELECT *
FROM users U LEFT JOIN
posts P
ON P.userId = U.id AND P.userId IS NOT NULL;
Returns all rows in the users as well as all columns from posts, regardless of whether or not they match. This is true, regardless of whether the ON clause evaluates to TRUE or FALSE.
What you want is a WHERE. In addition, you should only select the columns from users:
SELECT u.*
FROM users U LEFT JOIN
posts P
ON P.userId = U.id
WHERE P.userId IS NOT NULL;
Note that you can also accomplish this using NOT IN or NOT EXISTS.
Because the LEFT JOIN must return every row from the left table by it's definition. The raw may be augmented with the data of the right table depending on the ON clause evaluation. So the following code must return a row.
select u.*, p.*
from (
select 1 as id
) u
left join (
-- no data at all
select 2 as id where 1=2
) p on 3 = 4 -- never is true
Try this
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id
SELECT * FROM users U LEFT JOIN posts P ON P.userId = U.id where P.userId IS NOT NULL;
IS NOT NULL WITH JOINS
SELECT * FROM users
LEFT JOIN posts ON post.user_id = users.id
WHERE user_id IS NOT NULL;
I have 4 tables: posts, users, mentions, following
posts
----------------------------
id | user_id | post_text
1 1 foo
2 1 bar
3 2 hello
4 3 jason
users
------------
id | name
1 jason
2 nicole
3 frank
mentions
--------------------------
id | post_id | user_id
1 4 1
following
-------------------------------------------------
id | user_id | user_id_of_user_being_followed
1 1 2
posts includes the user_id of the user who posted some text
users has the user id and name of the user
mentions has the post id and user id of any post which has mentioned 1 or more other users
following has a the user id and the user they are following (user can follow 0 to many users)
What I'm trying to do is return all posts from users a that a given user follows, PLUS any posts that have mentioned that user (whether or not the given user is following), without returning any duplicates.
SELECT p.id, p.post, u.name,
FROM following f
JOIN posts p ON f.following = p.user_id
JOIN users u ON u.id = p.user_id
WHERE f.user_id = :user;
The above returns all posts from users that a given user is following, but I'm struggling figuring out how to include mentions as well (remember, a user does not have to follow someone to be able to see the post they've been mention in).
UPDATE:
Thanks to John R I was able to figure this out:
SELECT DISTINCT(p.id), p.post, u.name
FROM posts p
LEFT JOIN following f ON f.following = p.user_id
LEFT JOIN mentions m ON m.posts_id = p.id
JOIN users u ON u.id = p.user_id
WHERE (f.user_id = :user_id OR m.user_id = :user_id)
if i understand your querstion correctly you would want a left join to include any mentions.. but not filter out any followers/posts
if you can add some sample data to play with I can make sure its working how you want it to...
SELECT
if(p.id is not null, p.id, p1.id) as post_id,
if(p.post is not null, p.post, p1.post) as post_text,
u.username, m.id, m.user_id
FROM posts p
JOIN users u on u.id = p.user_id
JOIN following f on f.user_id_of_user_being_followed = u.id
LEFT JOIN mentions m on m.user_id = f.user_id
LEFT JOIN posts p1 on p1.id = m.post_id
WHERE f.user_id = :user or m.user_id = :user;
I left join mentions to the post made and also when the user_id in the mention table is equal to the specified user to filter out other users. the left join shouldn't change the number of rows returned.. but only include any mentions
EDIT: WORKING FIDDLE
after playing around with it I realised it was trying to put all of the data into one row.. try this:
(
SELECT p.id, p.post_text, u.name
FROM posts p
JOIN users u on u.id = p.user_id
JOIN following f on f.user_id_of_user_being_followed = u.id
WHERE f.user_id = 1
)
UNION
(
SELECT p.id, p.post_text, u.name
FROM following f
JOIN mentions m on m.user_id = f.user_id
JOIN posts p on p.id = m.post_id
join users u on u.id = p.user_id
WHERE f.user_id = 1
);
Maybe you inherited this db; but the last table is not really in line with good data normalization. The table should be the id and following_id; as set up you'll eventually run out of columns (or have to keep adding them when a user gets an error) - new users won't be able to follow anyone.
I need some help with query from multiple tables.
My database:
I have following tables:
1. users (user_id, user_name, ..) //user_name is unique.
2. guestbook_comments(owner_id, comment_author_id, comment ..)
3. profile_photo(profile_photo_id, user_id, path)
My problem:
I want to build a following query:
I have url like guestbook.php?user=sudeep. So i get user_name by $_GET['user']; user_name is unique (in the users table).
I want to display all entries from guestbook_comments for a specific owner_id (but i only know user_name, so i will need to find user_id from users table).
For each of the comment_author_id in guestbook_comments table, I want to get the his user_name from table users and path from profile_photo table.
My approach:
First I join users and guestbook_comments table to get all guestbook comments for a specific user_id. Then In the while loop I join users and profile_photo table to get user_name and photo path respectively.
I highly doubt if my approach is any efficient. Can you tell me if there is a right way to do that?
$sql = "SELECT u.user_id, g.owner_id, g.comment_author_id
FROM guestbook g
INNER JOIN users u ON u.user_id = g.owner_id
WHERE u.user_name = $user_name";
while($row = mysql_fetch_array( $result )) {
$author_id = $row['comment_author_id'];
$sql = "SELECT u.user_name, p.path
FROM profile_photo p
INNER JOIN users u ON u.user_id = p.user_id
WHERE u.user_id = $author_id;
//display comment text from guestbook, user_name from users, and photo from profile_photo
}
Am I missing something, or could you combine both joins at once and get all the data with 1 call to the database, rather than 2 calls per user?
$sql = "SELECT u.user_id, g.owner_id, g.comment_author_id, p.path
FROM guestbook g
INNER JOIN users u ON u.user_id = g.owner_id
INNER JOIN profile_photo p on u.user_id = p.user_id
WHERE u.user_name = $user_name";
Try a single query:
SELECT u.user_id, g.owner_id, g.comment_author_id, g.comment_text, c.user_name, p.path
FROM users u
LEFT JOIN guestbook g
ON u.user_id = g.owner_id
LEFT JOIN users c
ON c.user_id = g.comment_author_id
LEFT JOIN profile_photo p
ON p.user_id = g.comment_author_id
WHERE u.user_name = $user_name
From user, find guestbook entries, find commenters, find commenters' photos