I have this sql:
SELECT logins.*, users.*, invoices.number
FROM logins,
users,
invoices
WHERE logins.user_id = users.id
AND users.id = invoices.userId;
This is simplified version of my statement. The reality is more complicated.
When
users.id=invoices.userId
is false (because table invoices don't contain row with this userId) I need add to invoices.number=0.
Use a left join to return rows even if no invoices are found. For those rows, columns from the invoices table will be null. You can use coalesce to return 0 instead of null in that situation:
SELECT logins.*
, users.*
, COALESCE(invoices.number, 0) AS number
FROM logins
JOIN users
ON logins.user_id = users.id
LEFT JOIN
invoices
ON users.id = invoices.userId
use explicit join instead of comma separated join
SELECT logins.*, users.*, invoices.number
FROM logins
join users on logins.user_id=users.id
join invoices on users.id=invoices.userId;
Related
I have a table of groups with details of groups that my users can join and a separate table of group members to show which users have joined which groups. The group_members table just has a column for group_id and a column for user_id. If there is a row with group_id = 3 AND user_id = 10 then we know user 10 is a member of group 3.
I have the following mysql statement that works well to return the details of the groups including the number of members each group has depending upon a couple of conditions in the WHERE clause.
SELECT groups.*, COUNT(group_members.group_id) AS member_count
FROM groups LEFT JOIN
group_members
ON groups.group_id = group_members.group_id
WHERE groups.deleted = 0 AND
groups.trainer_id = ' .$trainer_id .'
GROUP BY groups.group_id
ORDER BY groups.group_name'
The problem is though that users can be deleted in and aren't always removed from the group_members table when this happens. This means that users can show up in the count even though they are no longer actually in the system in a third users table.
So I want to only include group members in the count if they are also present in the users table. I thought I could do this with an INNER JOIN between the group members and users tables. Something like this...
SELECT groups.*, COUNT(users.user_id) AS client_count
FROM groups LEFT JOIN
group_members
ON groups.group_id = group_members.group_id INNER JOIN
users
ON group_members.user_id = users.user_id
WHERE groups.deleted = 0 AND groups.trainer_id = ' .$trainer_id .'
GROUP BY groups.group_id
ORDER BY groups.group_name
The trouble with this is that when a group has no members the group is not showing up in the results. I guess because it is not able to join to the users table.
If anyone could explain to me how I can achieve what I am looking to do I'd be very grateful.
A LEFT JOIN to users` should really fix your problem:
SELECT g.*, COUNT(u.user_id) AS client_count
FROM groups g LEFT JOIN
group_members gm
ON g.group_id = gm.group_id LEFT JOIN
users u
ON gm.user_id = u.user_id
WHERE g.deleted = 0 AND g.trainer_id = ' .$trainer_id .'
GROUP BY g.group_id
ORDER BY g.group_name;
If a group is not being returned, then it does not meet the WHERE filtering conditions. All groups meeting those conditions should be returned if LEFT JOIN is used for both joins.
I would also strongly advise you to use parameters rather than munging query strings, when you call queries from an application language.
You can just do a LEFT JOIN instead of INNER on the users table. The COUNT function skips null entries so your last query should work.
Say I have
users.id
roles.id
usersroles.users_id, usersroles.roles_id
How can I get the following headers for say user 1 with a triple join (I think IF clause in select?):
users.id, roles.id, (tinyint)has_the_role
1,1,0
1,2,1
1,3,1
1,4,0
You will need two joins for that:
SELECT users.id, roles.id,
CASE WHEN usersroles.roles_id IS NULL THEN 0 ELSE 1 END AS has_the_role
FROM users
INNER JOIN roles
LEFT JOIN usersroles ON users.id = usersroles.users_id AND roles.id = usersroles.roles_id
First you join the users table with the roles table (no condition, so all possibilities are joined) and after that you check if there is a usersroles entry for a specific combination.
I am trying to count users that are NOT referenced in another table... Right now, I have something along the lines of this:
SELECT COUNT(DISTINCT u.id) FROM users u INNER JOIN orders o ON o.assigned!=u.id;
However, it's returning an invalid value. Any suggestions?
Thank you!
I would suggest using a LEFT JOIN between the two tables and filter the rows without a matching id in the orders table:
select count(u.id)
from users u
left join orders o
on o.assigned = u.id
where o.assigned is null
See SQL Fiddle with Demo
Use a left join and count the rows with no match:
SELECT COUNT(*)
FROM users u
LEFT JOIN orders o
ON o.assigned = u.id
WHERE o.assigned IS NULL
An alternative is to use a NOT IN check:
SELECT COUNT(*)
FROM users
WHERE id NOT IN (SELECT distinct(assigned) FROM orders)
However, in my experience the left join performs better (assuming appropriate indexes).
Simply use this query, assuming that the id is unique in users table:
select count(*) From Users as u where u.id not in (select assigned from orders)
an inner join explicitly looks for rows that match so that isn't the way to go if you are looking for non matched records
assuming that ORDERS.ASSIGNED is matched with USER.ID an outer join could return values from both and show when there aren't matches like so
select
u.id,
o.*
from users u
full outer join orders o
on o.assigned = u.id;
if you only want to know which USER.ID don't have an ORDERS record you could also INTERSECT or use NOT IN () eg
select u.id from users u where id not in (select o.assigned from orders.o);
SELECT COUNT(1) FROM users u
WHERE NOT EXISTS (SELECT * FROM orders o WHERE o.assigned=u.id);
Are you wanting a straight count (like you mentioned), or do you need values returned? This will give you the count; if you want other values, you should take one of the other approaches listed above.
I'm trying to get a list of the number of entries in the changes_cc table by each user. Not all users have made entries into it, however for some reason it's returning "1" for each user that has 0 entries. I'm assuming that it's because it's counting the entries in the JOINed table. How can I make it so that it is "0" instead?
SELECT COUNT(*) as num, users.id, realname, username
FROM changes_cc
RIGHT JOIN users
ON changes_cc.user_id = users.id
GROUP BY users.id
I think this should work -- count a specific field in the changes_cc table vs counting *:
SELECT u.id, realname, username, COUNT(c.id) as num
FROM users u
LEFT JOIN changes_cc c
ON u.user_id = c.id
GROUP BY u.id
I prefer reading a LEFT JOIN over a RIGHT JOIN, but they are both OUTER JOINs and work the same.
You should not be using COUNT(*) (counts the record including null values) because it will normally give atleast 1 since it returns all records from the right table. If you specify the column name to be counted, it will gove you the result you want because COUNT only counts for NON_NULL value.
SELECT COUNT(changes_cc.user_id) as num,
users.id,
realname,
username
FROM changes_cc
RIGHT JOIN users
ON changes_cc.user_id = users.id
GROUP BY users.id
Instead of using count(*), use count(changes_cc.user_id).
The problem is that you are counting rows (with the *) rather than counting the non-NULL values in the "right-joined" table.
The following query does what I want. It returns all the resuls in the users table and then if there is a match in the details tble, returns the relevant data
users
id|username
details
id|userid|firstname|lastname
$sql = "SELECT u.*, d.*
FROM `users` u
LEFT JOIN `details` d on
u.id = d.userid
ORDER BY $strorder";
However, when I try to join an additonal table where I want to do the same thing--return all the results of the users table and if there is a match in the third table, return the relevant data (total followers of this user)--it only returns one record.
3rd table
follow
id|followerid|followedid
$sql = "SELECT u.*, d.*, COUNT(f.id)
FROM `users` u
LEFT JOIN `details` d on
u.id = d.userid
LEFT JOIN `follow` f on
u.id = f.followedid
ORDER BY $strorder";
Can anyone see what I am doing wrong? Thanks in advance for any suggestions.
Many thanks.
Try to avoid * to select fields, it will be clearer to group your datas (even if mysql is quite permissive with groupings).
When you have an aggregate function (like COUNT, SUM), the other "non aggregated" requested fields should be in a GROUP BY clause.
Mysql don't force you to GROUP BY all the fields, but... I think it's quite a good habit to be "as ANSI as possible" (usefull when you use another DBMS)
SELECT u.id, u.username, d.firstname, d.lastname, count(*) as numberfollowers
FROM user u
LEFT JOIN details d on u.id = d.userid
LEFT JOIN follow f on u.id = f.followedid
GROUP BY u.id, u.username, d.firstname, d.lastname --or just GROUP BY u.id with Mysql
ORDER BY count(*) desc
COUNT being an aggregate function, when selected with other columns, requires you to group your results by those other columns in the select list.
You should rewrite your query with columns that you want to select from users and details and group by those columns.