在 postgres 中使用 Ecto 到 generate_series 并将空值检索为“0”
Use Ecto to generate_series in postgres and also retrieve Null-values as “0”
我想显示一系列日期(统计数据),例如:
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;
我想在没有 Raw 的情况下将它与 Ecto 一起使用 SQL
我找到的最接近的是:
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)
}
)
但它 returns 仅显示 table 中显示的日期,而不是整个范围(即显示 0 表示当天没有类似日期的日期)
您可以使用 right_join
,因为您在 Ecto 查询中加入 clicks
和生成的日期,而不是像在原始 [=26] 中那样使用 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 强大的查询能力,减少对 as
和 fragment
的依赖。以下查询生成相同的输出:
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)
}
)
我想显示一系列日期(统计数据),例如:
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;
我想在没有 Raw 的情况下将它与 Ecto 一起使用 SQL
我找到的最接近的是:
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)
}
)
但它 returns 仅显示 table 中显示的日期,而不是整个范围(即显示 0 表示当天没有类似日期的日期)
您可以使用 right_join
,因为您在 Ecto 查询中加入 clicks
和生成的日期,而不是像在原始 [=26] 中那样使用 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 强大的查询能力,减少对 as
和 fragment
的依赖。以下查询生成相同的输出:
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)
}
)