快速获取连接表中最新相关行的顶行

Raj*_*ngh 10 sql postgresql greatest-n-per-group postgresql-performance postgresql-13

有两个表conversationsmessages,我想获取对话及其最新消息的内容。

conversations- id(主键)、名称、创建时间

messages- id、内容、created_at、conversation_id

目前我们正在运行此查询来获取所需的数据

SELECT
    conversations.id,
    m.content AS last_message_content,
    m.created_at AS last_message_at
FROM
    conversations
INNER JOIN messages m ON conversations.id = m.conversation_id
                     AND m.id = (
    SELECT
        id
    FROM
        messages _m
    WHERE
        m.conversation_id = _m.conversation_id
    ORDER BY
        created_at DESC
    LIMIT 1)
ORDER BY
    last_message_at DESC
LIMIT 15
OFFSET 0
Run Code Online (Sandbox Code Playgroud)

上面的查询返回有效数据,但其性能随着行数的增加而降低。有没有其他方法可以提高性能来编写此查询?例如附加小提琴。

http://sqlfiddle.com/#!17/2decb/2

还尝试了已删除答案之一中的建议:

SELECT DISTINCT ON (c.id)
       c.id,
       m.content AS last_message_content,
       m.created_at AS last_message_at
  FROM conversations AS c
 INNER JOIN messages AS m
    ON c.id = m.conversation_id 
 ORDER BY c.id, m.created_at DESC
 LIMIT 15 OFFSET 0
Run Code Online (Sandbox Code Playgroud)

http://sqlfiddle.com/#!17/2decb/5

但这个查询的问题是它不按 排序m.created_at。我希望结果集按以下顺序排序m.created_at DESC

Kaz*_*Nur 5

由于结果中除了对话中的 id 之外没有选择任何列,因此您只能查询消息表以减少时间(查询 1)。如果可能存在conversation_id对话表中不可用的任何消息,并且您不想选择这些消息,那么您可以使用第二个查询。

架构和插入语句:

CREATE TABLE conversations 
(
    id INT, 
    name VARCHAR(200), 
    created_at DATE
);

INSERT INTO conversations VALUES (1, 'CONV1', '1 DEC 2021');
INSERT INTO conversations VALUES (2, 'CONV2', '1 DEC 2021');

CREATE TABLE messages 
(
    id INT, 
    content VARCHAR(200), 
    created_at DATE, 
    conversation_id INT
);

INSERT INTO messages VALUES (1, 'TEST 3', '12 DEC 2021', 1);
INSERT INTO messages VALUES (2, 'TEST 2', '11 DEC 2021', 1);
INSERT INTO messages VALUES (3, 'TEST 1', '10 DEC 2021', 1);
INSERT INTO messages VALUES (4, 'TEST CONV2 1', '10 DEC 2021', 2);
Run Code Online (Sandbox Code Playgroud)

询问:

 WITH Latest_Conversation_Messages AS
 ( 
     SELECT
         conversation_id, content, created_at, 
         ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY created_at DESC) rn 
     FROM
         messages
 )
 SELECT
     conversation_id, content AS last_message_content,
     created_at AS last_message_at 
 FROM
     Latest_Conversation_Messages 
 WHERE
     rn = 1
Run Code Online (Sandbox Code Playgroud)

输出:

对话id 最后消息内容 最后一条消息的时间
1 测试3 2021-12-12
2 测试转换器2 1 2021-12-10

查询2:

WITH Latest_Conversation_Messages AS
( 
    SELECT
        conversation_id, content, created_at , 
        ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY created_at DESC) rn 
    FROM
        messages m
    WHERE
        EXISTS (SELECT 1 FROM conversations c 
                WHERE c.id = m.conversation_id)
)
SELECT 
    conversation_id, content AS last_message_content,
    created_at AS last_message_at 
FROM
    Latest_Conversation_Messages 
WHERE
    rn = 1
Run Code Online (Sandbox Code Playgroud)

输出:

对话id 最后消息内容 最后一条消息的时间
1 测试3 2021-12-12
2 测试转换器2 1 2021-12-10

db<>在这里摆弄


ESG*_*ESG 1

您是否尝试过横向连接?

SELECT
    conversations.id,
    m.content AS last_message_content,
    m.created_at AS last_message_at
FROM "conversations" 
INNER JOIN LATERAL (
  SELECT content, created_at 
  FROM  messages m
  WHERE conversations.id = m.conversation_id 
  ORDER BY created_at DESC
  FETCH FIRST 1 ROW ONLY
) m ON TRUE
ORDER BY last_message_at DESC
LIMIT 15 OFFSET 0
Run Code Online (Sandbox Code Playgroud)