PostgreSQL按组删除重复项
我想打印一个人的最后一条消息,但每人只打印他最近的消息。我使用PostgreSQL 10PostgreSQL按组删除重复项,sql,postgresql,sql-order-by,distinct,greatest-n-per-group,Sql,Postgresql,Sql Order By,Distinct,Greatest N Per Group,我想打印一个人的最后一条消息,但每人只打印他最近的消息。我使用PostgreSQL 10 +-----------+----------+--------------+ | name | body | created_at | +-----------+----------+--------------+ | Maria | Test3 | 2017-07-07 | | Paul | Test5 | 2017-06-01 | +----
+-----------+----------+--------------+
| name | body | created_at |
+-----------+----------+--------------+
| Maria | Test3 | 2017-07-07 |
| Paul | Test5 | 2017-06-01 |
+-----------+----------+--------------+
我已经在下面的SQL查询中尝试过了,这给了我确切的答案,但不幸的是,使用它的人数增加了一倍
从消息中选择*
其中receive='t'
按名称分组
最大订单(创建时)描述
我试图用一个不同的名称删除重复项,但不幸的是,我收到以下错误消息:
从消息中选择DISTINCT ON(名称)*
其中receive='t'
按名称分组
最大订单(创建时)描述
你知道我如何解决这个问题吗?你可以在上使用
DISTINCT,如下所示:
SELECT DISTINCT ON (name) *
FROM messages
WHERE receive = 't'
ORDER BY name, created_at DESC
即:
- 不需要
分组依据
子句
- 在(…)
中列出的列必须首先出现在ORDER BY
子句中
)
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
在上使用不同的,但使用右侧的按排序:
SELECT DISTINCT ON (name) m.*
FROM messages m
WHERE receive = 't'
ORDER BY name, created_at DESC;
一般情况下,您不会将上的DISTINCT ON
与分组依据一起使用。它与订购人一起使用。它的工作方式是基于orderby
子句为每个name
选择第一行
你不应该把你正在做的事情看作是聚合。您希望根据在
创建的进行筛选。在许多数据库中,可以使用相关子查询来表示:
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
上面的查询查找满足以下条件的消息:
- 接收是't'
- 不存在另一条
- 具有与接收相同的值
- 同名
- 而且是新的
假设相同的名称不能同时发送两条消息,这就足够了。另一点需要指出的是,如果值中存在一些白色字符,则名称可能看起来相似,但可能不同。因此,如果您在结果中看到两条记录具有相同的名称,但在上面的查询中创建的位置不同,然后,很可能是白人角色在捉弄你。但随后会按名称排序。当我尝试使用子查询执行此操作时,最新的条目将丢失。为什么会这样?
SELECT DISTINCT ON (name) m.*
FROM messages m
WHERE receive = 't'
ORDER BY name, created_at DESC;
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
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