Left Join on association table - mysql

I need help trying to join a table that uses an association table.
I have a users table
Users Table
user_id
user_name
Association Table
user_id
project_id
Project Table
project_id
project_name
I need to pull a user and the count of projects they are associated with.
SELECT u.user_name, COUNT(p.project_id) projects
FROM users u
LEFT JOIN association a ON u.user_id = a.user_id
GROUP BY u.user_name
How do I associate the two tables?

If you want to associate projects and users, you need to do 2 joins:
SELECT u.user_name, COUNT(p.project_id) projects
FROM users u
LEFT JOIN association a ON u.user_id = a.user_id
LEFT JOIN projects p ON p.project_id = a.project_id
GROUP BY u.user_name
If you want it faster you can do:
SELECT u.user_name, COUNT(a.project_id) projects
FROM users u
LEFT JOIN association a ON u.user_id = a.user_id
GROUP BY u.user_name

I think you can do something like:
SELECT
Utbl.user_name,
NumTbl.numProjects
FROM
UsersTable Utbl,
(SELECT
Atbl.user_id,
COUNT(*) AS numProjects
FROM
ProjectTable Ptbl,
AssociationTable Atbl
WHERE
Utbl.user_id = Atbl.user_id AND
Atbl.project_id = Ptbl.project_id) NumTbl
WHERE
Utbl.user_id = NumTbl.user_id

Related

How to triple JOIN tables to perform a query with OR operand in MySQL [Specific Case]

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
);

MySql join with 2 Tables and 2 join

I have 2 tables:
posts: userid, lastuserid
users: id, name
I need to join posts.userid = users.id and posts.lastuserid = users.id to get username and lastusername.
My query did as below:
SELECT posts. * , users.name, vUsers.name
FROM posts
INNER JOIN users ON users.id = posts.userid
INNER JOIN Users ON vUsers.id = posts.lastuserid
Is there any other (better) way to do this?
Your query is probably correct. I would encourage you to use table aliases that are abbreviations for the things you are looking for:
SELECT p. * , u.name as username, l.name as lastusername
FROM posts p INNER JOIN
users u
ON u.id = p.userid INNER JOIN
users l
ON l.id = p.lastuserid;
Your query has something called vUsers, which is not defined.

SQL join count(*) of one table to another table as aliases

I have two table, users and comments.
In the users table, there're columns id and username.
In the comments table I have user_id and his message.
And I wanted to create a table that select the username and his comment count when I search a particular username.
How do I write this?
my testing attempt:
SELECT COUNT(*) AS comment_count
FROM song_comments
RIGHT JOIN users
WHERE user_id = 7 AND comments.user_id = users.id
Try this:
SELECT U.Username, COUNT(SC.message) AS comment_count
FROM song_comments SC JOIN
users U ON U.id=SC.user_id
WHERE U.user_id = 7
GROUP BY U.Username
This gives you users and count
select u.username, count(c.user_id) as comment_count
from users u
join comments c on u.id = c.user_id
group by u.username
You can add a where to get one user's count
select u.username, count(c.user_id) as comment_count
from users u
join comments c on u.id = c.user_id
where u.username = 'Hogan'
group by u.username

chat application friend list

I have 3 MySQL tables namely chat_comments, chat_friends and user_details and I want to display a friend list.
My tables:
chat_comments(comment_id,comment,user_id,user_id2,date_added)
chat_friends(user_id,user_id2,approved)
user_details(user_id, mainimage_id, fullname)
To do this, I need a query that will return the needed fields (u.mainimage_id, u.fullname, b.comment, b.user_id) so I can loop through the list to display a table.
SQL so far (help from #Andriy M):
SELECT
cc.comment,
cc.date_added,
u.fullname,
u.mainimage_id
FROM
user_details u
LEFT JOIN
chat_comments cc
INNER JOIN (
SELECT
user_id,
MAX(comment_id) AS maxcomment
FROM chat_comments WHERE user_id=2020 OR user_id2=2020
GROUP BY user_id
) a ON a.user_id = cc.user_id
AND a.maxcomment = cc.comment_id
ON a.user_id = u.user_id
WHERE u.user_id IN (
SELECT user_id2
FROM chat_friends
WHERE user_id = 2020
AND approved = 1
)
The above query returns the last comment made by the logged-in user's friends in conversation not the last comment between the logged-in user and his/her friend regardless of who made it.
I would like it to return the last comment between the logged-in user and their friend individually regardless of who made it. In the chat_messages table, user_id is the sender and user_id2 is the receiver. Hope it makes sense?
Like #imm said in a comment, you need to use an outer join. In case of a left join, the user_details table should become the left side of the join, the right side being the result of your inner join of chat_comments with your a derived table. You'll also need to remove the user_id IN (…) condition from inside the a subselect and re-apply it to the user_details table. Here:
SELECT
cc.comment,
cc.date_added,
u.fullname,
u.mainimage_id
FROM
user_details u
LEFT JOIN
chat_comments cc
INNER JOIN (
SELECT
user_id,
MAX(comment_id) AS maxcomment
FROM chat_comments
GROUP BY user_id
) a ON a.user_id = cc.user_id
AND a.maxcomment = cc.comment_id
ON a.user_id = u.user_id
WHERE u.user_id IN (
SELECT user_id2
FROM chat_friends
WHERE user_id = 2020
AND approved = 1
)
;
Alternatively, you could use a right join. In this case you would just need to move the user_id IN (…) condition, similarly to the LEFT JOIN solution above, and replace the second INNER JOIN with RIGHT JOIN:
SELECT
cc.comment, cc.date_added, u.fullname, u.mainimage_id
FROM
(
SELECT user_id, MAX(comment_id) AS maxcomment
FROM chat_comments
GROUP BY user_id
) a
INNER JOIN
chat_comments cc ON
a.user_id = cc.user_id AND
a.maxcomment = cc.comment_id
RIGHT JOIN
user_details u ON
a.user_id = u.user_id
WHERE u.user_id IN (select user_id2 from chat_friends where user_id=2020 AND approved=1)

Need help with a mysql query

I have a payments table that has the following structure.
**Payments**
id
name
created_by - user_id
closed_by - user_id
**Users**
user_id
name
surname
What is the best way to show both the name and surname of the user who has created and closed the payment file.
The only way i can think this could work would be using a subquery for both(created_by,closed_by fields) thanks
Try this:
SELECT p.id, p.name,
CONCAT (u1.name,' ', u1.surname) created,
CONCAT (u2.name,' ', u2.surname) closed,
FROM payments p INNER JOIN users u1
ON p.created_by = u1.user_id
INNER JOIN users u2
ON p.closed_by = u2.user_id
EDITED: if you want name and surname splitted
SELECT p.id, p.name,
u1.name created_name, u1.surname created_surname,
u2.name closed_name, u2.surname closed_surname,
FROM payments p INNER JOIN users u1
ON p.created_by = u1.user_id
INNER JOIN users u2
ON p.closed_by = u2.user_id
A strict join is not enough, I think you can use joins in the where clause
select name, surname
from
payments p, users u
where
u.user_id = p.created_by
and u.user_id = p.closed_by
You can join to a single table multiple times, but you have to use an alias.
select
p.*,cr.*,cl.*
from
payments p
join users cr
on p.created_by = cr.user_id
join users cl
on p.closed_by = cl.user_id