Ecto 查询 - 日期 + Postgres 间隔 + 查询插值
Ecto Query - Dates + Postgres Intervals + Query Interpolation
我想创建一个 Ecto 查询,按年龄(即 "minimum age (months) -> maximum age (months)".
过滤 children
table 中的记录
一种简单的方法是使用 Ecto date_add
功能:
from c in Child, where: c.birthday >
datetime_add(^Ecto.DateTime.utc, -1, "month")
问题是并非所有 children 都在同一时区,当然也不全都在 Etc/UTC
。这个查询会非常接近,但不是准确的(有些会在一天后关闭)。
我一直在尝试使用 PostgreSQL 的 interval
功能来使这个查询工作。我可以使用 SQL 客户端让它工作,但是当我尝试在片段中插入值时遇到了插入问题。
这有效(child 的时区来源于它的 location
关联):
query = from ch in Child,
join: loc in assoc(ch, :location),
where: ch.birthday <= fragment("(now() AT TIME ZONE ?)::date - interval '2 months'", loc.time_zone)
Repo.all(query)
请注意,我在 '2 months'
间隔内 hard-coded。
我认为这行得通,但不行:
query = from ch in Child,
join: loc in assoc(ch, :location),
where: ch.birthday <= fragment("(now() AT TIME ZONE ?)::date - interval ?", loc.time_zone, ^"2 months")
Repo.all(query)
请注意,我正在尝试使用 Ecto 的查询插值将 '2 months'
值引入查询。
错误如下:
[debug] QUERY ERROR source="children" db=1.7ms queue=0.1ms
SELECT c0."id", (... other properties) FROM "children" AS c0 INNER JOIN "programs" AS p2 ON p2."id" = c0."program_id" INNER JOIN "locations" AS l1 ON l1."id" = p2."location_id" WHERE (c0."birthday" <= (now() AT TIME ZONE l1."time_zone")::date - interval ) ["2 months"]
** (Postgrex.Error) ERROR 42601 (syntax_error): syntax error at or near ""
(ecto) lib/ecto/adapters/sql.ex:436: Ecto.Adapters.SQL.execute_and_cache/7
(ecto) lib/ecto/repo/queryable.ex:130: Ecto.Repo.Queryable.execute/5
(ecto) lib/ecto/repo/queryable.ex:35: Ecto.Repo.Queryable.all/4
查询失败的部分(我在 SQL 客户端中尝试了相同的查询)是:
(now() AT TIME ZONE l1."time_zone")::date - interval )
它不喜欢那里的 </code> 部分。无法将值插入到这种查询中吗?</p>
<p>我尝试在 SQL 客户端中使用 single-quotes,但出现了同样的错误。我尝试了以下方法:</p>
<pre><code>SELECT c0."id" FROM "children" AS c0 INNER JOIN "programs" AS p2 ON p2."id" = c0."program_id" INNER JOIN "locations" AS l1 ON l1."id" = p2."location_id" WHERE (c0."birthday" <= (now() AT TIME ZONE l1."time_zone")::date - interval ) ['2 months']
如有任何帮助,我们将不胜感激!
我刚才确实需要这样做,但最终使用了可以将间隔与 </code> 相乘的事实。</p>
<pre><code>postgres=# select interval '1 year' - interval '1 month' * 5;
?column?
----------
7 mons
(1 row)
所以,这应该有效:
query = from ch in Child,
join: loc in assoc(ch, :location),
where: ch.birthday <= fragment("(now() AT TIME ZONE ?)::date - interval '1 month' * ?", loc.time_zone, 2)
Repo.all(query)
我想创建一个 Ecto 查询,按年龄(即 "minimum age (months) -> maximum age (months)".
过滤children
table 中的记录
一种简单的方法是使用 Ecto date_add
功能:
from c in Child, where: c.birthday >
datetime_add(^Ecto.DateTime.utc, -1, "month")
问题是并非所有 children 都在同一时区,当然也不全都在 Etc/UTC
。这个查询会非常接近,但不是准确的(有些会在一天后关闭)。
我一直在尝试使用 PostgreSQL 的 interval
功能来使这个查询工作。我可以使用 SQL 客户端让它工作,但是当我尝试在片段中插入值时遇到了插入问题。
这有效(child 的时区来源于它的 location
关联):
query = from ch in Child,
join: loc in assoc(ch, :location),
where: ch.birthday <= fragment("(now() AT TIME ZONE ?)::date - interval '2 months'", loc.time_zone)
Repo.all(query)
请注意,我在 '2 months'
间隔内 hard-coded。
我认为这行得通,但不行:
query = from ch in Child,
join: loc in assoc(ch, :location),
where: ch.birthday <= fragment("(now() AT TIME ZONE ?)::date - interval ?", loc.time_zone, ^"2 months")
Repo.all(query)
请注意,我正在尝试使用 Ecto 的查询插值将 '2 months'
值引入查询。
错误如下:
[debug] QUERY ERROR source="children" db=1.7ms queue=0.1ms
SELECT c0."id", (... other properties) FROM "children" AS c0 INNER JOIN "programs" AS p2 ON p2."id" = c0."program_id" INNER JOIN "locations" AS l1 ON l1."id" = p2."location_id" WHERE (c0."birthday" <= (now() AT TIME ZONE l1."time_zone")::date - interval ) ["2 months"]
** (Postgrex.Error) ERROR 42601 (syntax_error): syntax error at or near ""
(ecto) lib/ecto/adapters/sql.ex:436: Ecto.Adapters.SQL.execute_and_cache/7
(ecto) lib/ecto/repo/queryable.ex:130: Ecto.Repo.Queryable.execute/5
(ecto) lib/ecto/repo/queryable.ex:35: Ecto.Repo.Queryable.all/4
查询失败的部分(我在 SQL 客户端中尝试了相同的查询)是:
(now() AT TIME ZONE l1."time_zone")::date - interval )
它不喜欢那里的 </code> 部分。无法将值插入到这种查询中吗?</p>
<p>我尝试在 SQL 客户端中使用 single-quotes,但出现了同样的错误。我尝试了以下方法:</p>
<pre><code>SELECT c0."id" FROM "children" AS c0 INNER JOIN "programs" AS p2 ON p2."id" = c0."program_id" INNER JOIN "locations" AS l1 ON l1."id" = p2."location_id" WHERE (c0."birthday" <= (now() AT TIME ZONE l1."time_zone")::date - interval ) ['2 months']
如有任何帮助,我们将不胜感激!
我刚才确实需要这样做,但最终使用了可以将间隔与 </code> 相乘的事实。</p>
<pre><code>postgres=# select interval '1 year' - interval '1 month' * 5;
?column?
----------
7 mons
(1 row)
所以,这应该有效:
query = from ch in Child,
join: loc in assoc(ch, :location),
where: ch.birthday <= fragment("(now() AT TIME ZONE ?)::date - interval '1 month' * ?", loc.time_zone, 2)
Repo.all(query)