Left Join Subquery Exclude GROUP BY with null result - mysql

I am trying to create a query with a GROUP_CONCAT added as a new column in my current query, first here are my tables:
Users table
+----+----------+--------------+
| id | username | date_created |
+----+----------+--------------+
| 1 | user1 | 2000-03-16 |
| 2 | user2 | 2001-05-14 |
| 3 | user3 | 2002-01-13 |
| 4 | user4 | 2003-03-14 |
+----+----------+--------------+
Shifts table
+----+------------+--------------+
| id | shift_name | date_created |
+----+------------+--------------+
| 1 | shift1 | 2002-05-10 |
| 2 | shift2 | 2002-07-11 |
| 3 | shift3 | 2002-09-23 |
+----+------------+--------------+
Accounts table
+----+--------------+--------------+
| id | account_name | date_created |
+----+--------------+--------------+
| 1 | account1 | 2001-05-01 |
| 2 | account2 | 2001-05-02 |
| 3 | account3 | 2001-05-03 |
+----+--------------+--------------+
Shift Mapping table
+----+---------+----------+------------+
| id | user_id | shift_id | account_id |
+----+---------+----------+------------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 |
| 3 | 3 | 1 | 1 |
+----+---------+----------+------------+
basically, I want a query that gets all the user (to display in a table) with a custom column that shows all shift that is attach to that user(if there is no shift attach to that obviously has a null result)
Here is the query I've done so far:
SELECT users.id AS user_id, users.username, users.date_created,
GROUP_CONCAT(DISTINCT (t.shift_name)) AS shifts
FROM (`users`)
LEFT JOIN
(SELECT s.shift_name, sm.user_id FROM shift_map sm
LEFT JOIN shifts s ON sm.shift_id = s.id) t
ON users.id = t.user_id
GROUP BY user_id
ORDER BY `users`.`date_created` DESC;
Now there is no problem getting the users with a shift attach to it, my problem is that the users with no shifts attach to only returns 1 result which is caused by the GROUP BY user_id how can I exclude the users with no shift in the GROUP BY or how can I return all the users with attached shifts and with no attach shifts? Thanks.
Update
Here is the example result I want to see:
+---------+----------+--------------+----------------+
| user_id | username | date_created | shifts |
+---------+----------+--------------+----------------+
| 1 | user1 | 2000-03-16 | shift1,shift2 |
| 2 | user2 | 2001-05-14 | (NULL) |
| 3 | user3 | 2002-01-13 | shift1 |
| 4 | user4 | 2003-03-14 | (NULL) |
+---------+----------+--------------+----------------+
My problem in my query is that it only shows only 1 user with null shifts.

SELECT users.id AS user_id, users.username, users.date_created,
GROUP_CONCAT(DISTINCT (t.shift_name)) AS shifts
FROM (`users`)
LEFT JOIN
(SELECT s.shift_name, sm.user_id FROM mapping sm
LEFT JOIN shifts s ON sm.shift_id = s.id) t
ON users.id = t.user_id
GROUP BY username
ORDER BY user_id
+---------+----------+--------------+---------------+
| user_id | username | date_created | shifts |
+---------+----------+--------------+---------------+
| 1 | user1 | 2000-03-16 | shift1,shift2 |
| 2 | user2 | 2001-03-16 | NULL |
| 3 | user3 | 2002-03-16 | shift1 |
| 4 | user4 | 2003-03-16 | NULL |
+---------+----------+--------------+---------------+

My Bad, I can just use the simple LEFT JOIN:
SELECT users.id AS user_id, users.username, users.date_created,
GROUP_CONCAT(DISTINCT (shift.shift_name)) AS shifts
FROM (`users`)
LEFT JOIN shifts_map ON users.id = shifts_map.user_id
LEFT JOIN shifts ON shifts_map.shift_id = shift.id
GROUP BY user_id
ORDER BY `users`.`date_created` DESC;
I am just complicating the query, Forgot that the simple LEFT JOIN can do the trick. Thanks.

Related

Return the data based on the statuses from other table in SQL

I have table with users:
+-----------+----------+
| id | name |
+-----------+----------+
| 1 | Joe |
| 2 | Tom |
| 3 | Jack |
| 4 | Tim |
+-----------+----------+
and second table with tasks liked with these users:
+--------------+--------------+--------------+
| id | user_id | status |
+--------------+--------------+--------------+
| 1 | 1 | new |
| 2 | 1 | done |
| 3 | 1 | in_progress |
| 4 | 2 | in_progress |
| 5 | 2 | done |
| 6 | 2 | done |
| 7 | 2 | done |
| 8 | 3 | new |
| 9 | 3 | new |
| 10 | 3 | new |
| 11 | 4 | in_progress |
| 12 | 4 | in_progress |
| 13 | 4 | new |
+--------------+--------------+--------------+
Each task could be in 'new', 'in_progress' or 'done' status.
I would like to get a list of user_ids who do not have any tasks in 'new' status but have a task in 'done' status.
Could anyone help me with this? Thanks in advance!
A variety of ways to accomplish this. Here are just a couple:
Query #1: Use CTEs
with done as (
select distinct user_id
from tasks
where status = 'done'
),
new as (
select distinct user_id
from tasks
where status = 'new'
)
select u.id, u.name
from users u
join done d
on u.id = d.user_id
where u.id not in (select user_id from new);
id
name
2
tom
Query #2: No CTEs
select id, name
from users
where id in (select user_id from tasks where status = 'done')
and id not in (select user_id from tasks where status = 'new');
id
name
2
tom
View on DB Fiddle
select u.id , u.name,t.status from users u
left join tasks t on t.user_id = u.id
where t.status<>'new';

MySQL sort by sum multiple columns in different tables

I have 3 tables:
Users
| id | name |
|----|-------|
| 1 | One |
| 2 | Two |
| 3 | Three |
Likes
| id | user_id | like |
|----|---------|-------|
| 1 | 1 | 3 |
| 2 | 1 | 5 |
| 3 | 2 | 1 |
| 4 | 3 | 2 |
Transations
| id | user_id | transaction |
|----|---------|-------------|
| 1 | 1 | -1 |
| 2 | 2 | 5 |
| 3 | 2 | -1 |
| 4 | 3 | 10 |
I need get sum of likes.like and transations.transation for each user and then sort it by its result.
I was able to do it for users and likes:
select users.*, sum(likes.like) as points
from `users`
inner join `likes` on `likes`.`user_id` = `users`.`id`
group by `users`.`id`
order by points desc
But then I add transactions table like this:
select users.*, (sum(likes.like)+sum(transactions.`transaction`)) as points
from `users`
inner join `likes` on `likes`.`user_id` = `users`.`id`
inner join `transactions` on `transactions`.`user_id` = `users`.`id`
group by `users`.`id`
order by points desc
It is show wrong results.
I expecting to see:
| id | name | points |
|----|-------|--------|
| 3 | Three | 12 |
| 1 | One | 7 |
| 2 | Two | 5 |
But get this instead:
| id | name | points |
|----|-------|--------|
| 3 | Three | 12 |
| 1 | One | 6 |
| 2 | Two | 5 |
So, how sort users by sum likes.like and transations.transation?
Thank you!
Since there's not a 1-to-1 relationships between transactions and likes, I think you need to use subqueries:
select users.*,
(select sum(points) from likes where user_id = users.id) as points,
(select sum(transaction) from transactions where user_id = users.id) as transactions
from users
order by points desc
Updated after more explanation of requirements:
select users.*,
(select sum(points) from likes where user_id = users.id) +
(select sum(transaction) from transactions where user_id = users.id) as points
from users
order by points desc

mysql join 3 tables by id

I have 3 tables to join and need some help to make it work, this is my schema:
donations:
+--------------------+------------+
| uid | amount | date |
+---------+----------+------------+
| 1 | 20 | 2013-10-10 |
| 2 | 5 | 2013-10-03 |
| 2 | 50 | 2013-09-25 |
| 2 | 5 | 2013-10-01 |
+---------+----------+------------+
users:
+----+------------+
| id | username |
+----+------------+
| 1 | rob |
| 2 | mike |
+----+------------+
causes:
+--------------------+------------+
| id | uid | cause | <missing cid (cause id)
+---------+----------+------------+
| 1 | 1 | stop war |
| 2 | 2 | love |
| 3 | 2 | hate |
| 4 | 2 | love |
+---------+----------+------------+
Result I want (data cropped for reading purposes)
+---------+-------------+---------+-------------+
| id | username | amount | cause |
+---------+-------------+---------+-------------+
| 1 | rob | 20 | stop war |
| 2 | mike | 5 | love |
+---------+-------------+-----------------------+
etc...
This is my current query, but returns double data:
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid)
EDIT: fixed sql schema on fiddle
http://sqlfiddle.com/#!2/0e06c/1 schema and data
How I can do this?
It seems your table's model is not right. There should be a relation between the Causes and Donations.
If not when you do your joins you will get duplicated rows.
For instance. Your model could look like this:
Donations
+--------------------+------------+
| uid | amount | date | causeId
+---------+----------+------------+
| 1 | 20 | 2013-10-10 | 1
| 2 | 5 | 2013-10-03 | 2
| 2 | 50 | 2013-09-25 | 3
| 2 | 5 | 2013-10-01 | 2
+---------+----------+------------+
causes:
+----------------------+
| id | cause |
+---------+------------+
| 1 | stop war |
| 2 | love |
| 3 | hate |
+---------+------------+
And the right query then should be this
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.id = tti.causeId)
Try this
SELECT CONCAT(i.username ,' ',i.first_name) `name`,
SUM(tti.amount),
t.cause AS tag_name
FROM users i
LEFT JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid)
GROUP BY i.id
Fiddle
You need to match the id from both the users and causes table at the same time, like so:
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid and t.id = i.id)
Apologies for formatting, I'm typing this on a phone.

Get all users even if no records in another table

I'm facing a problem here :
I have two tables :
A users table :
+----+---------------+----------+
| id | username | company |
+----±---------------±----------+
| 1 | John | 0 |
| 2 | Jack | 0 |
| 3 | Casimir | 0 |
±----±---------------±----------±
A orders table :
+----+---------------+----------+--------+
| id | date | iduser | status |
+----±---------------±----------+--------+
| 1 | 2012-05-28 | 1 | 1 |
| 2 | 2012-05-25 | 1 | 1 |
| 3 | 2012-04-28 | 2 | 1 |
| 4 | 2012-03-28 | 1 | 1 |
| 5 | 2012-02-28 | 2 | 0 |
±----±---------------±----------±--------+
What I'm trying to do is to get a result like this :
+----------+---------------+-------------+
| username | COUNT(order) | MAX(date) |
+----------±---------------±-------------+
| John | 3 | 2012-05-28 |
| Jack | 1 | 2012-04-28 |
| Casimir | 0 | NULL |
±----------±---------------±-------------±
Here's the request I have for the moment :
SELECT u.username, COUNT(o.id), MAX(o.date)
FROM users u
INNER JOIN orders ON u.id = o.iduser
WHERE o.status = 1
GROUP BY u.id
This request gives me a result like :
+----------+---------------+-------------+
| username | COUNT(order) | MAX(date) |
+----------±---------------±-------------+
| John | 3 | 2012-05-28 |
| Jack | 1 | 2012-04-28 |
±----------±---------------±-------------±
As you can see, the user Casimir is not shown as he made no order. How can I modify my request to get the result I need please ?
Thanks !
A LEFT JOIN or LEFT OUTER JOIN will include all rows of the inital table, including those where there is no match in the joined-to table
SELECT u.username, COUNT(o.id), MAX(o.date)
FROM users u
LEFT OUTER JOIN orders o ON u.id = o.iduser AND o.status = 1
GROUP BY u.id
You need to use an OUTER JOIN instead of your current INNER JOIN.
Have a look at Jeff's post here to see how they differ:
http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html

Select Distinct from multiple tables with order by

I'm stucked with a Mysql query, can you help me?
I have two tables:
user
id | name
1 | foo1
2 | foo2
3 | foo3
posts
id | id_user | created_at | kind
1 | 2 | 15-03-2011 | a
1 | 2 | 14-03-2011 | b
2 | 3 | 13-03-2011 | a
1 | 2 | 12-03-2011 | b
What I want is to retrieve the latest post of each user (the kind doesn't matter) ordered by de creation date.
How can I do that?
Thank you guys
One possible query is:
SELECT
u.id,
(SELECT MAX(p.created_at) FROM posts AS p WHERE u.id = p.id_user) AS latest
FROM
user AS u;
although the dependent subquery may not be the best solution to this. Example output:
users:
+------+------+
| id | name |
+------+------+
| 0 | test |
| 1 | one |
+------+------+
posts:
+------+---------+------------+------+
| id | id_user | created_at | kind |
+------+---------+------------+------+
| 0 | 0 | 2011-02-05 | a |
| 1 | 1 | 2011-02-06 | b |
| 2 | 0 | 2011-02-03 | a |
| 3 | 1 | 2011-02-02 | b |
+------+---------+------------+------+
output:
+------+------------+
| id | latest |
+------+------------+
| 0 | 2011-02-05 |
| 1 | 2011-02-06 |
+------+------------+
You can also add an ORDER BY latest DESC to the end of the query if you wish to get an ordered list of the latest posts across all user IDs.
Using a GROUP BY on id_user and the max post date ?
Something like that :
SELECT u.name, p.id_user, MAX( p.created_at )
FROM posts AS p
LEFT JOIN user AS u ON u.id
WHERE p.id_user = u.id
GROUP BY id_user