如何根据多列获取不同的行,包括来自另一个 table 的最新行?
How to get distinct rows based on multiple columns are include the latest row from another table?
我有一个 users
table 和一个 private_messages
table,我想获取当前用户发送或接收消息的用户列表另一位用户连同最新消息内容。
用户table
id | username
private_messages table
author_id | recipient_id | content | creation_date
这是我最接近获得用户的一次
SELECT u.id, u.username FROM users u
WHERE u.id IN
(SELECT p.author_id as id
FROM private_messages p
WHERE p.recipient_id = '6d480813-c854-40fc-a3cf-cea0944854ab'
);
但如您所见,只有当用户不是消息的作者时才有效,而且我不知道如何包含最新消息的内容。
作为 SQL 的新手,我也准备更改 table 的模型。
用户 table 数据
╔════════════════════════════════════════╦════════════════╗
║ "id" ║ "username" ║
╠════════════════════════════════════════╬════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "KayleeSchumm" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "JordonWaters" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "AsafAviv" ║
╚════════════════════════════════════════╩════════════════╝
private_messages数据
╔════════════════════════════════════════╦════════════════════════════════════════╦═════════════════╦══════════════════════════════╗
║ "author_id" ║ "recipient_id" ║ "content" ║ "creation_date" ║
╠════════════════════════════════════════╬════════════════════════════════════════╬═════════════════╬══════════════════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
╚════════════════════════════════════════╩════════════════════════════════════════╩═════════════════╩══════════════════════════════╝
预期输出
╔════════════════════════════════════════╦════════════════╦═════════════════╗
║ "id" ║ "username" ║ "content" ║
╠════════════════════════════════════════╬════════════════╬═════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "KayleeSchumm" ║ "hello world 0" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "JordonWaters" ║ "hello world 0" ║
╚════════════════════════════════════════╩════════════════╩═════════════════╝
我想你想要:
SELECT u.id, u.username, pm.*
FROM users u CROSS JOIN
(values ('6d480813-c854-40fc-a3cf-cea0944854ab')
) as v(the_user) CROSS JOIN LATERAL
(SELECT DISTINCT ON (v2.other_user) pm.*
FROM private_messages pm CROSS JOIN LATERAL
(VALUES (CASE WHEN pm.recipient_id = v.the_user
THEN pm.author_id ELSE pm.recipient_id
END)
) v2(other_user)
WHERE v.the_user = v2.other_user
ORDER BY v2.other_user, pm.creation_date desc
) pm;
VALUES()
子句只是为了方便处理常量用户值。
关键部分是DISTINCT ON
。这returns第一行每个"other user"基于ORDER BY
。在这种情况下,ORDER BY
将最近的记录放在第一位。
试试这个:
SELECT u.id, u.username, p.content
FROM users u
INNER JOIN (
SELECT DISTINCT
CASE WHEN p.author_id = '6d480813-c854-40fc-a3cf-cea0944854ab' THEN p.recipient_id ELSE p.author_id END user_id,
p.content
FROM private_messages p
WHERE '6d480813-c854-40fc-a3cf-cea0944854ab' IN (p.author_id, p.recipient_id)
AND NOT EXISTS (
SELECT 1 FROM private_messages
WHERE (author_id, recipient_id) IN ((p.author_id, p.recipient_id), (p.recipient_id, p.author_id))
AND creation_date > p.creation_date
)
) p ON u.id = p.user_id
连接到 users
的查询使用 NOT EXISTS
到 return 给每个用户的最后一条消息。
见 demo.
或者使用 CTE
s 和 ROW_NUMBER()
window 函数:
WITH
cur_user(id) AS (VALUES('6d480813-c854-40fc-a3cf-cea0944854ab')),
cte_all AS (
SELECT content, creation_date,
CASE (SELECT id FROM cur_user)
WHEN author_id THEN recipient_id
ELSE author_id
END user_id
FROM private_messages
WHERE (SELECT id FROM cur_user) IN (author_id, recipient_id)
),
cte_last AS (
SELECT t.content, t.creation_date, t.user_id
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY creation_date) rn
FROM cte_all
) t
WHERE t.rn = 1
)
SELECT u.id, u.username, c.content
FROM users u INNER JOIN cte_last c
ON c.user_id = u.id
参见demo。
结果:
| id | username | content |
| ------------------------------------ | ------------ | ------------- |
| 2f8afc87-13d3-4654-8333-465b094c9ccf | KayleeSchumm | hello world 0 |
| e5e37e98-3468-47e1-9c7e-1311988d6d79 | JordonWaters | hello world 0 |
我有一个 users
table 和一个 private_messages
table,我想获取当前用户发送或接收消息的用户列表另一位用户连同最新消息内容。
用户table
id | username
private_messages table
author_id | recipient_id | content | creation_date
这是我最接近获得用户的一次
SELECT u.id, u.username FROM users u
WHERE u.id IN
(SELECT p.author_id as id
FROM private_messages p
WHERE p.recipient_id = '6d480813-c854-40fc-a3cf-cea0944854ab'
);
但如您所见,只有当用户不是消息的作者时才有效,而且我不知道如何包含最新消息的内容。
作为 SQL 的新手,我也准备更改 table 的模型。
用户 table 数据
╔════════════════════════════════════════╦════════════════╗
║ "id" ║ "username" ║
╠════════════════════════════════════════╬════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "KayleeSchumm" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "JordonWaters" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "AsafAviv" ║
╚════════════════════════════════════════╩════════════════╝
private_messages数据
╔════════════════════════════════════════╦════════════════════════════════════════╦═════════════════╦══════════════════════════════╗
║ "author_id" ║ "recipient_id" ║ "content" ║ "creation_date" ║
╠════════════════════════════════════════╬════════════════════════════════════════╬═════════════════╬══════════════════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
║ "6d480813-c854-40fc-a3cf-cea0944854ab" ║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "hello world 0" ║ "2020-04-14 14:01:40.121+03" ║
╚════════════════════════════════════════╩════════════════════════════════════════╩═════════════════╩══════════════════════════════╝
预期输出
╔════════════════════════════════════════╦════════════════╦═════════════════╗
║ "id" ║ "username" ║ "content" ║
╠════════════════════════════════════════╬════════════════╬═════════════════╣
║ "2f8afc87-13d3-4654-8333-465b094c9ccf" ║ "KayleeSchumm" ║ "hello world 0" ║
║ "e5e37e98-3468-47e1-9c7e-1311988d6d79" ║ "JordonWaters" ║ "hello world 0" ║
╚════════════════════════════════════════╩════════════════╩═════════════════╝
我想你想要:
SELECT u.id, u.username, pm.*
FROM users u CROSS JOIN
(values ('6d480813-c854-40fc-a3cf-cea0944854ab')
) as v(the_user) CROSS JOIN LATERAL
(SELECT DISTINCT ON (v2.other_user) pm.*
FROM private_messages pm CROSS JOIN LATERAL
(VALUES (CASE WHEN pm.recipient_id = v.the_user
THEN pm.author_id ELSE pm.recipient_id
END)
) v2(other_user)
WHERE v.the_user = v2.other_user
ORDER BY v2.other_user, pm.creation_date desc
) pm;
VALUES()
子句只是为了方便处理常量用户值。
关键部分是DISTINCT ON
。这returns第一行每个"other user"基于ORDER BY
。在这种情况下,ORDER BY
将最近的记录放在第一位。
试试这个:
SELECT u.id, u.username, p.content
FROM users u
INNER JOIN (
SELECT DISTINCT
CASE WHEN p.author_id = '6d480813-c854-40fc-a3cf-cea0944854ab' THEN p.recipient_id ELSE p.author_id END user_id,
p.content
FROM private_messages p
WHERE '6d480813-c854-40fc-a3cf-cea0944854ab' IN (p.author_id, p.recipient_id)
AND NOT EXISTS (
SELECT 1 FROM private_messages
WHERE (author_id, recipient_id) IN ((p.author_id, p.recipient_id), (p.recipient_id, p.author_id))
AND creation_date > p.creation_date
)
) p ON u.id = p.user_id
连接到 users
的查询使用 NOT EXISTS
到 return 给每个用户的最后一条消息。
见 demo.
或者使用 CTE
s 和 ROW_NUMBER()
window 函数:
WITH
cur_user(id) AS (VALUES('6d480813-c854-40fc-a3cf-cea0944854ab')),
cte_all AS (
SELECT content, creation_date,
CASE (SELECT id FROM cur_user)
WHEN author_id THEN recipient_id
ELSE author_id
END user_id
FROM private_messages
WHERE (SELECT id FROM cur_user) IN (author_id, recipient_id)
),
cte_last AS (
SELECT t.content, t.creation_date, t.user_id
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY creation_date) rn
FROM cte_all
) t
WHERE t.rn = 1
)
SELECT u.id, u.username, c.content
FROM users u INNER JOIN cte_last c
ON c.user_id = u.id
参见demo。
结果:
| id | username | content |
| ------------------------------------ | ------------ | ------------- |
| 2f8afc87-13d3-4654-8333-465b094c9ccf | KayleeSchumm | hello world 0 |
| e5e37e98-3468-47e1-9c7e-1311988d6d79 | JordonWaters | hello world 0 |