【问题标题】:Select all threads and order by the latest one选择所有线程并按最新线程排序
【发布时间】:2014-08-28 10:31:45
【问题描述】:

现在我已经回答了Select all forums and get latest post too.. how? 问题,我正在尝试编写一个查询来选择一个特定论坛中的所有主题,并按最新帖子的日期对它们进行排序(列“updated_at ")。

这又是我的结构:

forums                      forum_threads              forum_posts
----------                  -------------             -----------
id                          id                        id
parent_forum (NULLABLE)     forum_id                  content
name                        user_id                   thread_id
description                 title                     user_id
icon                        views                     updated_at
                            created_at                created_at
                            updated_at
                            last_post_id (NULLABLE)

我尝试编写此查询,它可以正常工作.. 但不如预期:它没有按线程的最后发布日期排序:

SELECT DISTINCT ON(t.id) t.id, u.username, p.updated_at, t.title
FROM   forum_threads             t
LEFT   JOIN forum_posts   p ON p.thread_id = t.id
LEFT   JOIN users         u ON u.id = p.user_id
WHERE t.forum_id = 3
ORDER  BY t.id, p.updated_at DESC;

我该如何解决这个问题?

【问题讨论】:

  • 将 t.id 从您的订单中删除
  • 我不能因为 DISTINCT ON.. 否则会收到错误消息
  • (column "updated_at") 不明确。请澄清:forum_posts.updated_atforum_threads.updated_at?假设我的回答是前者,就像你的previous question 和你的查询所暗示的那样。

标签: sql postgresql greatest-n-per-group window-functions


【解决方案1】:

假设您希望每个线程有一个单行,而不是所有帖子的所有行。

DISTINCT ON 仍然是最方便的工具。但是前面的 ORDER BY 项必须匹配 DISTINCT ON 子句的表达式。如果您想以其他方式对结果进行排序,则需要将其包装到子查询中,并将另一个 ORDER BY 添加到外部查询中:

SELECT *
FROM  (
   SELECT DISTINCT ON (t.id)
          t.id, u.username, p.updated_at, t.title
   FROM   forum_threads      t
   LEFT   JOIN forum_posts   p ON p.thread_id = t.id
   LEFT   JOIN users         u ON u.id = p.user_id
   WHERE  t.forum_id = 3
   ORDER  BY t.id, p.updated_at DESC
   ) sub
ORDER  BY updated_at DESC;

如果您出于某种未知原因正在寻找没有子查询的查询,这也应该可以:

SELECT DISTINCT
       t.id
     , first_value(u.username)   OVER w AS username
     , first_value(p.updated_at) OVER w AS updated_at
     , t.title
FROM   forum_threads      t
LEFT   JOIN forum_posts   p ON p.thread_id = t.id
LEFT   JOIN users         u ON u.id = p.user_id
WHERE  t.forum_id = 3
WINDOW w AS (PARTITION BY t.id ORDER BY p.updated_at DESC)
ORDER  BY updated_at DESC;

这里发生了很多事情:

  1. 根据JOINWHERE 子句连接表并选择行。

  2. 窗口函数first_value() 的两个实例运行(在同一个窗口定义上)以从每个线程的最新帖子中检索usernameupdated_at。这会产生与线程中的帖子一样多的相同行。

  3. DISTINCT 步骤在窗口函数之后执行,并将每个集合缩减为一个实例。

  4. ORDER BY 最后应用,updated_at 引用 OUT 列(SELECT 列表),而不是两个 IN 列之一> 同名的列(FROM 列表)。

还有一个变体,带有窗口函数row_number()的子查询:

SELECT id, username, updated_at, title
FROM  (
   SELECT t.id
        , u.username
        , p.updated_at
        , t.title
        , row_number() OVER (PARTITION BY t.id
                             ORDER BY p.updated_at DESC) AS rn
   FROM   forum_threads      t
   LEFT   JOIN forum_posts   p ON p.thread_id = t.id
   LEFT   JOIN users         u ON u.id = p.user_id
   WHERE  t.forum_id = 3
   ) sub
WHERE  rn = 1
ORDER  BY updated_at DESC;

类似情况:

您必须测试哪个更快。取决于几种情况。

【讨论】:

    【解决方案2】:

    忘记distinct on

    SELECT t.id, u.username, p.updated_at, t.title
    FROM   forum_threads             t
    LEFT   JOIN forum_posts   p ON p.thread_id = t.id
    LEFT   JOIN users         u ON u.id = p.user_id
    WHERE t.forum_id = 3
    ORDER  BY p.updated_at DESC;
    

    【讨论】:

    • erm,你确定你没有得到所有 forum_id 为 3 的话题以及他们所有的帖子吗?
    • 你是对的,我得到了所有线程和他们所有的帖子..但这没有帮助,因为我不能分组(或只能按 t.id 和 p.id 分组,这不会改变任何东西)
    猜你喜欢
    • 2021-05-21
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多