Mla*_*vić 9 mysql elixir arity ecto
我想执行如下查询:
SELECT id, name
FROM mytable
ORDER BY FIELD(name, 'B', 'A', 'D', 'E', 'C')
Run Code Online (Sandbox Code Playgroud)
FIELD是MySQL特定的函数,'B', 'A', 'D', 'E', 'C'是来自List的值.
我尝试使用片段,但它似乎不允许仅在运行时中知道动态arity.
除了使用完全原始使用Ecto.Adapters.SQL.query,有没有办法使用Ecto的查询DSL处理这个?
编辑:这是第一个天真的方法,当然不起作用:
ids = [2, 1, 3] # this list is of course created dynamically and does not always have three items
query = MyModel
|> where([a], a.id in ^ids)
|> order_by(fragment("FIELD(id, ?)", ^ids))
Run Code Online (Sandbox Code Playgroud)
ORM 非常棒,直到它们泄漏为止。最终,所有人都会这样做。Ecto 还很年轻(例如,它在 30 天前OR才获得了将where 子句组合在一起的能力),因此它还不够成熟,不足以开发考虑高级 SQL 循环的 API。
在调查了可能的选择后,您并不是唯一有此要求的人。无法理解片段中的列表(无论是作为片段的一部分order_by还是where其他任何地方)已在Ecto 问题 #1485、StackOverflow、Elixir 论坛和这篇博客文章中提到。后者尤其具有指导意义。稍后会详细介绍。首先,让我们尝试一些实验。
实验#1:人们可能首先尝试使用Kernel.apply/3将列表传递给fragment,但这不起作用:
|> order_by(Kernel.apply(Ecto.Query.Builder, :fragment, ^ids))
Run Code Online (Sandbox Code Playgroud)
实验#2:那么也许我们可以通过字符串操作来构建它。如何为fragment运行时内置的字符串提供足够的占位符以便从列表中提取:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(Enum.map(ids, fn _ -> "?" end), ","), ")"], ""), ^ids))
Run Code Online (Sandbox Code Playgroud)
这会产生FIELD(id,?,?,?)给定的ids = [1, 2, 3]. 不,这也行不通。
实验#3:创建从 ids 构建的完整的最终 SQL,将原始 ID 值直接放入组合字符串中。除了可怕之外,它也不起作用:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(^ids, ","), ")"], "")))
Run Code Online (Sandbox Code Playgroud)
实验#4:这让我想到了我提到的那篇博客文章。在其中,作者解决了缺乏or_where使用一组基于条件数量的预定义宏的问题:
defp orderby_fragment(query, [v1]) do
from u in query, order_by: fragment("FIELD(id,?)", ^v1)
end
defp orderby_fragment(query, [v1,v2]) do
from u in query, order_by: fragment("FIELD(id,?,?)", ^v1, ^v2)
end
defp orderby_fragment(query, [v1,v2,v3]) do
from u in query, order_by: fragment("FIELD(id,?,?,?)", ^v1, ^v2, ^v3)
end
defp orderby_fragment(query, [v1,v2,v3,v4]) do
from u in query, order_by: fragment("FIELD(id,?,?,?)", ^v1, ^v2, ^v3, ^v4)
end
Run Code Online (Sandbox Code Playgroud)
虽然可以这么说,这种方法有效并使用了 ORM,但它要求您拥有有限的、可管理数量的可用字段。这可能会也可能不会改变游戏规则。
我的建议:不要试图解决 ORM 的泄漏问题。你知道最好的查询。如果 ORM 不接受它,请直接使用原始 SQL 编写,并记录 ORM 不起作用的原因。将其屏蔽在函数或模块后面,以便您可以保留将来更改其实现的权利。有一天,当 ORM 赶上时,您就可以很好地重写它,而不会对系统的其余部分产生影响。
| 归档时间: |
|
| 查看次数: |
861 次 |
| 最近记录: |