SQL WHERE IN 每个 IN id 的结果有限且排序
SQL WHERE IN with limited and sorted results per IN id
我不知道怎么形容这个,所以请原谅这个标题...
我正在尝试编写一个函数,以便在提供对话 ID 数组时检索属于对话的最新消息。
我的"message"table看起来如下:
id | conversationId | body | createdDate
-------------------------------------------------------
1 | 1 | Hello | 2020-06-09 01:01
2 | 1 | How are you? | 2020-06-09 01:02
3 | 2 | Hi | 2020-06-09 01:02
4 | 1 | I'm good, you? | 2020-06-09 01:03
5 | 2 | Hey there! | 2020-06-09 01:04
我查询到:
SELECT * FROM "message" WHERE "conversationId" IN (1, 2) ORDER BY "createdDate" DESC
正如预期的那样,这 returns 所有与提供的对话 ID 匹配的消息,按最近 createdDate
.
排序
我对如何 LIMIT
只包含每个 conversationId
的第一个结果(最近的 createdDate
)感到有点困惑。
我正在寻找的输出是:
id | conversationId | body | createdDate
-------------------------------------------------------
4 | 1 | I'm good, you? | 2020-06-09 01:03
5 | 2 | Hey there! | 2020-06-09 01:04
这是一个典型的每组最大 n 问题。在 Postgres 中,解决这个问题的一种简单有效的方法是 distinct on
:
select distinct on (conversationId) m.*
from messages m
-- where conversationId in (...) -- if needed
order by conversationId, createdDate desc
你也可以使用window函数row_number
select
id,
conversationId,
body,
createdDate
from
(
SELECT
*,
row_number() over (partition by conversationId order by createdDate desc) as rn
FROM "message"
) val
where rnk = 1
如之前在其他答案中所述,DISTINCT ON 是最简单的方法。但它有限制。特别是排序顺序。
没有限制,这可以通过这两种方法实现,也可以不使用 GROUP BY
A) 只是一个简单的 self ANTI JOIN;
SELECT
m.*
FROM
"message" AS m
LEFT JOIN "message" _m ON _m."conversationId" = m."conversationId" AND _m."createdDate" > m."createdDate"
WHERE
_m."id" IS NULL
它被称为 ANTI JOIN,因为通常我们想要 "joined" 结果,而在这种情况下我们不需要它们。因此,使用对话中最高时间戳的所需条件进行自左外连接。
为了更好地理解这个概念,您可以 运行 不带 _m."id" IS NULL 条件的查询。这将为您提供所有匹配的记录:
Right Side : "m" || Left Side : "_m"
---------------------------------------------------------------------------------
id | conversationId | createdDate || id | conversationId | createdDate
---------------------------------------------------------------------------------
1 | 1 | 2020-06-09 01:01 || 2 | 1 | 2020-06-09 01:02
1 | 1 | 2020-06-09 01:01 || 4 | 1 | 2020-06-09 01:03
2 | 1 | 2020-06-09 01:02 || 4 | 1 | 2020-06-09 01:03
3 | 2 | 2020-06-09 01:02 || 5 | 2 | 2020-06-09 01:04
4 | 1 | 2020-06-09 01:03 || NULL
5 | 2 | 2020-06-09 01:04 || NULL
包含 4 和 5 的记录没有任何匹配项,因为这些对话中没有更高的日期。当左侧没有匹配记录时,我们可以确定右侧具有该对话的最高时间戳。
B) 一个简单的子查询
SELECT
m.*
FROM
"message" m
WHERE
m."id" = (
SELECT
"id"
FROM
"message"
WHERE
"conversationId" = m."conversationId"
ORDER BY
"createdDate" DESC
LIMIT 1
)
条件中的 SUBQUERY 总是 returns 会话中具有最高时间戳的记录的 ID。我们使用它的 return 值来限制主查询中的记录。
我不知道怎么形容这个,所以请原谅这个标题...
我正在尝试编写一个函数,以便在提供对话 ID 数组时检索属于对话的最新消息。
我的"message"table看起来如下:
id | conversationId | body | createdDate
-------------------------------------------------------
1 | 1 | Hello | 2020-06-09 01:01
2 | 1 | How are you? | 2020-06-09 01:02
3 | 2 | Hi | 2020-06-09 01:02
4 | 1 | I'm good, you? | 2020-06-09 01:03
5 | 2 | Hey there! | 2020-06-09 01:04
我查询到:
SELECT * FROM "message" WHERE "conversationId" IN (1, 2) ORDER BY "createdDate" DESC
正如预期的那样,这 returns 所有与提供的对话 ID 匹配的消息,按最近 createdDate
.
我对如何 LIMIT
只包含每个 conversationId
的第一个结果(最近的 createdDate
)感到有点困惑。
我正在寻找的输出是:
id | conversationId | body | createdDate
-------------------------------------------------------
4 | 1 | I'm good, you? | 2020-06-09 01:03
5 | 2 | Hey there! | 2020-06-09 01:04
这是一个典型的每组最大 n 问题。在 Postgres 中,解决这个问题的一种简单有效的方法是 distinct on
:
select distinct on (conversationId) m.*
from messages m
-- where conversationId in (...) -- if needed
order by conversationId, createdDate desc
你也可以使用window函数row_number
select
id,
conversationId,
body,
createdDate
from
(
SELECT
*,
row_number() over (partition by conversationId order by createdDate desc) as rn
FROM "message"
) val
where rnk = 1
如之前在其他答案中所述,DISTINCT ON 是最简单的方法。但它有限制。特别是排序顺序。
没有限制,这可以通过这两种方法实现,也可以不使用 GROUP BY
A) 只是一个简单的 self ANTI JOIN;
SELECT
m.*
FROM
"message" AS m
LEFT JOIN "message" _m ON _m."conversationId" = m."conversationId" AND _m."createdDate" > m."createdDate"
WHERE
_m."id" IS NULL
它被称为 ANTI JOIN,因为通常我们想要 "joined" 结果,而在这种情况下我们不需要它们。因此,使用对话中最高时间戳的所需条件进行自左外连接。
为了更好地理解这个概念,您可以 运行 不带 _m."id" IS NULL 条件的查询。这将为您提供所有匹配的记录:
Right Side : "m" || Left Side : "_m"
---------------------------------------------------------------------------------
id | conversationId | createdDate || id | conversationId | createdDate
---------------------------------------------------------------------------------
1 | 1 | 2020-06-09 01:01 || 2 | 1 | 2020-06-09 01:02
1 | 1 | 2020-06-09 01:01 || 4 | 1 | 2020-06-09 01:03
2 | 1 | 2020-06-09 01:02 || 4 | 1 | 2020-06-09 01:03
3 | 2 | 2020-06-09 01:02 || 5 | 2 | 2020-06-09 01:04
4 | 1 | 2020-06-09 01:03 || NULL
5 | 2 | 2020-06-09 01:04 || NULL
包含 4 和 5 的记录没有任何匹配项,因为这些对话中没有更高的日期。当左侧没有匹配记录时,我们可以确定右侧具有该对话的最高时间戳。
B) 一个简单的子查询
SELECT
m.*
FROM
"message" m
WHERE
m."id" = (
SELECT
"id"
FROM
"message"
WHERE
"conversationId" = m."conversationId"
ORDER BY
"createdDate" DESC
LIMIT 1
)
条件中的 SUBQUERY 总是 returns 会话中具有最高时间戳的记录的 ID。我们使用它的 return 值来限制主查询中的记录。