Как получить X уникальных записей в MySQL - PullRequest
0 голосов
/ 03 июля 2019

Я хочу получить 10 записей из таблицы с максимум 2 одинаковыми пользовательскими записями.

В таблице MySQL есть сообщения от пользователей. Я хочу получать уникальные пользовательские сообщения, что легко, если бы я просто хотел одно уникальное сообщение, я мог бы использовать distinct, чтобы получить его. Но я хочу 2 уникальных пользовательских сообщения.

В таблице ниже приведены исходные данные.

--------------------------------------------------------------
| id | user_id | message                                     | 
--------------------------------------------------------------
| 1  | 111     | this is message A from user 1               |
--------------------------------------------------------------
| 2  | 111     | this is message B from user 1               |
--------------------------------------------------------------
| 3  | 111     | this is message C from user 1               |
--------------------------------------------------------------
| 4  | 222     | this is message A from user 2               |
--------------------------------------------------------------
| 5  | 222     | this is message B from user 2               |
--------------------------------------------------------------
| 6  | 222     | this is message C from user 2               |
--------------------------------------------------------------
| 7  | 333     | this is message A from user 3               |
--------------------------------------------------------------
| 8  | 333     | this is message B from user 3               |
--------------------------------------------------------------
| 9  | 333     | this is message C from user 3               |
--------------------------------------------------------------
... so on ...

Теперь мне нужен запрос, который может принести 2 результата для каждого пользователя, как показано ниже, с максимум 10 записями:

--------------------------------------------------------------
| id | user_id | message                                     | 
--------------------------------------------------------------
| 1  | 111     | this is message A from user 1               |
--------------------------------------------------------------
| 2  | 111     | this is message B from user 1               |
--------------------------------------------------------------
| 4  | 222     | this is message A from user 2               |
--------------------------------------------------------------
| 5  | 222     | this is message B from user 2               |
--------------------------------------------------------------
| 7  | 333     | this is message A from user 3               |
--------------------------------------------------------------
| 8  | 333     | this is message B from user 3               |
--------------------------------------------------------------
... so on ...

EDIT:

Использование запроса, подобного этому, для получения записей, сгруппированных по user_id, может принести только отдельные записи:

select max(id) as id, user_id, max(message) as message from user_messages group by user_id
--------------------------------------------------------------
| id | user_id | message                                     | 
--------------------------------------------------------------
| 2  | 111     | this is message B from user 1               |
--------------------------------------------------------------
| 5  | 222     | this is message B from user 2               |
--------------------------------------------------------------
| 8  | 333     | this is message B from user 3               |
--------------------------------------------------------------
... so on ...

Но я не могу найти способ получить 2 набора записей для каждого пользователя.

EDIT2:

Используя обходной путь языка программирования, мы можем сделать это примерно так:

- we need 10 records total
- we need 2 records max per user
- we can run a loop => 10 / 2 = 5 times
- each time we get a distinct user record
- each next time we append `id not in` to the query to avoid already loaded records

Что-то вроде:

$data = [];
$ids = [0]; // keep a value in it so that first query does not give error
for ($i=0; $i<5; $i++) {
  $res = mysql_query("select max(id) as id, user_id from user_messages where id not in (".implode(',', $ids).") group by user_id");
  while ( ($row = mysql_fetch_assoc($res)) ) {
    $ids[] = $row['id'];
    $data[] = $row;
  }
}

Но это не лучшее решение, так как оно включает в себя код, а не чистый sql.

1 Ответ

0 голосов
/ 03 июля 2019

В MySQL 8+ вы бы использовали row_number():

select um.*
from (select um.*,
             row_number() over (partition by user_id order by id) as seqnum
      from user_messages um
     ) um
where seqnum <= 2;

В более ранних версиях вы можете использовать коррелированный подзапрос:

select um.*
from user_messages um
where um.id <= any (select um2.id
                    from user_messages um2
                    where um2.user_id = um.user_id
                    order by um2.id
                    limit 2
                   );

Кстати, any обрабатывает случай, когда для пользователя меньше двух сообщений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...