Rails has_many 根据最新的 created_at 记录对列进行排序
Rails has_many sorting of a column based on latest created_at record
我有两个模型,分别命名为 'customer' 和 'membership'。客户可以有很多会员资格。我想根据成员资格的 created_at 和 cancelled_at 日期列对记录进行排序。会员记录应该是最近创建和取消的记录,不应包含 nil 值。例如,如果 ID 为 1 的客户有两条 created_at 分别为 2020 年 12 月 25 日和 2020 年 12 月 27 日的会员记录,取消日期为 2021 年 1 月 1 日和 nil,那么该记录应该放在底部,因为最新的会员资格仍然有效它的 cancelled_at 日期为 nil,不应出现在 asc 或 desc 排序列表中。但是,如果客户有 2 个 created_at 的会员资格,分别为 2020 年 12 月 25 日和 2020 年 12 月 27 日,取消日期为 2000 年 1 月 1 日和 2000 年 1 月 5 日,那么应该弹出一条记录,取消日期为 2000 年 1 月 5 日。
Database Table - Customer
Id Name
1 A
2 B
3 C
4 D
5 E
Database Table - Membership
id customer_id created_at cancelled_at
1 1 1 jan 2000 5 jan 2000
2 1 2 jan 2000 nil
3 2 1 Dec 1999 2 Dec 1999
4 5 15 Jan 2000 16 Jan 2000
5 5 17 Jan 2000 20 Jan 2000
Then result should be (for asc order of cancelled_at)
customer_id name cancelled at
2 B 2 Dec 1999
5 E 20 Jan 2000
1 A nil
3 C nil
4 D nil
我的查询没有产生所需的输出:
Customer.joins('LEFT JOIN memberships')
.where("memberships.cancel_at = (SELECT MAX(memberships.cancel_at) FROM memberships WHERE customers.id = memberships.customer_id)")
.group('customers.id')
.order('MAX(memberships.created_at) ASC')
解决这个问题的一个非常有效的方法是使用 lateral join:
SELECT c.*, latest_membership.cancelled_at AS cancelled_at
FROM customers c
LEFT JOIN LATERAL (
SELECT cancelled_at
FROM memberships m
WHERE m.customer_id = c.id -- lateral reference
ORDER BY m.created_at
LIMIT 1
) AS latest_membership ON TRUE
ORDER BY latest_membership.cancelled_at ASC NULLS LAST
这个奇特的新玩具可能有点难以理解,但它基本上就像一个 foreach 循环 - 除了 SQL。 PostgreSQL 将遍历结果集中的每一行并使用该行作为参数评估子查询。疯狂的东西!
ActiveRecord 不支持“开箱即用”的横向连接,因为它是一个相当 Postgres 特定的功能,它旨在成为多语言。但是您可以使用 SQL 字符串或 Arel.
创建它们
Customer
.select(
'customers.*',
'latest_membership.cancelled_at AS cancelled_at'
)
.joins(<<~SQL)
LEFT JOIN LATERAL (
SELECT cancelled_at
FROM memberships m
WHERE m.customer_id = c.id -- lateral reference
ORDER created_at
LIMIT 1
) AS latest_membership ON TRUE
SQL
.order('latest_membership.cancelled_at ASC NULLS LAST')
我有两个模型,分别命名为 'customer' 和 'membership'。客户可以有很多会员资格。我想根据成员资格的 created_at 和 cancelled_at 日期列对记录进行排序。会员记录应该是最近创建和取消的记录,不应包含 nil 值。例如,如果 ID 为 1 的客户有两条 created_at 分别为 2020 年 12 月 25 日和 2020 年 12 月 27 日的会员记录,取消日期为 2021 年 1 月 1 日和 nil,那么该记录应该放在底部,因为最新的会员资格仍然有效它的 cancelled_at 日期为 nil,不应出现在 asc 或 desc 排序列表中。但是,如果客户有 2 个 created_at 的会员资格,分别为 2020 年 12 月 25 日和 2020 年 12 月 27 日,取消日期为 2000 年 1 月 1 日和 2000 年 1 月 5 日,那么应该弹出一条记录,取消日期为 2000 年 1 月 5 日。
Database Table - Customer
Id Name
1 A
2 B
3 C
4 D
5 E
Database Table - Membership
id customer_id created_at cancelled_at
1 1 1 jan 2000 5 jan 2000
2 1 2 jan 2000 nil
3 2 1 Dec 1999 2 Dec 1999
4 5 15 Jan 2000 16 Jan 2000
5 5 17 Jan 2000 20 Jan 2000
Then result should be (for asc order of cancelled_at)
customer_id name cancelled at
2 B 2 Dec 1999
5 E 20 Jan 2000
1 A nil
3 C nil
4 D nil
我的查询没有产生所需的输出:
Customer.joins('LEFT JOIN memberships')
.where("memberships.cancel_at = (SELECT MAX(memberships.cancel_at) FROM memberships WHERE customers.id = memberships.customer_id)")
.group('customers.id')
.order('MAX(memberships.created_at) ASC')
解决这个问题的一个非常有效的方法是使用 lateral join:
SELECT c.*, latest_membership.cancelled_at AS cancelled_at
FROM customers c
LEFT JOIN LATERAL (
SELECT cancelled_at
FROM memberships m
WHERE m.customer_id = c.id -- lateral reference
ORDER BY m.created_at
LIMIT 1
) AS latest_membership ON TRUE
ORDER BY latest_membership.cancelled_at ASC NULLS LAST
这个奇特的新玩具可能有点难以理解,但它基本上就像一个 foreach 循环 - 除了 SQL。 PostgreSQL 将遍历结果集中的每一行并使用该行作为参数评估子查询。疯狂的东西!
ActiveRecord 不支持“开箱即用”的横向连接,因为它是一个相当 Postgres 特定的功能,它旨在成为多语言。但是您可以使用 SQL 字符串或 Arel.
创建它们Customer
.select(
'customers.*',
'latest_membership.cancelled_at AS cancelled_at'
)
.joins(<<~SQL)
LEFT JOIN LATERAL (
SELECT cancelled_at
FROM memberships m
WHERE m.customer_id = c.id -- lateral reference
ORDER created_at
LIMIT 1
) AS latest_membership ON TRUE
SQL
.order('latest_membership.cancelled_at ASC NULLS LAST')