如何将此SQL连接语句转换为Ecto查询?

时间:2018-03-17 12:21:41

标签: elixir phoenix-framework ecto

我有一个SQL查询只返回任何其他用户发送给用户的最新消息(即"只显示每个消息发送者发送给我的最新消息")。在纯SQL中,我可以按如下方式完成这项工作:

select messages.* from messages
join
(select max(inserted_at) maxtime,sender_id from messages group by sender_id) latest
on messages.inserted_at=latest.maxtime and messages.sender_id=latest.sender_id and messages.receiver_id=2;

关于如何将这样的嵌套连接语句翻译成惯用的Ecto查询的任何想法?

1 个答案:

答案 0 :(得分:0)

经过一番挖掘,我发现我能够在连接中使用subquery使用纯Ecto语法来完成此操作,如下所示:

user_id = 2

subquery = from(s in Message,
                select: %{ sender_id: s.sender_id, max_time: max(s.inserted_at) },
                where: s.receiver_id == ^user_id,
                group_by: s.sender_id
               )

query = from(m in Message,
             join: s in subquery(subquery), on: m.inserted_at == s.max_time and m.sender_id == s.sender_id,
             where: m.receiver_id == ^user_id
            )

Repo.all(query)

对于那些感兴趣的人,当您检查查询以查看SQL的外观时,这是输出:

 > Ecto.Adapters.SQL.to_sql(:all, Repo, query)
 {"SELECT m0.\"id\", m0.\"sender_id\", m0.\"receiver_id\", m0.\"body\", m0.\"received_at\", m0.\"inserted_at\" FROM \"messages\" AS m0 INNER JOIN (SELECT m0.\"sender_id\" AS \"sender_id\", max(m0.\"inserted_at\") AS \"max_time\" FROM \"messages\" AS m0 WHERE (m0.\"receiver_id\" = $1) GROUP BY m0.\"sender_id\") AS s1 ON (m0.\"inserted_at\" = s1.\"max_time\") AND (m0.\"sender_id\" = s1.\"sender_id\") WHERE (m0.\"receiver_id\" = $2)", [30064, 30064]}

不完全漂亮的SQL,但是当我尝试它in a SQL fiddle时,它运行的执行时间与执行时间基本相同!

感谢@radzserg whose answer to this question让我找到了缺失的子查询。奇怪的是,这个问题的措辞和结构与我的几乎完全一样,但一年前得到了3个upvotes而不是我的4个downvotes。希望这并不意味着Elixir / Ecto社区对待初学者试图处理事情的方式发生了变化!