Show number of users in groups, even empty groups - mysql

I am trying to pull out the information about each user group, while counting the number of users in each.
The counting works, however if a group does not contain any users, it does not retrieve the information - it simply gets ignored.
This is my query:
SELECT
g.*,
count(u.username) as total,
u.usergroup
FROM
usergroups as g,
users as u
WHERE
u.usergroup = g.g_id
GROUP BY
g.group_name
This is a part of my "users" and "user groups" tables:
Sample usergroups table structure
+--------------------------+---------------+
| Field | Type |
+--------------------------+---------------+
| g_id | int(10) |<- the user group's ID.
| group_name | varchar(255) |<- the name of the user group
+--------------------------+---------------+
Sample users table structure
+--------------------------+---------------+
| Field | Type |
+--------------------------+---------------+
| id | int(10) |<- the user ID
| username | varchar(255) |<- the username
| usergroup | int(10) |<- ID of the user group the user is in
+--------------------------+---------------+
I have no idea what I have missed - but then again, I am very new to more complex queries (if you can call it that).
Any help would be very appreciated!

You want a left join, and you're doing an inner join:
SELECT
g.*,
count(u.username) as total,
u.usergroup
FROM
usergroups as g
left join users as u on
g.u_id = u.usergroup
GROUP BY
g.u_id,
g.group_name
Now that I have your attention, I'd like to take a minute to discuss what join conditions are. See, when you're first learning MySQL, they teach you the syntax you use. And hey, it works great, for certain sets of problems. Unfortunately, it causes you to equate "join condition" with "where predicate," and that's a problem.
In this case, we're saying, "Hey, MySQL, grab me all the stuff out of the usergroups table, and try to find a match to the users table where the u_id column from usergroups equals the usergroup column from users. If you can't find anything, that's okay, bring the usergroup back anyway, and just null out the columns that users brings to the table."
Previously, you were saying, "Hey, MySQL, grab me all the stuff out of the usergroups and the users table, but only where the u_id column from usergroups matches the usergroup column from users."
If you had done it with inner join syntax, like so:
SELECT
g.*,
count(u.username) as total,
u.usergroup
FROM
usergroups as g
inner join users as u on
g.u_id = u.usergroup
GROUP BY
g.u_id,
g.group_name
You would have been saying, "Hey, MySQL, grab me everything from the usergroups table, and then go find everything in the users table where u_id equals usergroup. If you don't find a match, I don't want to see that usergroup show up in the results.
So, to be clear, the where predicates filter the result set, whereas the join conditions map one table to another.

You need to use a LEFT JOIN here instead of your implicit INNER JOIN:
SELECT
g.*,
count(u.username) as total,
u.usergroup
FROM
usergroups as g
LEFT JOIN users as u ON g.g_id = u.usergroup
GROUP BY
g.group_name

Related

Three way join with linking table

This is what I'd think is a fairly common pattern, but I'm just struggling with the appropiate query for it.
User Table
id
Member table
id
name
User Member link table
user_id
member_id
A user may exist in the user's table, but not have a row in the User Member link table.
I want to select all rows of the User table, and where a user has a link to a member in the user member link table to show the columns linked to them from the member table.
Here's what I've got, but it only gets the rows from the User table that are linked:
SELECT user.id, user.username, member.id, member.name
FROM users
LEFT JOIN user_member ON user.id = user_member.user_id
JOIN member ON user_member.member_id = member.id;
I should get something like this:
user.id user.username member.id member.name
1 bob null null
2 alice 10 Alice
3 jane 11 Jane
4 joe null null
Any suggestions?
I assume a member_id in the user_member table always has a corresponding row in the member table. First, join member and user_member. Second, join user.
SELECT user.id, user.username, member.id, member.name
FROM users
LEFT JOIN
(user_member INNER JOIN member ON user_member.member_id = member.id)
ON user.id = user_member.user_id;
Try using a CROSS JOIN
SELECT user.id, user.username, member.id, member.name
FROM users u
CROSS JOIN member m
LEFT JOIN user_member um
ON u.id= um.user_id
AND m.id= um.member_id

How to get data from database with condition on another table where something exist or not

I have a table for users like this
id | name | password | email
1 saeid ***** asd#asd.com
I have another table called appointments
id | created_by | due_date | notification_send
1 1 ***** 0
I want to get all users from users table where they have at least created one appointment in the appointments table (denoted by created_by field in the appointments table).
I have tried the code below but it fails:
SELECT * FROM users LEFT JOIN appointments a ON persons.id = a.created_by
But obviously it does not work.
One way is to use the exists predicate:
SELECT * FROM users u
WHERE EXISTS (SELECT 1 FROM appointments a WHERE a.created_by = u.id)
Alternatively you could use an inner join, but the exists query corresponds better to your question in my opinion (that is if you only need data from the users table).
The left join says to get all rows from users regardless if they have matching rows in appointments which is not what you want.
You are searching for a match between the table and so I would suggest doing a INNER JOIN rather like below
SELECT * FROM users u
JOIN appointments a ON u.id = a.created_by
Also check your ON clause once I think either this is a typo or a big mistake. You are selecting from users table then why persons.id??
ON persons.id = a.created_by
Try something like this:
http://sqlfiddle.com/#!9/5eba3/2
select * from users c where (select count(*) from appointments where created_by = c.id) > 0;

Mysql select between two table without limiting if record appear on the joined table

I have been trying to figure out how to select data related to one id between to tables without limit it to the joined table. I tried using UNION, Inner join, JOIN, but it limit me to show records that are only in both tables. By example:
Table 1 (users)
id | name | register
1 | John | 2014-03-01
2 | Kate | 2014-03-02
etc..
Table 2 (birthdays by example)
id | user | birthday
1 | 1 | 1989-09-09
Note that kate dont have a record on the birthdays table, if i do:
SELECT U.id, name, register, B.birthday FROM users as U INNER JOIN birthday as B ON B.user = U.id
it will only shows JOHN data, i would like to select all my users and if the record do not exist on the joined table, still be able to select all my users, sort of:
id | name | register | birthday
1 | John | 2014-03-01 | 1989-09-09
2 | kate | 2014-03-02 | null or ''
3
4
etc.
Sorry if its a stupid question but i dont find the light on this one. I would appreciate the help.
Regards
You need a LEFT OUTER JOIN instead of the plain JOIN (also known as INNER JOIN), like this:
SELECT U.id, name, register, B.birthday
FROM users as U
LEFT JOIN birthday as B
ON B.user = U.id
A LEFT JOIN between users and birthday tables will contain all records of the "left" table (users), even if the join-condition does not find any matching record in the "right" table (birthday).
This excellent article on The Code Project will help you a lot: Visual Representation of SQL Joins.
Summary of all JOIN types:
Note: Mysql does not support FULL OUTER JOIN but it can be emulated. Useful articles:
https://stackoverflow.com/a/4796911
http://www.sql-tutorial.ru/en/book_full_join_and_mysql.html
http://www.xaprb.com/blog/2006/05/26/how-to-write-full-outer-join-in-mysql/
Use left outer join instead of inner join..
SELECT U.id, name, register, B.birthday
FROM users as U left join birthday as B ON B.user = U.id

Mysql join query

I have table users and another table premium_users in which I hold the userid and the date when he bought premium membership.
How can I use mysql join , so that in a single query I can select all the columns from the table users and also know for each premium user the date he joined on.
USERS:
ID USERNAME
1 JOHN
2 BILL
3 JOE
4 KENNY
PREMIUM USERS:
ID USERID DATE
1 2 20/05/2010
2 4 21/06/2011
And the final table (the one that will be returned my the query) should look like this:
ID USERNAME DATE
1 JOHN
2 BILL 20/05/2010
3 JOE
4 KENNY 21/06/2011
Is it ok for some rows to have the DATE value empty?
How can I check if that value is empty? $row['date']=='' ?
EDIT:
This was only an example, but the users table has much more columns, how can I select all from users and only date from premium_users without writing all the columns?
select u.*, pu.DATE
from USERS u LEFT OUTER JOIN PREMIUM_USERS pu on
u.ID = pu.USERID
You can check if a row is empty with:
if (!$row['DATE'])
{
...
}
select USERS.ID, USERS.USERNAME, PREMIUM_USERS.DATE
from USERS
join PREMIUM_USERS on USERS.ID = PREMIUM_USERS.ID
order by USERS.ID
This is mssql syntax, but it should be pretty similar...
select *
from users u
left join premiumUsers p
on u.id = p.id
order by u.id asc
SELECT A.*, B.DATE
FROM USERS A
LEFT JOIN PREMIUIM_USERS B on A.ID=B.USERID
EDITED
It might be easier to have it all in one table. You can have nullable fields for isPremium(t/f) and premiumDate. you actually dont even need the isPremium field. just premiumDate if its null they are not premium and if it has value they are premium user and you have the date they joined.

Counting records from table that appear is one but not other: MYSQL

I have a two simple tables
users
+----+--------+-----------+
| id | gender | birthdate |
+----+--------+-----------+
userpreference
+----+------------------+-----------------+
| id | preference value | preference type |
+----+------------------+-----------------+
Question:
I want to query all people who have not listed a specific preference value such as 'shopping'.This includes all people who have not listed any preference types as well so that column could be null, however since userpreference's column 'id' references users as a foreign key, I also want to include in my count all people who don't show up in the second table (user preference)?
Total # of people who do not have preference value 'shopping' as their preference value:
Here is what i have tried:
SELECT
(
SELECT COUNT(DISTINCT userpreference.id) FROM userpreference
WHERE preferencevalue != 'shopping')
+
(
SELECT COUNT(users.id)
FROM users
WHERE users.id NOT IN
(SELECT userpreference.Id
FROM userpreference )
)
AS'Total'
Select Count(*)
From Users
Where Not Exists (
Select 1
From UserPreference As UP1
Where UP1.id = Users.id
And UP1.PreferenceValue = 'Shopping'
)
Try a RIGHT JOIN, that will include all people who dont show up in the second table
SELECT *
FROM Users
RIGHT JOIN Userpreference ON ( users.userID = Users.userID)
WHERE preference_value = 'shopping'
Try this:
SELECT COUNT(DISTINT U.id) FROM users U NATURAL LEFT JOIN userpreference UP
WHERE UP.preferencevalue IS NULL OR UP.preferenceValue != 'shopping';
The LEFT JOIN should bring in all the users records whether or not they have a UP record.