首页 > 解决方案 > 使用 Ecto 选择、选择合并和连接

问题描述

我试图弄清楚如何组合选择和连接表,但遇到了问题。

假设我有表 A、表 B 和表 C。表 A 有一些值,表 B 有表 C 中的许多项目。

我的查询是这样的:

query = from(
  a in TableA,
  select: %NewStruct{
    a: a.value  
  }
) 

from(
  a in query,
  join: b in TableB,
  on: b.a_id == a.id,
  join: c in TableC,
  on: c.b_id == b.id,
  select_merge: %{
    c: [c]
  }
)
|> Repo.all()

这在某种意义上是有效的,它将返回 3 个结构。它们都具有相同的值a,但是c是 TableC 中的每个项目。

当前结果:

[
  %NewStruct{
    a: "value"
    id: 1,
    c: %TableC{
      id: 1
    }
  },
  %NewStruct{
    a: "value"
    id: 1,
    c: %TableC{
      id: 2
    }
  },
  %NewStruct{
    a: "value"
    id: 1,
    c: %TableC{
      id: 3
    }
  }
]

通常,我知道使用preload: [c: c]ecto 来组合所有连接的项目,并嵌套在a.c. 但我不能在这种情况下,因为NewStruct只是一个defstruct. 我尝试NewStruct变成embedded_schema,但无法has_many正确获取定义。

我的问题是:有没有办法告诉 Ecto 连接表c应该预加载/嵌套在其中a.c?我知道如果你做了类似的事情SELECT * FROM a INNER JOIN c.b_id ON b.id,你会从 PSQL 中得到 3 行的结果。但我确实知道有时 Ecto 可以发挥一些作用,并且想知道我是否遗漏了什么。

期望的结果:

[
  %NewStruct{
    a: "value"
    id: 1,
    c: [
      %TableC{
        id: 1
      },
      %TableC{
        id: 2
      },
      %TableC{
        id: 3
      },
    ]
  }
]

标签: elixirecto

解决方案


我会构建查询,预加载TableC,然后才select进入结构。

TableA
|> join(:left, [a], b in assoc(a, :table_b))
|> join(:left, [b], c in assoc(b, :table_c))
|> preload([b, c], [:table_b, table_c: c])
|> select([a], %NewStruct{a: a.value, c: [c]})
|> Repo.all()

未经测试,但它应该工作。


推荐阅读