Sql 获取最新的子消息以及无子消息的父消息

Sql 获取最新的子消息以及无子消息的父消息,sql,ruby-on-rails,postgresql,activerecord,greatest-n-per-group,Sql,Ruby On Rails,Postgresql,Activerecord,Greatest N Per Group,下面是消息模型 class Message < ApplicationRecord belongs_to :parent_message, class_name: 'Message', optional: true has_many :child_messages, foreign_key: :parent_message_id, class_name: "Message" has_many :message_participants scope :latest_mess

下面是消息模型

class Message < ApplicationRecord
  belongs_to :parent_message, class_name: 'Message', optional: true
  has_many :child_messages, foreign_key: :parent_message_id, class_name: "Message"
  has_many :message_participants

  scope :latest_messages_by_participant, -> (user_id) do
    select("DISTINCT ON (parent_message_id) messages.*").
        joins(:message_participants).
        where(message_participants: { user_id: user_id }).
        order("parent_message_id, created_at DESC")
  end
end
类消息(用户id)do
选择(“在(父消息\u id)消息上不同。*”)。
加入(:消息\参与者)。
其中(消息参与者:{user\u id:user\u id})。
订单(“父消息id,在描述处创建”)
结束
结束
消息\u参与者
对每条消息以及发送或接收该消息的不同人员都有记录。它上面有一个用户id

上述参与者提供的
最新消息\u的问题在于,它能够获取所有子消息,但只获取最后一条父消息。这是因为我们在parent_message_id上调用distict,对于无子女的父消息,该值为NULL,因此它只在NULL上调用distinct,并返回1值(最后一个无子女的父消息)

如何在单个查询中获取所有最新消息,包括最新的子消息和最新的无子父消息

我使用的是Rails 6和Postgres 11


p.S:我还应该指出第二个问题,即消息是在ASC的created_中返回的。DESC处创建的_能够获取最新的子消息,但不能对整个集合进行排序。我可以通过调用.reverse来解决这个问题,但我想知道是否有办法解决这个问题。

我认为您需要在您的不同的on&order by中添加一个coalese,以便在
父消息\u id
为空时选择消息的
id

select("DISTINCT ON (parent_message_id) messages.*")
...
order("parent_message_id, created_at DESC")
需要转化为

select("DISTINCT ON (COALESCE(parent_message_id, messages.id)) messages.*")
...
order("COALESCE(parent_message_id, messages.id), created_at DESC")
现在,您还没有提供示例数据库表&预期的或完整的模型定义,因此我推断了很多事情。以下是最小的表定义(据我所知),在我建议的修改后AR将生成的原始sql查询[这是我们想要的查询,给出下面的模式]&结果

安装程序 提供最后一条父消息或子消息的原始SQL查询: 结果 给定
user\u id=1
,上述查询将返回结果:

 id | parent_message_id |         created_at
----+-------------------+----------------------------
  1 |                   | 2020-05-11 13:50:00.857589
  4 |                   | 2020-05-11 13:50:00.857589
(2 rows)
 id | parent_message_id |         created_at
----+-------------------+----------------------------
  2 |                 1 | 2020-05-11 13:50:00.857589
  5 |                   | 2020-05-11 13:52:01.261975
(2 rows)
给定
user\u id=2
,上述查询将返回结果:

 id | parent_message_id |         created_at
----+-------------------+----------------------------
  1 |                   | 2020-05-11 13:50:00.857589
  4 |                   | 2020-05-11 13:50:00.857589
(2 rows)
 id | parent_message_id |         created_at
----+-------------------+----------------------------
  2 |                 1 | 2020-05-11 13:50:00.857589
  5 |                   | 2020-05-11 13:52:01.261975
(2 rows)
对总体结果进行排序: DESC处创建的_能够获取最新的子消息,但不能对整个集合进行排序。我可以通过打电话来解决这个问题。相反,我想知道是否有办法解决这个问题

要在数据库中进行排序,可以将上述查询包装在cte中

例如:

WITH last_messages AS (
SELECT DISTINCT ON (COALESCE(parent_message_id, messages.id)) messages.*
FROM messages
JOIN message_participants ON message_participants.message_id = messages.id
WHERE message_participants.user_id = 2
ORDER BY COALESCE(parent_message_id, messages.id), created_at DESC
)
SELECT * FROM last_messages ORDER BY created_at;

但是,我不能100%确定这在AR中是如何表达的,我认为您需要在不同的on&order by中添加一个coalese,以便在
父消息\u id
为空时选择消息的
id

select("DISTINCT ON (parent_message_id) messages.*")
...
order("parent_message_id, created_at DESC")
需要转化为

select("DISTINCT ON (COALESCE(parent_message_id, messages.id)) messages.*")
...
order("COALESCE(parent_message_id, messages.id), created_at DESC")
现在,您还没有提供示例数据库表&预期的或完整的模型定义,因此我推断了很多事情。以下是最小的表定义(据我所知),在我建议的修改后AR将生成的原始sql查询[这是我们想要的查询,给出下面的模式]&结果

安装程序 提供最后一条父消息或子消息的原始SQL查询: 结果 给定
user\u id=1
,上述查询将返回结果:

 id | parent_message_id |         created_at
----+-------------------+----------------------------
  1 |                   | 2020-05-11 13:50:00.857589
  4 |                   | 2020-05-11 13:50:00.857589
(2 rows)
 id | parent_message_id |         created_at
----+-------------------+----------------------------
  2 |                 1 | 2020-05-11 13:50:00.857589
  5 |                   | 2020-05-11 13:52:01.261975
(2 rows)
给定
user\u id=2
,上述查询将返回结果:

 id | parent_message_id |         created_at
----+-------------------+----------------------------
  1 |                   | 2020-05-11 13:50:00.857589
  4 |                   | 2020-05-11 13:50:00.857589
(2 rows)
 id | parent_message_id |         created_at
----+-------------------+----------------------------
  2 |                 1 | 2020-05-11 13:50:00.857589
  5 |                   | 2020-05-11 13:52:01.261975
(2 rows)
对总体结果进行排序: DESC处创建的_能够获取最新的子消息,但不能对整个集合进行排序。我可以通过打电话来解决这个问题。相反,我想知道是否有办法解决这个问题

要在数据库中进行排序,可以将上述查询包装在cte中

例如:

WITH last_messages AS (
SELECT DISTINCT ON (COALESCE(parent_message_id, messages.id)) messages.*
FROM messages
JOIN message_participants ON message_participants.message_id = messages.id
WHERE message_participants.user_id = 2
ORDER BY COALESCE(parent_message_id, messages.id), created_at DESC
)
SELECT * FROM last_messages ORDER BY created_at;

但是,我不能100%确定这将如何在AR中表达

中使用
合并
表达式,在
上使用不同的
顺序

并在外部查询中对结果进行排序,以获得所需的排序顺序:

SELECT *
FROM  (
   SELECT DISTINCT ON (COALESCE(m.parent_message_id, m.id))
          m.*
   FROM   messages m
   JOIN   message_participants mp ON ...
   WHERE  mp.user_id = ...
   ORDER  BY (COALESCE(m.parent_message_id, m.id)), created_at DESC
   )
ORDER  BY created_at;
见(附详细说明):

演出
对于每个用户和消息ID的少数行,
上的
DISTINCT通常是最快的解决方案之一。对于许多行,有(更多)更快的方法。取决于更多信息,如注释所示。

中使用
合并
表达式,在
排序依据

并在外部查询中对结果进行排序,以获得所需的排序顺序:

SELECT *
FROM  (
   SELECT DISTINCT ON (COALESCE(m.parent_message_id, m.id))
          m.*
   FROM   messages m
   JOIN   message_participants mp ON ...
   WHERE  mp.user_id = ...
   ORDER  BY (COALESCE(m.parent_message_id, m.id)), created_at DESC
   )
ORDER  BY created_at;
见(附详细说明):

演出
对于每个用户和消息ID的少数行,
上的
DISTINCT通常是最快的解决方案之一。对于许多行,有(更多)更快的方法。取决于更多信息,如注释所示。

实际表定义(
CREATE table
语句)和一些示例数据将是有用的。一个简单的SQL语句也会有帮助。如果你关心性能,我们需要更多的信息。考虑这里的说明:最重要的是:在avg上每个用户和消息id大约有多少行?谢谢@ErwinBrandstetter,现在,性能不是一个考虑因素,但在情况发生变化时肯定会进行评估。实际的表定义(
CREATE table
语句)和一些示例数据将非常有用。一个简单的SQL语句也会有帮助。如果你关心性能,我们需要更多的信息。考虑这里的说明:最重要的是:avg上每个用户和消息id大约有多少行?谢谢@ErwinBrandstetter,现在,性能不是一个考虑因素,但当情况发生变化时,肯定会进行评估。