Search code examples
mysqlsqlsql-limit

How to make a dynamic limit in MySQL?


I have a table like this:

// notifications
+----+--------------+------+---------+------------+
| id |      event   | seen | id_user | time_stamp |
+----+--------------+------+---------+------------+
| 1  | vote         | 1    | 123     | 1464174617 |
| 2  | comment      | 1    | 456     | 1464174664 |
| 3  | vote         | 1    | 123     | 1464174725 |
| 4  | answer       | 1    | 123     | 1464174813 |
| 5  | comment      | NULL | 456     | 1464174928 |
| 6  | comment      | 1    | 123     | 1464175114 |
| 7  | vote         | NULL | 456     | 1464175317 |
| 8  | answer       | NULL | 123     | 1464175279 |
| 9  | vote         | NULL | 123     | 1464176618 |
+----+--------------+------+---------+------------+ 

I'm trying to select at least 15 rows for specific user. Just there is two conditions:

  1. Always all unread rows (seen = NULL) should be matched, even if they are more than 15 rows.

  2. If the number of unread rows is more than 15, then it also should select 2 read rows (seen = 1).


Examples: read is the number of read rows and unread is the number of unread rows in notifications table.

 read | unread |          output should be           
------|--------|-------------------------------------
 3    | 8      | 11 rows                             
 12   | 5      | 15 rows (5 unread, 10 read)         
 20   | 30     | 32 rows (30 unread, 2 read)         
 10   | 0      | 10 rows (0 unread, 10 read)         
 10   | 1      | 11 rows (1 unread, 10 read)         
 10   | 6      | 15 rows (6 unread, 9 read)          
 100  | 3      | 15 rows (3 unread, 12 read)         
 3    | 100    | 102 rows (100 unread, 2 read)       

Here is my current query, it doesn't support second condition.

SELECT id, event, seen, time_stamp 
 FROM notifications n
 WHERE id_user = :id AND seen IS NULL
) UNION 
(SELECT id, event, seen, time_stamp 
 FROM notifications n
 WHERE id_user = :id 
 ORDER BY (seen IS NULL) desc, time_stamp desc
 LIMIT 15
)
ORDER BY (seen IS NULL) desc, time_stamp desc;

Solution

  • I find a solution. To add second condition (selecting two read rows if there is more than 15 unread rows), I have to use one more UNION. Something like this:

    (SELECT id, event, seen, time_stamp 
     FROM notifications n
     WHERE id_user = :id AND seen IS NULL
    )UNION
    (SELECT id, event, seen, time_stamp
     FROM notification n
     WHERE id_user = :id AND seen IS NOT NULL
     LIMIT 2
    )UNION 
    (SELECT id, event, seen, time_stamp 
     FROM notifications n
     WHERE id_user = :id 
     ORDER BY (seen IS NULL) desc, time_stamp desc
     LIMIT 15
    )
    ORDER BY (seen IS NULL) desc, time_stamp desc;
    

    The first subquery gets all unseen rows. The second gets two seen rows. The third gets fifteen rows. The UNION removes duplicates, but no other limit is applied.