【问题标题】:Use Ecto to generate_series in postgres and also retrieve Null-values as “0”使用 Ecto 在 postgres 中生成_series 并将 Null 值检索为“0”
【发布时间】:2017-01-26 04:11:39
【问题描述】:

我想显示一系列日期(统计数据),例如:

Dates        | Count
--------------------
"2016-09-01" | 0
"2016-09-02" | 0
"2016-09-03" | 0
"2016-09-04" | 0
"2016-09-05" | 0
"2016-09-06" | 12
"2016-09-07" | 9
"2016-09-08" | 0
"2016-09-09" | 90

原始 SQL 查询:

select date(d) as day, count(clicks.id)
  from generate_series(
    current_date - interval '13 day', 
    current_date, 
    '1 day'
  ) d 
left join clicks on date(clicks.inserted_at) = d and clicks.link_id = 15
group by day order by day;

我很想在没有原始 SQL 的情况下将它与 Ecto 一起使用

我发现的最接近的是:

query = from c in Like
  where: c.link_id == ^link.id

...

from( c in query,
  join: day in fragment("select generate_series(current_date - interval '10 day', current_date, '1 day')::date AS d "),
  on: fragment("date(?) = d", field(c, :inserted_at)),
  group_by: fragment("day"),
  order_by: fragment("day"),
  select: %{
    day: fragment("date(d) as day"),
    count: count(c.id)
  }
)

但它只返回表格中显示的日期,而不是整个范围(即显示 0 表示当天没有类似的日期)

【问题讨论】:

    标签: postgresql elixir ecto postgrex


    【解决方案1】:

    您可以使用 right_join,因为您将在 Ecto 查询中使用生成的日期加入 clicks,而不是像在原始 SQL 中那样使用clicks 生成日期。

    测试:

    alias MyApp.{Click, Repo}
    import Ecto.Query
    
    from(c in Click,
      order_by: c.inserted_at,
      select: fragment("date(?) as day", c.inserted_at)
    )
    |> Repo.all
    |> IO.inspect
    
    from(c in Click,
      right_join: day in fragment("select generate_series(current_date - interval '10 day', current_date, '1 day')::date AS d "),
      on: fragment("date(?) = d", field(c, :inserted_at)) and c.link_id == 15,
      group_by: fragment("day"),
      order_by: fragment("day"),
      select: %{
        day: fragment("date(d) as day"),
        count: count(c.id)
      }
    )
    |> Repo.all
    |> IO.inspect
    

    输出:

    [debug] QUERY OK source="clicks" db=1.9ms
    SELECT date(c0."inserted_at") as day FROM "clicks" AS c0 ORDER BY c0."inserted_at" []
    [{2016, 9, 12}, {2016, 9, 13}, {2016, 9, 13}, {2016, 9, 13}, {2016, 9, 13},
     {2016, 9, 15}, {2016, 9, 17}, {2016, 9, 17}]
    [debug] QUERY OK source="clicks" db=1.8ms
    SELECT date(d) as day, count(c0."id") FROM "clicks" AS c0 RIGHT OUTER JOIN (select generate_series(current_date - interval '10 day', current_date, '1 day')::date AS d ) AS f1 ON date(c0."inserted_at") = d GROUP BY day ORDER BY day []
    [%{count: 0, day: {2016, 9, 8}}, %{count: 0, day: {2016, 9, 9}},
     %{count: 0, day: {2016, 9, 10}}, %{count: 0, day: {2016, 9, 11}},
     %{count: 1, day: {2016, 9, 12}}, %{count: 4, day: {2016, 9, 13}},
     %{count: 0, day: {2016, 9, 14}}, %{count: 1, day: {2016, 9, 15}},
     %{count: 0, day: {2016, 9, 16}}, %{count: 2, day: {2016, 9, 17}},
     %{count: 0, day: {2016, 9, 18}}]
    

    编辑:您可能还想更多地利用 Ecto 强大的查询能力,减少对 asfragment 的依赖。以下查询生成相同的输出:

    from(c in Click,
      right_join: day in fragment("select generate_series(current_date - interval '10 day', current_date, '1 day')::date AS d"),
      on: day.d == fragment("date(?)", c.inserted_at) and c.link_id == 15,
      group_by: day.d,
      order_by: day.d,
      select: %{
        day: fragment("date(?)", day.d),
        count: count(c.id)
      }
    )
    

    【讨论】:

    • 哦,非常感谢@Dogbert 的解决方案,它非常有效,并且解释:)
    • 非常感谢你,这太棒了!
    猜你喜欢
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多