PostgreSQL удаление дубликатов по GROUP BY - PullRequest
2 голосов
/ 25 апреля 2020

Я хотел бы напечатать последнее сообщение человека, но на человека должно быть напечатано только его последнее сообщение. Я использую PostgreSQL 10.

+-----------+----------+--------------+
| name      |   body   |  created_at  |
+-----------+----------+--------------+
| Maria     | Test3    |  2017-07-07  |
| Paul      | Test5    |  2017-06-01  |
+-----------+----------+--------------+

Я пробовал это с помощью следующего запроса SQL, это дает мне именно это, но, к сожалению, люди удвоены в этом.

SELECT * FROM messages 
WHERE receive = 't'
GROUP BY name
ORDER BY MAX(created_at) DESC
+-----------+----------+--------------+
| name      |   body   |  created_at  |
+-----------+----------+--------------+
| Maria     | Test1    |  2016-06-01  |
| Maria     | Test2    |  2016-11-01  |
| Maria     | Test3    |  2017-07-07  |
| Paul      | Test4    |  2017-01-01  |
| Paul      | Test5    |  2017-06-01  |
+-----------+----------+--------------+

Я пытался удалить дубликаты с помощью DISTINCT, но, к сожалению, я получаю это сообщение об ошибке:

SELECT DISTINCT ON (name) * FROM messages 
WHERE receive = 't'
GROUP BY name
ORDER BY MAX(created_at) DESC
ERROR: SELECT DISTINCT ON expressions must match initial ORDER BY expressions LINE 1: SELECT DISTINCT ON (name) * FROM messages ^ : SELECT DISTINCT ON (name) * FROM messages WHERE receive = 't' GROUP BY name ORDER BY MAX(created_at) DESC

У вас есть идеи, как я могу решить эту проблему?

Ответы [ 3 ]

2 голосов
/ 25 апреля 2020

Используйте DISTINCT ON, но с правом ORDER BY:

SELECT DISTINCT ON (name) m.*
FROM messages m
WHERE receive = 't'
ORDER BY name, created_at DESC;

Как правило, вы не используете DISTINCT ON с GROUP BY. Используется с ORDER BY. Он работает так, что выбирает первую строку для каждого name на основе предложения ORDER BY.

Вы не должны думать о том, что вы делаете, как об агрегации. Вы хотите фильтровать на основе created_at. Во многих базах данных вы могли бы express использовать коррелированный подзапрос:

select m.*
from messages m
where m.created_at = (select max(m2.created_at)
                      from messages m2
                      where m2.name = m.name and m2.receive = 't'
                     ) and
      m.receive = 't';   -- this condition is probably not needed
2 голосов
/ 25 апреля 2020

Вы бы использовали DISTINCT ON следующим образом:

SELECT DISTINCT ON (name) * 
FROM messages 
WHERE receive = 't'
ORDER BY name, created_at DESC

То есть:

  • нет GROUP BY предложение необходимо

  • столбец (столбцы), перечисленные в DISTINCT ON(...), должны появляться сначала в ORDER BY пункте

  • ..., а затем столбец, который следует использовать для разбиения group (здесь created_at)

Обратите внимание, что результаты запроса distinct on всегда сортируются по столбцам в предложении (потому что именно этот тип используется для идентификации какие строки должны быть сохранены).

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

SELECT *
FROM (
    SELECT m.*, ROW_NUMBER() OVER(PARTITION BY name ORDER BY created_at DESC) rn
    FROM messages m
    WHERE receive = 't'
) t
WHERE rn = 1
ORDER BY created_at DESC
1 голос
/ 25 апреля 2020
SELECT * 
FROM messages 
WHERE receive = 't' and not exists (
    select 1
    from messages m
    where m.receive = message.receive and messages.name = m.name and m.created_at > messages.created_at
)
ORDER BY created_at DESC

Приведенный выше запрос находит сообщения, которые удовлетворяют следующим критериям:

  • receive is 't'
  • не существует другого сообщения, которое
    • имеет то же значение для приема
    • с тем же именем
    • и новее

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

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