Joining two tables without returning unwanted row - mysql

My table structure looks like this:
tbl.users tbl.issues
+--------+-----------+ +---------+------------+-----------+
| userid | real_name | | issueid | assignedid | creatorid |
+--------+-----------+ +---------+------------+-----------+
| 1 | test_1 | | 1 | 1 | 1 |
| 2 | test_2 | | 2 | 1 | 2 |
+--------+-----------+ +---------+------------+-----------+
Basically I want to write a query that will end in a results table looking like this:
(results table)
+---------+------------+---------------+-----------+--------------+
| issueid | assignedid | assigned_name | creatorid | creator_name |
+---------+------------+---------------+-----------+--------------+
| 1 | 1 | test_1 | 1 | test_1 |
| 2 | 1 | test_1 | 2 | test_2 |
+---------+------------+---------------+-----------+--------------+
My SQL looks like this at the moment:
SELECT
`issues`.`issueid`,
`issues`.`creatorid`,
`issues`.`assignedid`,
`users`.`real_name`
FROM `issues`
JOIN `users`
ON ( `users`.`userid` = `issues`.`creatorid` )
OR (`users`.`userid` = `issues`.`assignedid`)
ORDER BY `issueid` ASC
LIMIT 0 , 30
This returns something like this:
(results table)
+---------+------------+-----------+-----------+
| issueid | assignedid | creatorid | real_name |
+---------+------------+-----------+-----------+
| 1 | 1 | 1 | test_1 |
| 2 | 1 | 2 | test_1 |
| 2 | 1 | 2 | test_2 |
+---------+------------+-----------+-----------+
Can anyone help me get to the desired results table?

SELECT
IssueID,
AssignedID,
CreatorID,
AssignedUser.real_name AS AssignedName,
CreatorUser.real_name AS CreatorName
FROM Issues
LEFT JOIN Users AS AssignedUser
ON Issues.AssignedID = AssignedUser.UserID
LEFT JOIN Users AS CreatorUser
ON Issues.CreatorID = CreatorUser.UserID
ORDER BY `issueid` ASC
LIMIT 0, 30

On the general knowledge front, our illustrious site founder wrote a very nice blog article on this subject which I find myself referring to over and over again.
Visual Explanation of SQL Joins

Use this:
SELECT
`issues`.`issueid`,
`issues`.`creatorid`,
`creator`.`real_name`,
`issues`.`assignedid`,
`assigned`.`real_name`
FROM `issues` i
INNER JOIN `users` creator ON ( `creator`.`userid` = `issues`.`creatorid` )
INNER JOIN `users` assigned ON (`assigned`.`userid` = `issues`.`assignedid`)
ORDER BY `issueid` ASC
LIMIT 0 , 30

SELECT DISTINCT (i.issueid, i.creatorid, i.assignedid, u.real_name)
FROM issues i, users u
WHERE u.userid = i.creatorid OR u.userid = assignedid
ORDER BY i.issueid ASC
LIMIT 0 , 30
Not sure if the parenthesis are needed or not.

Does this work?
SELECT
i.issueid,
i.assignedid,
u1.real_name as assigned_name,
i.creatorid,
u2.real_name as creator_name
FROM users u1
INNER JOIN issues i ON u1.userid = i.assignedid
INNER JOIN users u2 ON u2.userid = i.creatorid
ORDER BY i.issueid

SELECT
i.issueid,
i.assignedid,
a.real_name,
i.creatorid,
c.real_name
FROM
issues i
INNER JOIN users c
ON c.userid = i.creatorid
INNER JOIN users a
ON a.userid = i.assignedid
ORDER BY
i.issueid ASC

Related

How to limit and search number of joined rows from table in multiple joins in mysql

i have a following tables in MySQL database:
+------------------------+
| Users |
+----+--------+----------+
| id | name | role |
+----+--------+----------+
| 1 | Martin | admin |
+----+--------+----------+
| 2 | George | admin |
+----+--------+----------+
| 3 | John | employee |
+----+--------+----------+
+-------------------------+
| Forms |
+----+--------------------+
| id | type |
+----+--------------------+
| 10 | marketing_form |
+----+--------------------+
| 11 | client_survey_form |
+----+--------------------+
| 12 | client_survey_form |
+----+--------------------+
+---------------------------------------------+
| UsersAssignToForms |
+----+---------+---------+--------------------+
| id | user_id | form_id | additional_comment |
+----+---------+---------+--------------------+
| 20 | 1 | 10 | Lorem ipsum... |
+----+---------+---------+--------------------+
| 21 | 2 | 10 | Lorem ipsum.... |
+----+---------+---------+--------------------+
| 22 | 3 | 10 | null |
+----+---------+---------+--------------------+
| 23 | 3 | 11 | null |
+----+---------+---------+--------------------+
I would like to have result:
+---------+---------+------------+--------------------+--------------------+
| user_id | form_id | first_name | form_type | additional_comment |
+---------+---------+------------+--------------------+--------------------+
| 1 | 10 | Martin | marketing_form | Lorem ipsum... |
+---------+---------+------------+--------------------+--------------------+
| 3 | 11 | John | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
| null | 12 | null | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
First of all i would like to limit number of users returned from join query (one user per one form). If user with admin role is assigned to form i would like to display this user (prioritize admin role over employee role) and limit number of returned users to 1, if admin is not assign, but employee is assigned query should return this user, if no-one is assign query should return nulls (left or right join probably).
I saw this question on stackoverflow - MySQL JOIN with LIMIT 1 on joined table, but unfortunately first answer has n+1 issue and rest of answers was made with simple one join. For my purposes i need to join more tables but wouldn't like to design this tables above to clarify what i would like to achieve, but it's very important.
So my query will looks like probably:
SELECT u.id, f.id, u.name, f.type, uf.additional_comment, [more selects from other tables...] FROM Forms as f
LEFT JOIN Users as u ON ......
INNER JOIN UsersAssignToForms as uf ON .....
[here i would like to put more and more inner joins.....]
In MySql >= 8.0 you can number the rows using some criteria (for each Form starting from one and order by u.role ASC and u.id ASC), then you can filter rows with number one:
WITH sq AS (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment,
ROW_NUMBER() OVER (PARTITION BY f.id ORDER BY u.role ASC, u.id ASC) AS num
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id)
SELECT *
FROM sq
WHERE num = 1;
Before MySql 8.0 you can try something like this (the idea is the same but with different implementation):
SELECT sq2.user_id, sq2.form_id, sq2.name, sq2.type, sq2.additional_comment
FROM (
SELECT
sq1.*,
#row_number:=CASE WHEN #form_id = sq1.form_id THEN #row_number + 1 ELSE 1 END AS num,
#form_id:= sq1.form_id
FROM (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id
ORDER BY f.id ASC, u.role ASC, u.id ASC) AS sq1
ORDER BY sq1.form_id) AS sq2
WHERE sq2.num = 1;

mysql LEFT JOIN main table and two additional tables with max value from each

So i have three tables:
Users
+-------+-----+----+
| id | val1|val2|
+-------+-----+----+
| 1 | 1 |3 |
| 2 | 2 |5 |
| 3 | 4 |7 |
+-------+-----+----+
UsersData
+----+--------------+------------+-----|
| id | users_id | created_at | gold|
+----+--------------+------------+-----|
| 9 | 1 |121454561212| 14 |
| 10| 1 |131454561212| 2 |
| 11| 2 |111454561212| 99 |
+----+--------------+------------+-----+
Extra
+----+------------+-----|
| id | users_id | val4|
+----+------------+-----|
| 1 | 1 | 5 |
| 2 | 1 | 6 |
| 3 | 1 | 7 |
+----+------------+-----+
So what i wish to achieve(in a single query) is to get a single row result for user with id = 1, that holds:
everything from Users Table
gold value of the most recent entry for that user (users_id = 1, created_at = MAX)
biggest val4 from the Extra table, where users_id = 1
So the result row would look like this:
+-------+-----+----+-----+----+
| id | val1|val2|gold |val4|
+-------+-----+----+-----+----|
| 1 | 1 |3 | 2 | 7 |
------------------------------+
I can get The first part done with
SELECT Users.id, Users.val1, Users.val2, UsersData.gold
FROM UsersData
LEFT JOIN Users ON UsersData.users_id = Users.id
WHERE Users.id = 1
ORDER BY UsersData.created_at DESC
LIMIT 1
and the second part with
SELECT MAX(Distances.distance) AS maxdistance FROM Distances WHERE Distances.users_id = 1
But i can't combine them no matter how i try... I would really like to have this done in single query, obviously i can do it with multiple - but i believe it is just my lack of mysql skills that is the issue here.
Thanks!
Just use subquery:
SELECT Users.id, Users.val1, Users.val2, UsersData.gold,
(SELECT MAX(Distances.distance) FROM Distances WHERE Distances.users_id = Users.id) AS maxdistance
FROM UsersData
RIGHT JOIN Users ON UsersData.users_id = Users.id
WHERE Users.id = 1
ORDER BY UsersData.created_at DESC
LIMIT 1
This is subquery connected by Users.id:
SELECT MAX(Distances.distance) FROM Distances WHERE Distances.users_id = Users.id) AS maxdistance
I would use subqueries like this:
select u.*,
(select ud.gold
from userdata ud
where ud.users_id = u.id
order by ud.created_at desc
limit 1
) as most_recent_gold,
(select max(e.val4)
from extra e
where e.users_id = u.id
) as max_val4
from users u
where u.id = 1 ;

MySQL - Left Outer Join not updating original table

I created a toy dataset where I am trying to count the number of posts for each user. I seem to be getting the correct count values but the count column in the users table is not updated with the values.
I'm new to mysql and very confused! Can somebody tell me what I'm doing wrong?
users:
+---------+------+-------+
| user_id | user | pword |
+---------+------+-------+
| 1 | Amy | abcd |
| 2 | Jess | efgh |
| 3 | Lori | ijkl |
+---------+------+-------+
posts:
+---------+-------------+------+
| post_id | post | user |
+---------+-------------+------+
| 1 | hi | Lori |
| 2 | hello | Jess |
| 3 | hello again | Jess |
| 4 | and again | Jess |
+---------+-------------+------+
mysql> ALTER TABLE users ADD COLUMN post_count INT;
mysql> SELECT u.user_id, COUNT(p.user) AS post_count FROM users u LEFT JOIN posts p ON u.user LIKE p.user GROUP BY u.user_id;
+---------+------------+
| user_id | post_count |
+---------+------------+
| 1 | 0 |
| 2 | 3 |
| 3 | 1 |
+---------+------------+
mysql> SELECT * FROM users;
+---------+------+-------+------------+
| user_id | user | pword | post_count |
+---------+------+-------+------------+
| 1 | Amy | abcd | NULL |
| 2 | Jess | efgh | NULL |
| 3 | Lori | ijkl | NULL |
+---------+------+-------+------------+
Thanks!!
Please try the following...
UPDATE users
JOIN ( SELECT u.user_id AS user_id,
COUNT( p.user ) AS post_count
FROM users u
LEFT JOIN posts p ON u.user LIKE p.user
GROUP BY u.user_id ) postCountFinder
ON users.user_id = postCountFinder.user_id
SET users.post_count = postCountFinder.post_count;
This question takes your list of users and post counts obtained from the following...
SELECT u.user_id,
COUNT( p.user ) AS post_count
FROM users u
LEFT JOIN posts p ON u.user LIKE p.user
GROUP BY u.user_id;
... and performs an INNER JOIN with Users on shared value of user_id, creating a dataset with every row from users having the corresponding count tacked on the end.
We then use the SET command to set the empty post_count from users to its corresponding joined count.
If you have any questions or comments, thenplease feel free to post a Comment accordingly.
You need update statement to update the value in the newly added column.Try this:
Update usr
set usr.post_count=tbl.post_count
from users usr
inner join
(select u.user_id,COUNT(p.user)
AS post_count FROM users u
LEFT JOIN posts p ON u.user LIKE p.user GROUP BY u.user_id ) tbl
on tbl.user_id=usr.user_id

MySQL GroupBy with null/zero results

I'm currently writing a ticket system that has three tables
one for users:
users
+----+-----------+----------+
| ID | FirstName | LastName |
+----+-----------+----------+
| 1 | First | User |
| 2 | Second | User |
| 3 | Third | User |
| 4 | Fourth | User |
| 5 | Fifth | User |
+----+-----------+----------+
one for tickets:
ticket
+----+---------------+
| ID | TicketSubject |
+----+---------------+
| 1 | Ticket #1 |
| 2 | Ticket #2 |
| 3 | Ticket #3 |
| 4 | Ticket #4 |
+----+---------------+
and one to assign users to tickets to action (can be more than one user per ticket):
ticket_assigned
+----+----------+--------+
| ID | TicketID | UserID |
+----+----------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 3 | 5 |
| 5 | 3 | 3 |
+----+----------+--------+
I'm trying to create a summary to show each user, and how many tickets they have assigned to them, example:
+------------+-------+
| Name | Count |
+------------+-------+
| First | 2 |
| Second | 1 |
| Third | 1 |
| Fourth | 0 |
| Fifth | 1 |
| Unassigned | 2 |
+------------+-------+
Note that the last entry is "unassigned", this is the number of records in the ticket table that DONT appear in the ticket_assigned table (thus being, unassigned). Also further note that user "Fourth" is zero, in that that user has no records in the ticket_assigned table.
Here is the current MySQL query I am using:
SELECT
CASE
WHEN users.FirstName IS NULL
THEN 'Unassigned'
ELSE users.FirstName
END as 'UserName',
COUNT(*) as 'TicketCount'
FROM tickets
LEFT OUTER JOIN ticket_assigned ON tickets.ticket_id = ticket_assigned.ticket_id
LEFT OUTER JOIN users ON ticket_assigned.user_id = users.user_id
GROUP BY ticket_assigned.user_id
ORDER BY UserName;
Problem with this is that it's not showing any of the users that don't feature in the ticket_assigned table, I'm essentially getting this:
+------------+-------+
| Name | Count |
+------------+-------+
| First | 2 |
| Second | 1 |
| Third | 1 |
| Fifth | 1 |
| Unassigned | 2 |
+------------+-------+
Is anyone able to assist and tell me how I can modify my query to include users that have no records in the ticket_assigned table? Thanks in advance!
Use a LEFT JOIN with a subquery to aggregate tickets:
SELECT t1.FirstName,
COALESCE(t2.ticket_count, 0) AS num_tickets
FROM users t1
LEFT JOIN
(
SELECT UserID, COUNT(*) AS ticket_count
FROM ticket_assigned
GROUP BY UserID
) t2
ON t1.ID = t2.UserID
UNION ALL
SELECT 'Unassigned', COUNT(*)
FROM tickets t
WHERE NOT EXISTS (SELECT 1 FROM tickets_assigned ta
WHERE ta.ticketId = t.id)
In MySQL, I think you need a left join and union all:
select u.id, u.firstname, count(ta.userId) as num_tickets
from users u left join
tickets_assigned ta
on ta.userId = u.id
group by u.id, u.firstname
union all
select NULL, 'Unassigned', count(*)
from tickets t
where not exists (select 1
from tickets_assigned
where ta.ticketId = t.id
);
I included the u.id in the aggregations. I'm uncomfortable just aggregating (and reporting) by first name, because different people frequently have the same first name, even in a relatively small group.
SELECT
u2.Firstname, IFNULL(tmp.count, 0) AS count
FROM users u2
LEFT JOIN (
SELECT u.id, u.Firstname, COUNT(1) as count
FROM ticket_assigned ta
LEFT JOIN ticket t ON t.id = ta.ticketID
LEFT JOIN users u ON u.id = ta.userID
GROUP BY u.id
) tmp ON tmp.id = u2.id
UNION
SELECT
'Unassigned', count(1) AS count
FROM ticket
WHERE id NOT IN (SELECT ticketid FROM ticket_assigned)

Count rows from one tables of users in another table

I want to create a query for project listings that would give the number of registered applications, excluding the ones for which the user does not exist.
In this case, considering user 10 does not exist, I should have the query results as folows:
RESULTS
+----+------------+--------------+
| id | project | applications |
+----+------------+--------------+
| 1 | MyProject1 | 3 |
| 2 | MyProject2 | 0 |
| 3 | MyProject3 | 0 |
+----+------------+--------------+
TABLES
Projects
+----+------------+
| id | name |
+----+------------+
| 1 | MyProject1 |
| 2 | MyProject2 |
| 3 | MyProject3 |
+----+------------+
applications
+----+------+------------+
| id | user | project_id |
+----+------+------------+
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 3 | 5 | 1 |
| 4 | 10 | 1 |
+----+------+------------+
users
+----+---------+
| id | Name |
+----+---------+
| 1 | Smith |
| 2 | John |
| 3 | Paul |
| 4 | Chris |
| 5 | Gabriel |
+----+---------+
The below query is not excluding the non-existing users:
SELECT `projects` . * , (
SELECT COUNT( * )
FROM `applications`
WHERE `applications`.`project_id` = `projects`.`id`
AND EXISTS (
SELECT `applications`.`id`
FROM `applications` , `users`,`project`
WHERE `application`.`user` = `users`.`id` AND `applications`.`project_id` = `project`.`id`
)
) AS `applications`
FROM `projects` ORDER BY `id` DESC LIMIT 30
I think you want left join and group by:
select p.id, p.name, count(u.id)
from projects p left join
applications a
on p.id = a.project_id left join
users u
on a.user_id = u.id
group by p.id, p.name;
However, you might want to think about fixing the data. It seems like there should be foreign key relationships between applications and projects and applications and users. The ability to have an invalid user means that there is no valid foreign key relationship to users.
Your query looks overly complicated. This should do:
select
id,
name as project,
(
select count(*)
from applications a
where a.project_id = p.id
and a.user in (select id from users)
) as applications
from projects p;
Based on previous solution
select p.id, p.name, count(u.id)
from projects p left join
applications a
on p.id = a.project_id left join
users u
on a.user = u.id
where u.id is not null
group by p.id, p.name;
When you do a left join, if the search value doesn't exists, it returns null. Then filtering by excluding null users, will give you the result.
Please find a sqlfiddle to illustrate it : http://www.sqlfiddle.com/#!9/cbfec6/3
But easiest solution would be
select p.id, p.name, count(u.id)
from projects p,applications a, users u
where a.user = u.id
and p.id = a.project_id
group by p.id, p.name;