postgresql:加入第n行
postgresql: Join n-th row
我有 table 个用户和 table 个订单。
table 个用户有一个主键 user_id
和一些其他字段。 table 订单有外键 user_id
、名称 order_name
和日期 order_date
。
对于遗留导出,我需要构建一个查询 returns 表单中的数据
user_id | order_name1 | order_name2 | order_name3
其中订单 1-3 是客户最近的三个订单。
我的想法是查询客户并使用 select 对订单进行三个连接
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
但是,这不起作用,因为它仅 returns 每个订单一行,而不是专门针对每个用户。
如何编写这样的查询?
您可以使用 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
是 table 的主键,您可以从 users
table 添加其他列,而无需将它们添加到 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
我有 table 个用户和 table 个订单。
table 个用户有一个主键 user_id
和一些其他字段。 table 订单有外键 user_id
、名称 order_name
和日期 order_date
。
对于遗留导出,我需要构建一个查询 returns 表单中的数据
user_id | order_name1 | order_name2 | order_name3
其中订单 1-3 是客户最近的三个订单。
我的想法是查询客户并使用 select 对订单进行三个连接
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
但是,这不起作用,因为它仅 returns 每个订单一行,而不是专门针对每个用户。
如何编写这样的查询?
您可以使用 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
是 table 的主键,您可以从 users
table 添加其他列,而无需将它们添加到 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