【问题标题】:postgresql: Join n-th rowpostgresql:加入第n行
【发布时间】:2016-02-11 18:35:22
【问题描述】:

我有一个表 users 和一个表 orders。

表 users 有一个主键 user_id 和一些其他字段。表orders有一个外键user_id,一个名字order_name和一个日期order_date

对于旧版导出,我需要构建一个查询以返回表单中的数据 user_id | order_name1 | order_name2 | order_name3 其中订单 1-3 是客户最近的三个订单。

我的想法是查询客户并使用 select to orders 进行三个连接

SELECT 
 users.*,
 order1.order_name AS order_name1,
 order2.order_name AS order_name2,
 order3.order_name AS order_name3
 FROM users AS users
JOIN (
 SELECT
  user_id,
  order_name,
  order_date
 FROM orders ORDER BY order_date DESC OFFSET 0 LIMIT 1)
AS order1 ON order1.user_id=users.user_id
JOIN (
 SELECT
  user_id,
  order_name,
  order_date
 FROM orders ORDER BY order_date DESC OFFSET 1 LIMIT 1)
AS order2 ON order2.user_id=users.user_id
JOIN (
 SELECT
  user_id,
  order_name,
  order_date
 FROM orders ORDER BY order_date DESC OFFSET 2 LIMIT 1)
AS order3 ON order3.user_id=users.user_id

但是,这不起作用,因为它只从订单中返回一行,而不是专门针对每个用户。

这样的查询怎么写?

【问题讨论】:

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


    【解决方案1】:

    您可以使用window function 为每个用户的行编号:

    SELECT users.*,
           o.order_name,
           o.rn as order_number
    FROM users AS users
    JOIN ( SELECT user_id,
                  order_name,
                  order_date, 
                  row_number() over (Partition by user_id) order by order_date desc as rn
           FROM orders
    ) AS o ON o.user_id = users.user_id and o.rn <= 3;
    

    但是,以上内容不会为您提供三列,而是为每个用户提供三行。要将其转换为列,您需要通过应用 group by 来应用穷人的枢轴模式:

    SELECT users.user_id,
           max(case when o.rn = 1 then o.order_name end) as order_name1,
           max(case when o.rn = 2 then o.order_name end) as order_name2,
           max(case when o.rn = 3 then o.order_name end) as order_name3
    FROM users AS users
    JOIN ( SELECT user_id,
                  order_name,
                  order_date, 
                  row_number() over (Partition by user_id) order by order_date desc as rn
           FROM orders
    ) AS o ON o.user_id = users.user_id and o.rn <= 3;
    GROUP BY users.user_id;
    

    如果users.user_id 是该表的主键,您可以从users 表中添加其他列,而无需将它们添加到group by

    从 Postgres 9.4 开始,表达式

    max(case when o.rn = 1 then o.order_name end) as order_name1
    

    也可以写成:

    max(o.order_name) filter (where o.rn = 1) as order_name1
    

    【讨论】:

    • 你救了我的命!感谢您指出窗口功能!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多