2017-01-10 2 views
1

Я хотел бы создать запрос экто, который фильтрует записи в children таблице по возрасту (т.е. «минимального возраст (месяцы) -> максимальный возраст (месяцы)»Ecto Запрос - Даты + Интервалы Postgres + Query Интерполяция

.

Один простой способ сделать это было бы 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) 

Обратите внимание, что я пытаюсь использовать интерполяцию запрос экто, чтобы привести в значении '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'] 

Любая помощь была бы оценена!

ответ

2

Мне нужно было сделать это совсем недавно, и в итоге я воспользовался тем, что вы можете умножать интервалы с $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) 
+0

Ницца! Спасибо - это работает. Экономит день. – Brandon