简体   繁体   中英

Joining 3 Tables based on specific conditions

I have the following 3 tables:

  • users: [id, name, admin ...]
  • events: [id, user_id, type ...]
  • messages: [id, user_id, ...]

I want to construct a query that does the following:

-> Select all users from the table users who have not scheduled an event of the type "collection"

-> And who have less than 3 messages of the type "collection_reminder"

-> And who are not admin

I've managed to figure out the first part of this query, but it all goes a bit pear shaped when I try to add the 3 table, do the count, etc.

Here is a query that might get the job done. Each of the requirement is represented as a condition in the WHERE clause, using correlated subqueries when needed:

SELECT u.*
FROM users u
WHERE 
    NOT EXISTS (
        SELECT 1 
        FROM events e 
        WHERE e.user_id = u.id AND e.type = 'collection'
    )
    AND (
        SELECT COUNT(*) 
        FROM messages m 
        WHERE m.userid = u.id AND m.type = 'collection_reminder'
    ) <= 3
    AND u.admin IS NULL

Ill try this on the top of the head so expect some synthax issues, but the idea is the following.

You can filter out who have no events schedule using a left join. On a left join the elements on the second part of the query will show up as null.

select * from users u
left join events e on e.user_id = u.id
where e.user_id is null

Now, i dont think this is the most performant way, but a simple way to search for everyone that has 3 or less messages:

select * from users u
left join events e on e.user_id = u.id
where u.id in (
   select COUNT(*) from messages m where m.user_id = u.id HAVING COUNT(*)>3;
)
and e.user_id is null

Then filtering who is not admin is the easiest :D

select * from users u
left join events e on e.user_id = u.id
where u.id in (
   select COUNT(*) from messages m where m.user_id = u.id HAVING COUNT(*)>3;
)
and e.user_id is null
and u.admin = false

Hope it helps.

This is pretty much a direct translation of your requirements, in the order you listed them:

SELECT u.*
FROM users AS u
WHERE u.user_id NOT IN (SELECT user_id FROM events WHERE event_type = 'Collection')
   AND u.user_id IN (
      SELECT user_id 
      FROM messages 
      WHERE msg_type = 'Collection Reminder' 
      GROUP BY user_id 
      HAVING COUNT(*) < 3
   )
   AND u.admin = 0

or alternatively, this can be accomplished completely with joins:

SELECT u.*
FROM users AS u
LEFT JOIN events AS e ON u.user_id = e.user_id AND e.event_type = 'Collection'
LEFT JOIN messages AS m ON u.user_id = m.user_id AND m.msg_type = 'Collection Reminder'
WHERE u.admin = 0
   AND e.event_id IS NULL        -- No event of type collection
GROUP BY u.user_id -- Note: you should group on all selected fields, and 
                   -- some configuration of MySQL will require you do so.
HAVING COUNT(DISTINCT m.message_id) < 3   -- Less than 3 collection reminder messages 
             -- distinct is optional, but 
             -- if you were to remove the "no event" condition, 
             -- multiple events could multiply the message count.
;

This query uses joins to link the 3 tables, filters the result using the where clause, and using Group by, having limiting the result to only those who satisfy the less than count condition..

SELECT    a.id, 
      SUM(CASE WHEN b.type = 'collection' THEN 1 ELSE 0 END),
      SUM(CASE WHEN c.type = 'collection_reminder' THEN 1 ELSE 0 END
FROM      users a 
left join   events b on (b.user_id = a.id)
left join   messages c on (c.user_id = a.id)
WHERE     a.admin = false
GROUP BY  a.id
HAVING  SUM(CASE WHEN b.type = 'collection' THEN 1 ELSE 0 END) = 0 
    AND SUM(CASE WHEN c.type = 'collection_reminder' THEN 1 ELSE 0 END) < 3

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM