Reputation: 2233
I'll try to be straight to the point of what I am trying to accomplish here...
I have a query that works but I have recently discovered an issue with it. Now, I could simply fix this with PHP but I know it can be done somehow in MySQL... I just don't know how. So this is what is going on:
I have a USERS table and a SERVICES table. A user can have multiple SERVICES (each service being completely independent from one another) and every service has a field called "status". Status defines the current state the service is in: active / inactive
The query is called when you go to the Manage Users page, where every user is split into the category it belongs. For this example, it will be: Active Users | Deactivated Users | Other Users (users without any service plan at all).
So my deactivated users query comes down to this:
SELECT DISTINCT u.* FROM users u LEFT JOIN services s ON s.assignedto=u.id WHERE s.status!=1
The statement works great, however, if the customer has 2 services plans where one is activated and one is deactivated, then he will appear in the deactivated list as well as the activated list. The statement needs a condition where it will exclude the user from the query if they ALSO have an activated service plan. Right now, I have a few users that are falling into the deactivated users that should not be there.
Anyway, thank you in advance!
Upvotes: 0
Views: 128
Reputation: 69749
You can exclude users with active services using NOT EXISTS
:
SELECT u.*
FROM users u
WHERE NOT EXISTS
( SELECT 1
FROM Services s
WHERE s.assignedto=u.id
AND s.status = 1
);
To get the counts you are after I think you need something like this:
SELECT COUNT(CASE WHEN s.ActiveServices > 0 THEN 1 END) AS ActiveUsers,
COUNT(CASE WHEN s.ActiveServices = 0 THEN 1 END) AS DeactivatedUsers,
COUNT(CASE WHEN s.assignedto IS NULL THEN 1 END) AS OtherUsers
FROM users u
LEFT JOIN
( SELECT s.assignedto,
COUNT(CASE WHEN s.status = 1 THEN 1 END) AS ActiveServices
FROM Services s
GROUP BY s.assignedto
) s
ON s.assignedto = u.ID
I have just remembered that NOT EXISTS is not as efficient as LEFT JOIN/IS NULL in MySQL, Denis has also pointed out that NOT EXISTS does not scale well. The LEFT JOIN Approach would be:
SELECT u.*
FROM Users u
LEFT JOIN Services s
ON s.assignedto = u.id
AND s.status = 1
WHERE s.assignedto IS NULL;
Upvotes: 0
Reputation: 78413
You could rewrite your query with not exits()
as @GarethD suggested.
Another approach to have in mind if it doesn't scale well could be a group by clause, e.g.:
SELECT u.*
FROM users u
LEFT JOIN services s ON s.assignedto=u.id
GROUP BY u.id
HAVING max(s.status) <> 1
The best option, though, would be an extra field. If you add a status
field to users
, you could index and query that directly. You could maintain it using a trigger but, speaking personally, this is a rare case where I've found that maintaining it in the app is a better approach. (Basically, you never know when you need to quickly mark a user as inactive without messing around with other DB tables.)
Upvotes: 2