Mysql full outer join 3 tables with group by and count - mysql

My query is only showing a subset of records of people who have messages for me whereas I want to return a list of all users wether they have messages for me or not along with the count (or true/false, 0,1)
SELECT attendee.id, attendee.firstName, attendee.lastName, count(attendee_chat.to)
from attendee_chat
INNER JOIN attendee ON attendee.id = attendee_chat.to
INNER JOIN chat ON attendee_chat.id = chat.attendee_chat_id
WHERE attendee.id <> 1
GROUP BY attendee_chat.to;
picture is worth a thousand words.

Put the attendee table first in the FROM, and then left join to the other tables that may not have records.
SELECT attendee.id, attendee.firstName, attendee.lastName, count(attendee_chat.to)
from attendee
LEFT JOIN attendee_chat ON attendee_chat.to = attendee.id
LEFT JOIN chat ON attendee_chat.id = chat.attendee_chat_id
WHERE attendee.id <> 1
GROUP BY attendee_chat.to;
I would avoid doing a FULL OUTER JOIN as it is not necessary and may lead to some unexpected results.
EDIT: Try this. Remove the chat table as you aren't selecting from it anyway, and group on the fields from the primary table attendee, rather than the on tables that may not contain any data.
SELECT attendee.id, attendee.firstName, attendee.lastName, count(attendee_chat.to)
from attendee
LEFT JOIN attendee_chat ON attendee_chat.to = attendee.id
WHERE attendee.id <> 1
GROUP BY attendee.id, attendee.firstName, attendee.lastName;
EDIT2: After discussion of requirements that he wants a count of chats that were to him (#1)
SELECT attendee.id, attendee.firstName, attendee.lastName,
(SELECT count(1) FROM attendee_chat
WHERE attendee_chat.from = attendee.id and attendee_chat.to = 1) as chatcount
from attendee ;
EDIT3: After comment about external ID
SELECT attendee.id, attendee.firstName, attendee.lastName, attendee.attendee_id,
(SELECT count(1) FROM attendee_chat
INNER JOIN attendee a2 ON a2.id = attendee_chat.toid
WHERE attendee_chat.fromid = attendee.id and a2.attendee_id = 123
) as chatcount
FROM attendee ;
P.S. I've changed from to fromid and to -> toid as it is dangerous using reserved words as object names ;-) SQL Fiddle here

If you want to do a join on records even if they have no matching element(s) in the other relation, you probably want an OUTER JOIN instead of an INNER JOIN.
MySQL: Quick breakdown of the types of joins

Related

SQL Left Join a Table on a Left Joined Table

Iam currently trying to left join a table on a left joined table as follows.
I have the tables:
accounts (id, vorname, nachname)
projektkurse (id, accounts_id, projektwochen_id)
projektkurs_einzel (id, projektkurse_id)
projektkurs_einzel_zeiten (id, date, shift, projektkurs_einzel_id)
Now I want to get every account and the amount times they have an entry inside of projektkurs_einzel_zeiten, which should also be unique. So having the same date and shift multiple times does not count as multiple entries. The result should also be limited by the column projektwochen_id from the table projektkurse. This column should match a certain value for example 8.
Some Accounts don't have any entries in projektkurse, projektkurs_einzel and projektkurs_einzel_zeiten, this is why my first thought was using LEFT JOIN like this:
SELECT accounts.id, accounts.vorname, accounts.nachname, COUNT(DISTINCT projektkurs_einzel_zeiten.date, projektkurs_einzel_zeiten.shift) AS T
FROM accounts
LEFT JOIN projektkurse on accounts.id = projektkurse.creator_id
LEFT JOIN projektkurs_einzel on projektkurse.id = projektkurs_einzel.projektkurs_id
LEFT JOIN projektkurs_einzel_zeiten ON projektkurs_einzel.id = projektkurs_einzel_zeiten.projektkurs_einzel_id
WHERE projektkurse.projektwochen_id = 8
GROUP BY accounts.id
This query does not achieve exactly what I want. It only returns accounts that have atleast one entry in projektkurse even if they have none in projektkurs_einzel and projektkurs_einzel_zeiten. The Count is obviously 0 for them but the accounts that have no entries in projektkurse are being ignored completly.
How can I also show the accounts that don't have entries in any other table with the Count 0 aswell?
I would recommend writing the query like this:
SELECT a.id, a.vorname, a.nachname,
COUNT(DISTINCT pez.date, pez.shift) AS T
FROM accounts a LEFT JOIN
projektkurse
ON a.id = pk.creator_id AND
pk.projektwochen_id = 8 LEFT JOIN
projektkurs_einzel pe
ON pk.id = pe.projektkurs_id LEFT JOIN
projektkurs_einzel_zeiten pez
ON pe.id = pez.projektkurs_einzel_id
GROUP BY a.id, a.vorname, a.nachname;
Notes:
Your problem is fixed by moving the WHERE condition to the ON clause. Your WHERE turns the outer join into an inner join, because NULL values do not match.
Table aliases make the query easier to write and to read.
It is a best practice to include all unaggregated columns in the GROUP BY. However, assuming that id is unique, your formulation is okay (due to something called "functional dependencies").
You should not use eft join table's column ins where condition this work as inner join
You should move the where condition for a left joined table in the corresponding ON clause
SELECT accounts.id, accounts.vorname, accounts.nachname, COUNT(DISTINCT projektkurs_einzel_zeiten.date, projektkurs_einzel_zeiten.shift) AS T
FROM accounts
LEFT JOIN projektkurse on accounts.id = projektkurse.creator_id
AND projektkurse.projektwochen_id = 8
LEFT JOIN projektkurs_einzel on projektkurse.id = projektkurs_einzel.projektkurs_id
LEFT JOIN projektkurs_einzel_zeiten ON projektkurs_einzel.id = projektkurs_einzel_zeiten.projektkurs_einzel_id
GROUP BY accounts.id

MySQL - join two tables, group and count

I have two tables:
reviewStatusPhases - id|name
and
userPhase - id|reviewStatusPhase_id|user_id|created_at|updated_at
The reviewStatusPhases table have records inserted (Active, Inactive, On Pause, Terminated...), and userPhase is empty.
The tables are connected via
userPhase.reviewStatusPhase_id = reviewStatusPhases.id
one to one.
Is it possible that in one query I get all reviewStatusPhases, and cound how many users are in each phase? In this case I will get something like this:
Active (0 Users)
Inactive (0 Users)
On Pause (0 Users)
Terminated (0 Users)
I'm making some assumptions here (e.g. INNER JOIN versus LEFT JOIN in the join, and DISTINCT in the count), but it sounds like you just want
SELECT reviewStatusPhases.name, COUNT(DISTINCT userPhase.user_id)
FROM userPhase INNER JOIN reviewStatusPhases
ON userPhase.reviewStatusPhase_id = reviewStatusPhases.id
GROUP BY reviewStatusPhases.name
Query will be as follows:
SELECT r.name as `name`, count(u.id) as `count` FROM reviewStatusPhases r LEFT OUTER JOIN userPhase u ON r.id = u.reviewStatusPhase_id GROUP BY r.name
left outer join with reviewStatusPhases on left to show all names.
group by names of reviewStatusPhases.
display reviewStatusPhases name and count of user id's (to neglect null values)
Use LEFT JOIN as follows:
SELECT COUNT(m.UserId) FROM Table1 m
LEFT JOIN Table2 k ON k.StatusId = m.StatusId
WHERE k.Status = 'Inactive'
You can easily use the Status column to track the users and their activities. In your case, ReviewStatus.
I hope the following will be helpful
SELECT RPS.Name, COUNT(UP.user_id)
FROM reviewStatusPhases RPS
LEFT OUTER JOIN userphases UP ON RPS.id = UP.reviewStatusPhase_id
GROUP BY RPS.Name
ORDER BY RPS.Name
SELECT
DISTINCT s.s_level AS 'Level',
COUNT(DISTINCT s.s_id) AS Schools,
COUNT(DISTINCT st.st_id) AS Teachers
FROM schools AS s
JOIN school_teachers AS st ON st.st_school_idFk = s.s_id AND st.st_status = 1
WHERE s.s_status = 1
GROUP BY s.s_level

SQL: INNER JOIN SELECT makes the whole query return nothing

There are 3 tables, named as account_has_account1, account_has_photos, photos_has_message_photos where account_has_account1 have columns account_id, account_id1, status, type_id and this table takes care on storing accounts following to another account
account_has_photos stores information about all the photos one account has uploaded, it's columns are photos_id, account_id, type_id, this also stores likes according to type_id
photos_has_message_photos stores all messages posted to a photo, its a relational table from photos and message_photos
i need to fetch a count of all likes from account_has_photos where type_id = 1 which points to like from table type
i have done this SQL:
SELECT account_has_photos.photos_id as id, "photos" as type, account_has_photos.update_at, account_has_photos.account_id, posts.total as total_messages, likes.total as total_likes
FROM account_has_account1
INNER JOIN account_has_photos
ON (account_has_photos.account_id = account_has_account1.account_id1 AND account_has_photos.type_id = 17)
INNER JOIN (
SELECT photos_has_message_photos.photos_id, count(*) as total
FROM photos_has_message_photos
GROUP BY photos_has_message_photos.photos_id
) posts
ON(posts.photos_id = account_has_photos.photos_id)
INNER JOIN (
SELECT account_has_photos.photos_id, COUNT(account_has_photos.photos_id) as total
FROM account_has_photos
WHERE account_has_photos.type_id = 1
) likes
ON (likes.photos_id = account_has_photos.photos_id)
WHERE account_has_account1.account_id = 7 AND account_has_account1.`status` = "Active"
the first INNER JOIN account_has_account1 is for showing all accounts that one account is following, The second INNER JOIN photos_has_message_photos is only for getting the count of all posted messages from a account's photos.
At this point everything goes fine, but when i insert the third INNER JOIN account_has_photos again, the query result is now 0, the purpose of this third JOIN is for getting a count of likes a photo has which is stored in account_has_photos where type_id = 1
The rest is for setting the general conditions for the search.
Again the problem only happens in this query
INNER JOIN (
SELECT account_has_photos.photos_id, COUNT(account_has_photos.photos_id) as total
FROM account_has_photos
) likes
ON (likes.photos_id = account_has_photos.photos_id)
it could be that no likes are found on any photo, i have made the test for searching it separately and as i said, there is no like made on any photo, i didn't add any record because i want it to say 0 on count as it is going to be shown alot
here is much different way to write your query that should yeild the same results.
SELECT
account_has_photos.photos_id as id
,"photos" as type
,account_has_photos.update_at
,account_has_photos.account_id
,COUNT(photos_has_messages.photos_id) as total_messages
,COUNT(DISTINCT likes.photos_id) as total_likes
FROM
account_has_account1
INNER JOIN account_has_photos
ON account_has_photos.account_id = account_has_account1.account_id1
AND account_has_photos.type_id = 17
LEFT JOIN photos_has_message_photos
ON photos_has_message_photos.photos_id = account_has_photos.photos_id
LEFT JOIN account_has_photos likes
ON likes.photo_id = account_has_photos.photo_id
AND likes.type_id = 1
WHERE account_has_account1.account_id = 7 AND account_has_account1.`status` = "Active"
GROUP BY
account_has_photos.photos_id
,"photos"
,account_has_photos.update_at
,account_has_photos.account_id
I would recommend changing:
,COUNT(photos_has_messages.photos_id) as total_messages
to
,COUNT(DISTINCT photos_has_messages.WhateverTablesUniqueIdIs) as total_messages
Also this line
,COUNT(DISTINCT likes.photos_id) as total_likes
will always give you 1. so if likes does repeat photo_id then you also want to count whatever that account_has_photos unique identifier is....
Your last subquery is missing a GROUP BY. Try this:
INNER JOIN (
SELECT account_has_photos.photos_id, COUNT(account_has_photos.photos_id) as total
FROM account_has_photos
GROUP BY account_has_photos.photos_id
) likes
ON likes.photos_id = account_has_photos.photos_id
You may also want to replace the INNER JOINs with LEFT OUTER JOIN if you want rows with no likes.

Find unique values that do not exist in multiple columns and tables

A misconfigured manual import imported our entire AD into our help desk user database, creating a bunch of extraneous/duplicate accounts. Of course, no backup to restore from.
To facilitate the cleanup, I want to run a query that will find users not currently linked to any current or archived tickets. I have three tables, USER, HD_TICKET, and HD_ARCHIVE_TICKET. I want to compare the ID field in USER to the OWNER_ID and SUBMITTER_ID fields in the other two tables, returning the only the values in USER.ID that do not exist in any of the other four columns.
How can this be accomplished?
Do a left join for each relationship where the right table id is null:
select user.*
from user
left join hd_ticket on user.id = hd_ticket.owner_id
left join hd_ticket as hd_ticket2 on user.id = hd_ticket2.submitter_id
left join hd_archive_ticket on user.id = hd_archive_ticket.owner_id
left join hd_archive_ticket as hd_archive_ticket2 on user.id = hd_archive_ticket2.submitter_id
where hd_ticket.owner_id is null
and hd_ticket2.submitter_id is null
and hd_archive_ticket.owner_id is null
and hd_archive_ticket2.submitter_id is null
How about something like:
SELECT id
FROM user
WHERE id NOT IN
(
SELECT owner_id
FROM hd_ticket
UNION ALL
SELECT submitter_id
FROM hd_ticket
UNION ALL
SELECT owner_id
FROM hd_archive_ticket
UNION ALL
SELECT submitter_id
FROM hd_archive_ticket
)
If I understood you situation I would do this:
SELECT a.id FROM user a, hd_ticket b, hd_archive_ticket c WHERE a.id != b.id AND a.id != c.id
You would want to try something like below. Inner query where I am doing Inner join with other 2 tables, will return only those user id which exist in all 3 tables. Then in your outer query I am just filtering out those ID's returned by inner query; since your goal is to get only those USER ID which is not present in other tables.
select ID
FROM USER
WHERE ID NOT IN
(
select u.ID
from user u
inner join HD_TICKET h on u.ID = h.OWNER_ID
inner join HD_ARCHIVE_TICKET ha on u.ID = ha.SUBMITTER_ID
)

retrieve all record from one table and particular record from other

tbl_cmp
usr_id | cmp_name | usr_fname |cmp_addr
tbl_usr
cmp_usr_id |cmp_id | cmp_usr_fname | cnt_status
I am trying query retrieve data from both at a time for ::
retrieve all data from tbl_cmp
but
retrieve only field from tbl_usr that cnt_status=1
I tried this
SELECT *
FROM tbl_cmp
JOIN tbl_usr ON tbl_usr.cmp_id = tbl_cmp.usr_id
WHERE tbl_usr.cnt_status =1
but it shows only record that has cnt_status=1 not all record from first table
Try this
SELECT * FROM tbl_users LEFT JOIN tbl_cmp_user
ON tbl_cmp_user.cmp_id = tbl_users.usr_id
AND tbl_cmp_user.cnt_status =1
Use LEFT OUTER JOIN
SELECT *
FROM tbl_users
Left outer JOIN tbl_cmp_user
ON tbl_cmp_user.cmp_id = tbl_users.usr_id
and tbl_cmp_user.cnt_status =1
See Examples of left outer join here
What you really want is a cross join, because you do not have a join condition between the tables:
select c.*, u.*
from tbl_cmp c cross join
(select *
from tbl_users u
where u.cnt_status = 1
) u
You can express this without the subquery as:
select c.*, u.*
from tbl_cmp c cross join
tbl_users u
where u.cnt_status = 1
I prefer the subquery version, because it makes the condition on tbl_users quite apparent. In a more complicated query, the WHERE clause could be quite separated from where the table appears in the FROM clause.