2017-01-10 2 views
1

Ich möchte eine Ecto-Abfrage erstellen, die Datensätze in einer children Tabelle nach ihrem Alter (dh "Mindestalter (Monate) -> Höchstalter (Monate)" filtert.Ecto Query - Daten + Postgres Intervalle + Query Interpolation

Eine einfache Möglichkeit, dies wäre der Ecto date_add Funktion zu tun.

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

das Problem dabei ist, dass nicht alle Kinder auf der gleichen Zeitzone sein wird, und schon gar nicht alle auf Etc/UTC diese Abfrage wäre ziemlich nah, aber nicht vor Ort (einige wären um einen Tag weg)

Ich habe versucht, PostgreSQL interval Funktionalität zu verwenden, um diese Abfrage funktionieren zu lassen. Ich kann es mit einem SQL-Client zum Laufen bringen, aber ich bekomme Interpolationsprobleme, wenn ich versuche, Werte in einem Fragment zu interpolieren.

Dies funktioniert (Zeitzone des Kindes stammen aus es location Vereinigung):

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) 

Bitte beachte, dass ich hartcodiert im '2 months' Intervall haben.

dachte ich, das funktionieren würde, aber tut nicht:

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) 

Bitte beachte, dass ich versuche, Ecto Abfrage Interpolation im '2 months' Wert in die Abfrage bringen zu verwenden.

Der Fehler ist wie folgt:

[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 

Der Teil der Abfrage, die (ich die gleiche Abfrage in einem SQL-Client versucht) versagt ist:

(now() AT TIME ZONE l1."time_zone")::date - interval $1)

Es ist nicht wie der $1 Teil genau dort. Ist es nicht möglich, Werte in diese Art von Abfrage zu interpolieren?

Ich versuchte mit einfachen Anführungszeichen in einem SQL-Client, bekam aber die gleichen Fehler. Ich habe folgendes versucht:

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'] 

Jede Hilfe wäre willkommen!

Antwort

2

Ich musste genau dies vor einer Weile tun und nutzte die Tatsache, dass Sie Intervalle mit $1 multiplizieren können.

postgres=# select interval '1 year' - interval '1 month' * 5; 
?column? 
---------- 
7 mons 
(1 row) 

So sollte diese Arbeit:

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

Nice! Danke - das funktioniert. Rettete den Tag. – Brandon