user0000001
user0000001

Reputation: 2233

MySQL: Conditional statement within query

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

Answers (2)

GarethD
GarethD

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

Denis de Bernardy
Denis de Bernardy

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

Related Questions