在ecto中使用generate_series并传递值

时间:2018-07-18 12:10:48

标签: postgresql elixir ecto

我正在尝试在此查询中使用 generate_series 来获取某个时间段内的销售数量,即使没有销售的日子也是如此。我以硬编码的价格使用它,但自合同创建以来,现在我需要获得所有销售额。

到目前为止我所拥有的:

 query =
  from(
    c in Contract,
    join: v in Voucher,
    on: v.contract_id == c.id,
    join: s in Sale,
    on: s.voucher_id == v.id and c.id == ^contract_id,
    right_join:
      day in fragment(
        "select generate_series(current_date - interval '60 day', current_date, '1 day')::date AS d"
      ),
    on: day.d == fragment("date(?)", s.date),
    group_by: day.d,
    select: %{
      number_sales: count(s.id),
      total_value: sum(s.value),
      date: day.d
    },
    order_by: [asc: day.d]
  )

Repo.all(query)
|> Enum.map(fn entry -> Map.put(entry, :date, Date.from_erl!(entry.date)) end)

通过该查询,即使没有销售的日子,我也能获得过去60天的所有销售。

我现在将generate_series('2018-06-01', current_date, '1 day')行更改为  从合同创建之日开始,但出现以下错误:

[debug] QUERY ERROR source="contracts" db=1.0ms
SELECT count(s2."id"), sum(s2."value"), f3."d" FROM "contracts" AS c0 INNER 
JOIN "vouchers" AS v1 ON v1."contract_id" = v1."id" INNER JOIN "sales" AS s2 
ON (s2."voucher_id" = v1."id") AND (c0."id" = $1) RIGHT OUTER JOIN (select 
generate_series(c0."inserted_at", current_date, '1 day')::date AS d) AS f3 ON 
f3."d" = date(s2."date") GROUP BY f3."d" ORDER BY f3."d" [1]
** (Postgrex.Error) ERROR 42P01 (undefined_table): invalid reference to FROM- 
clause entry for table "c0"
(ecto) lib/ecto/adapters/sql.ex:431: Ecto.Adapters.SQL.execute_and_cache/7
(ecto) lib/ecto/repo/queryable.ex:133: Ecto.Repo.Queryable.execute/5
(ecto) lib/ecto/repo/queryable.ex:37: Ecto.Repo.Queryable.all/4
(app) lib/app/contracts/contracts.ex:313: App.Contracts.get_sales_day/1

修改后的代码:

query =
  from(
   c in Contract,
join: v in Voucher,
on: v.contract_id == c.id,
join: s in Sale,
on: s.voucher_id == v.id and c.id == ^contract_id,
right_join:
  day in fragment(
    "select generate_series(?, current_date, '1 day')::date AS d",
    c.inserted_at
  ),
on: day.d == fragment("date(?)", s.date),
group_by: day.d,
select: %{
  number_sales: count(s.id),
  total_value: sum(s.value),
  date: day.d
},
order_by: [asc: day.d]
  )
Repo.all(query)
|> Enum.map(fn entry -> Map.put(entry, :date, Date.from_erl!(entry.date)) end)

2 个答案:

答案 0 :(得分:1)

这应该有效。

static final Function<Function<Double,Double>,Function<Double,Function<Double, Double>>> 
integral = f -> a -> b ->
{
    final double dx=(b-a)/100000;
    return IntStream.range(0, 100000).mapToDouble(i -> f.apply(a+i*dx)*dx).sum();
};

如果您需要更大的灵活性,例如使用public static void main(String args[]) { double result = integral.apply(x->x*x-x+3).apply(-5.0).apply(12.0); System.out.println("Result is " + result); } ,则可以将fragment("SELECT generate_series(?, current_date, '1 day')", type(c.inserted_at, :naive_datetime) ) 更改为timestamptz,将:datetime更改为:string并发送值作为字符串。示例:

?

时间间隔也一样:

?::timestamptz

答案 1 :(得分:0)

根据上面的答案,这是针对日期时间值的“分组”查询的通用解决方案(对于“分组”,datetime值比较困难,因为它们不落在与generate_series匹配的离散点上)。 即使没有事件数据,此解决方案也会生成序列点。如果缺少事件数据,请使用coalesce()函数将nil转换为0。 我正在使用一个名为Event的表,其中的字段为'dt'(日期时间),字段为'value'(整数)。

  startpoint = startpoint = Timex.parse!("2011-04-18 12:00:00.000000Z", "{ISO:Extended}"
  group_span = 20     # group_by interval in minutes
  duration = 60       # in minutes, 24* 60 = 1 day, etc
  query = from(e in Event,
    right_join: minute in fragment(
      "select 
         ? + ( n    || ' minutes')::interval start_time,
         ? + ( n+?  || ' minutes')::interval end_time
      from generate_series(0, ?, ?) n", type(^startpoint, :naive_datetime), type(^startpoint, :naive_datetime), ^group_span, ^duration, ^group_span),
      on: e.dt >= minute.start_time and e.dt < minute.end_time,
    group_by: minute.start_time,
    order_by: [asc: minute.start_time],
    select: [minute.start_time, avg(e.value) ]
  )

结果

  [
    [~N[2011-04-18 12:00:00.000000], 3.0],
    [~N[2011-04-18 12:20:00.000000], 0.0],
    [~N[2011-04-18 12:40:00.000000], 0.0],
    [~N[2011-04-18 13:00:00.000000], 0.0]
  ]