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;
Related
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;
This is the situation I'm dealing with:
I have 4 tables:
Users table:
+----+-------+
| id | name |
+----+-------+
| 1 | name1 |
| 2 | name2 |
| 3 | name3 |
+----+-------+
Assignment table:
+----+-----------------+
| id | assignment_name |
+----+-----------------+
| 11 | name1 |
| 12 | name2 |
| 13 | name3 |
+----+-----------------+
Submissions:
+----+---------------+---------+
| id | assignment_id | user_id |
+----+---------------+---------+
| 1 | 11 | 3 |
| 2 | 12 | 1 |
| 3 | 11 | 2 |
+----+---------------+---------+
Group_submissions
+----+----------------+---------+
| id | submission_id | user_id |
+----+----------------+---------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 3 | 1 |
+----+----------------+---------+
The submission table has an assignment_id to tell in which assignment the submission belongs to.
Also users can submit a group submission, where the one that does the submission goes to the submissions table, while the others go to the group_submissions table. That way it will be counted as one submission instead of being 2,3...N submission based on how many people where in the group.
How can i get the users that have submitted a submission or have participated in a group submission in a given assignment?
The result should return the user or users that have are in the submissions table or in the group_submissions table based on a assignment id
The result should look something likes this:
+----+-------+
| id | name |
+----+-------+
| 1 | name1 |
| 2 | name2 |
+----+-------+
It should basically return the user table.
This is what i have tried so far:
This only gives me the users that aren't in the submissions table but are in the group_submission
select * from users u
right join group_submissions gs on u.id = gs.student_id
right join assignment_submissions ass on gs.submission_id = ass.id
inner join assignments a on a.id = ass.assignment_id
where a.id = number
This only gives me the one user that made the submission (in the submissions table)
select * from users u
right join assignment_submissions ass on u.id= ass.student_id
right join group_submissions gs on ass.id = gs.submission_id
inner join assignments a on a.id = ass.assignment_id
where a.id = number
What should my join strategy be here? Or maybe joins are not the right option here.
NOTE: This is a MySQL database.
You could use exists:
select u.*
from users u
where
exists (
select 1
from submissions s
where s.user_id = u.id and s.assignment_id = ?
)
or exists (
select 1
from group_submissions gs
inner join submissions s on s.id = gs.submission_id
where gs.user_id = u.id and s.assignment_id = ?
)
I am thinking a union could do it. Like this
select * from
(
select u.*, assignment_id, 'assignment_submissions' as type from users u
inner join assignment_submissions ass on u.id= ass.student_id
union
select u.*, assignment_id, 'group_submissions' from users u
inner join group_submissions gs on u.id = gs.student_id
inner join assignments a on a.id = ass.assignment_id
)a
where assignment_id = ?
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
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)
I have small trouble creating a query. I have two tables:
user_data
+----+---------+--------+
| id | mail | etc... |
+----+---------+--------+
| 1 | 1#m.com | ... |
| 2 | 2#m.com | ... |
| 3 | 3#m.com | ... |
+----+---------+--------+
contracts
+----+---------+--------+
| id | user_id | etc... |
+----+---------+--------+
| 1 | 1 | ... |
| 2 | 2 | ... |
| 3 | 1 | ... |
| 4 | 1 | ... |
| 5 | 3 | ... |
+----+---------+--------+
As you can see, the first table contains data about users and the secound one about their contracts. There will be always only one entry about a user, but a user can have multiple contracts. Now I need to find out
all users, theirs first contract id ( with the lowest id in contracts table ) and their email, if it's in the were parameters.
So far I have such query:
SELECT
u.id as user_id,
c.id as first_contract_id,
u.mail as email
FROM
user_data u
JOIN
contracts c ON u.id = c.user_id
WHERE
u.mail
IN (
'1#me.com',
'2#me.com',
'3#me.com'
);
Now I have no idea how I can select only the lowest contract ID from these results. Help apreciated.
SELECT
u.id as user_id,
min(c.id) as first_contract_id,
u.Mail as email
FROM
user_data u
JOIN
contracts c ON u.id = c.user_id
WHERE
u.mail IN ('1#me.com', '2#me.com', '3#me.com')
GROUP BY u.id
If you group by the user you can get the lowest contract by using min.
(And MySQL has no problem with selecting column that are not in a group)
select
u.id as user_id,
c.id as first_contract_id,
u.Mail as email
from users as u inner join
(
select min(id) as id,user_id from contracts
group by user_id
) as c
on u.id = c.user_id
WHERE
u.mail
IN (
'1#me.com',
'2#me.com',
'3#me.com'
);