1

我在哪里

对于这个例子,考虑Friends.repo

Person有字段:id, :name,:age

示例 Ecto 查询:

iex> from(x in Friends.Person, where: {x.id, x.age} in [{1,10}, {2, 20}, {1, 30}], select: [:name])

当我运行它时,我得到了相关的结果。就像是:

[
  %{name: "abc"},
  %{name: "xyz"}
]

但是当我尝试插入查询时,它会抛出错误

iex> list = [{1,10}, {2, 20}, {1, 30}]
iex> from(x in Friends.Person, where: {x.id, x.age} in ^list, select: [:name])
** (Ecto.Query.CompileError) Tuples can only be used in comparisons with literal tuples of the same size

我假设我需要对list变量进行某种类型的转换。此处的文档中提到了:“当插值时,您可能需要明确告诉 Ecto 被插值的预期类型是什么

我需要的

对于这样的复杂类型,我该如何实现?如何为“元组列表,每个大小为 2”键入 cast?[{:integer, :integer}] 之类的东西似乎不起作用。

如果不是上述情况,WHERE (col1, col2) in ((val1, val2), (val3, val4), ...)使用 Ecto Query 运行某种查询的任何替代方法?

4

2 回答 2

1

不幸的是,应该按照错误消息中的说明处理该错误:仅支持文字元组

我无法想出一些更优雅、更不脆弱的解决方案,但我们总是把大锤作为最后的手段。这个想法是生成和执行原始查询。

list = [{1,10}, {2, 20}, {1, 30}]
#⇒ [{1, 10}, {2, 20}, {1, 30}]
values =
  Enum.join(for({id, age} <- list, do: "(#{id}, #{age})"), ", ")
#⇒ "(1, 10), (2, 20), (1, 30)"


Repo.query(~s"""
  SELECT name FROM persons
  JOIN (VALUES #{values}) AS j(v_id, v_age)
  ON id = v_id AND age = v_age
""")    

上面应该返回{:ok, %Postgrex.Result{}}成功的元组。

于 2019-08-08T07:38:19.963 回答
0

您可以为每个字段和 使用单独的数组来执行此操作,它将数组unnest压缩成行,每个数组都有一列:

ids =[ 1,  2,  1]
ages=[10, 20, 30]

from x in Friends.Person, 
inner_join: j in fragment("SELECT distinct * from unnest(?::int[],?::int[]) AS j(id,age)", ^ids, ^ages),
        on: x.id==j.id and x.age==j.age,
select: [:name]

另一种方法是使用 json:

list = [%{id: 1, age: 10}, 
        %{id: 2, age: 20}, 
        %{id: 1, age: 30}]

from x in Friends.Person,
inner_join: j in fragment("SELECT distinct * from jsonb_to_recordset(?) AS j(id int,age int)", ^list),
        on: x.id==j.id and x.age==j.age,
select: [:name]

更新:我现在看到了标签 mysql,上面是为 postgres 编写的,但也许它可以用作 mySql 版本的基础。

于 2020-07-07T16:18:39.120 回答