【问题标题】:Find next birthdays to celebrate寻找下一个生日来庆祝
【发布时间】:2017-01-16 20:16:34
【问题描述】:

我的 phoenixframework 项目中有一个用户模型,其生日属性包含 Timex.Ecto.Date 值。

defmodule MyProject.User do
  use MyProject.Web, :model

  schema "users" do
    field :active, :boolean
    field :birthday, Timex.Ecto.Date
    field :login, :string
    field :email, :string
    field :password, :string, virtual: true
    field :password_hash, :string
    field :name, :string
    field :nickname, :string

    timestamps
  end

  # ... changeset and other code ...
end

现在我尝试找出所有将在未来 30 天内庆祝生日的用户。假设有一个出生日期为 1980-02-01 的用户记录。今天是2017-01-13。

我现在拥有的:

{:ok, date_from} =
  Timex.local
  |> Timex.Ecto.Date.cast

{:ok, date_to} =
  Timex.local
  |> Timex.add(Timex.Duration.from_days(30))
  |> Timex.Ecto.Date.cast

MyProject.Repo(from(u in MyProject.User, where: u.birthday >= ^date_from, where: u.birthday <= ^date_to))

因为年份它不能工作。我如何构建 ecto 查询?

【问题讨论】:

  • 虽然我不知道答案,但向社区询问您正在使用的数据库可能更有用。例如,如果您使用的是 SQL 数据库,请询问带有 SQL 标记的问题。一旦您知道如何进行原始查询,就应该相当直接地将其转换为 ecto。
  • 哦抱歉,我忘了说我使用的是 PostgreSQL 数据库。

标签: postgresql elixir phoenix-framework ecto


【解决方案1】:

在 PostgreSQL 中,我会使用 age 来获取出生日期和现在之间的间隔,将其截断为年份,将其添加到出生日期以获得下一个生日,然后查看它是否在未来 30 天:

postgres=# select current_date;
    date
------------
 2017-01-14
(1 row)

postgres=# select '2000-01-20'::date + date_trunc('year', age('2000-01-20'::date)) + interval '1 year' <= current_date + interval '30 days';
 ?column?
----------
 t
(1 row)

postgres=# select '2000-02-20'::date + date_trunc('year', age('2000-02-20'::date)) + interval '1 year' <= current_date + interval '30 days';
 ?column?
----------
 f
(1 row)

使用 Ecto,这应该看起来像(未经测试):

from(u in User, where: fragment("? + date_trunc('year', age(?)) + interval '1 year' <= current_date + interval '30 days'", u.birthday, u.birthday))

现在将间隔更改为'1 month' 也很简单,PostgreSQL 将正确添加当月的天数,并为您提供生日在 1 个月内而不是 30 天内的用户。

【讨论】:

  • OP:这个答案肯定比我的好。
  • 这是一个非常好的解决方案,如果我按照您的建议使用它,它就会起作用。但是如果我使用另一个参数,只是为了灵活,它会失败:days = 30; from(u in User, where: fragment("? + date_trunc('year', age(?)) + interval '1 year' &lt;= current_date + interval '? days'", u.birthday, u.birthday, ^days)) 这会导致错误:(ArgumentError) parameters must be of length 0 for query %Postgrex.Query
  • @guitarman 试试interval '1 day' * ?。另请查看我几天前写的这个答案:stackoverflow.com/a/41571998/320615
  • 非常感谢您的帮助。
【解决方案2】:

我不知道使用 Ecto 明确执行此操作的任何可能性,但以下原始 SQL 应该适用于 MySQL(仅限WHERE 子句):

WHERE DAY(bd) > DAY(NOW()) AND MONTH(db) = MONTH(NOW()) 
   OR DAY(bd) <= DAY(NOW()) AND MONTH(db) = MOD(MONTH(NOW()), 12) + 1 

对于 PostgreSQL,将 DAY(XXX) 更改为 EXTRACT(DAY FROM XXX)

WHERE EXTRACT(DAY FROM bd) > EXTRACT(DAY FROM NOW()) ....

这些子句可以按原样在Ecto 片段中使用。

【讨论】:

  • 好的,现在我了解了 ecto 查询中的片段。稍后我将与您的答案一起尝试。
  • 该死,我忘了在我的回答中提到片段,但你似乎有能力在字里行间阅读:)
  • 从技术上讲,这将返回未来 1 个月的生日,而不是 30 天 :)
猜你喜欢
  • 2014-05-26
  • 1970-01-01
  • 2019-05-14
  • 2019-12-27
  • 2022-12-02
  • 2012-03-01
  • 1970-01-01
  • 2011-02-19
  • 2021-01-20
相关资源
最近更新 更多