Ecto查询 - 日期+ Postgres间隔+查询插值

时间:2017-01-10 14:49:03

标签: sql postgresql elixir ecto

我想创建一个Ecto查询,按年龄过滤children表中的记录(即“最小年龄(月) - >最大年龄(月)”。

执行此操作的一种简单方法是使用Ecto date_add功能:

from c in Child, where: c.birthday >
                          datetime_add(^Ecto.DateTime.utc, -1, "month")

这个问题是,并非所有的孩子都在同一时区,当然也不是所有孩子都在Etc/UTC。这个查询非常接近,但没有现场(有些人会在一天之内完成)。

我一直在尝试使用PostgreSQL的interval功能来使这个查询工作。我可以使用SQL客户端来使用它,但是当我尝试在片段中插值时,我遇到了插值问题。

这是有效的(孩子的时区来自它的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'间隔内进行了硬编码。

我认为这样可行,但

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 $1) ["2 months"]
** (Postgrex.Error) ERROR 42601 (syntax_error): syntax error at or near "$1"
    (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 $1)

它不喜欢那里的$1部分。是否无法将值插入此类查询中?

我尝试在SQL客户端中使用单引号,但得到了相同的错误。我尝试了以下方法:

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 $1) ['2 months']

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:3)

前一段时间我需要做到这一点,最后使用的事实是你可以将间隔乘以$1

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)