如何将 Where 子句子查询转换为 Ecto 连接?

How do I translate a Where clause subquery into an Ecto join?

我目前有两个具有 hasMany 关系的表:

customers [hasMany] items

items 中有一些 belongsTo 关系:

providers [belongsTo] items
statuses [belongsTo] items
types [belongsTo] items

我的目标是让 Ecto 查询 returns 列表 customers 至少有一个 items 记录,status_id 为 2 和任何其他items 这条 customers 记录有。

我还需要预加载 itemsprovidersstatusestypes

我已在 SQL 中复制如下:

SELECT
distinct on (c0.id)
  c0."id", 
  c0."inserted_at", 
  c0."updated_at", 
  c1."id", 
  c1."inserted_at", 
  c1."updated_at"
FROM "customers" AS c0
  INNER JOIN "items" AS c1 ON c1."customer_id" = c0."id"
WHERE EXISTS (
  SELECT * 
  FROM "items" c2
  WHERE c2."customer_id" = c0."id"
    AND c2.status_id = 2);

这是我当前 Ecto 查询的一些逆向工程:

Repo.all( 
from(p in Customers,
inner_join: r in assoc(p, :items),
where: r.status_id == 2,
preload: [
  items: r, items: :provider, items: :type, items: :status
  ],
  )
)

这个 Ecto 查询向我提供了 status_id 为 2 的商品的客户,但它只有 returns 符合条件的商品记录。如果其中一条记录的 status_id 为 2,我希望获得与该客户关联的所有项目。

我尝试通过将子查询用作连接来实现此目的,如下所示:

Repo.all( 
from(p in Customers,
inner_join: r in assoc(p, :items),
join: i in subquery(from(i in MyApp.Items, where: i.status_id == 2 )), on: p.id == i.id,
preload: [
  items: r, items: :provider, items: :type, items: :status
  ]
  )
)

然而这returns没有结果。我将非常感谢一些 Ecto 专家的任何见解。

编辑:看起来您还应该在 join: ...

中将 on: p.id == i.id, 替换为 on: p.id == i.customer_id,

像这样的东西应该按照这个文档工作 Ecto subquery

看起来我们可以通过连接过滤 status_id 为 2 的所有客户+项目,然后我们得到所有这些客户的列表,并预加载了他们的所有项目。

另请注意用于获取嵌套关联的更简单的预加载结构。

我在我的一个类似结构的数据库上测试成功,希望对你有用

import Ecto.Query

from(c in Customers,
  join: i in subquery(
    from(i in Items,
      where: i.status_id == 2
    )
  ),
  on: i.customer_id == c.id,
  preload: [items: [:provider, :type, :status]]
) |> Repo.all