如何在 Rails 中使用 PostgreSQL 的 BETWEEN 来避免重复时间戳?

Posted

技术标签:

【中文标题】如何在 Rails 中使用 PostgreSQL 的 BETWEEN 来避免重复时间戳?【英文标题】:How to avoid duplicates using PostgreSQL's BETWEEN for timestamps in Rails? 【发布时间】:2019-07-24 08:13:15 【问题描述】:

我的 Rails 应用中有一个查询,看起来像这样。本质上,我想获取昨天上午 9:30 到今天上午 9:30 之间创建的记录。我打算在每天运行一次的任务中使用此查询。

last_execution_time = Time.zone.parse("#Time.zone.yesterday.strftime('%Y-%m-%d') 09:30:00")
this_execution_time = Time.zone.parse("#Time.zone.today.strftime('%Y-%m-%d') 09:30:00")

new_cat_records = Cat.where(created_at: last_execution_time..this_execution_time)

但是,我担心在上午 9:30:00 创建的记录会发生什么情况。如果我今天和明天运行此查询,是否两次都包含在内? 我知道 PostgreSQL 的 BETWEEN 包括范围边界(docs):

BETWEEN 谓词简化了范围测试:

a BETWEEN x AND y

等价于

a >= x AND a <= y

请注意,BETWEEN 将端点值视为包含在范围内。

如果上面的代码可能会导致重复,我该如何避免这种情况?

我应该把this_execution_time的时间改成9:29:59吗? 还是需要考虑更精细的因素,例如毫秒?

[编辑]我使用rails 5.2.3pg 1.1.4

【问题讨论】:

您可以编写一个查询,其中 last_execution_time > 9:30(昨天)和 this_execution_time 【参考方案1】:

你可以这样查询:

Cat.where("created_at >= ? AND created_at < ?", last_execution_time, this_execution_time)

Cat.where("created_at > ? AND created_at <= ?", last_execution_time, this_execution_time)

我不确定这是否会影响性能。

【讨论】:

您的解决方案是:PG::SyntaxError: ERROR: syntax error at or near "&lt;"。除了&amp;&amp;之外,您是否可能必须使用其他语法进行链接? 其实,我自己才想出来的——它是AND。像这样:Cat.where("cats.created_at &gt;= ? AND cats.created_at &lt; ?", last_execution_time, this_execution_time) 哦,是的,我的错我会修复它【参考方案2】:

是的,如果您将between 与 09:30:00..09:30:00 一起使用,则边界条件会出现小问题

你可以用毫秒来改变this_execution_time

this_execution_time = Time.zone.parse("#Time.zone.today.strftime('%Y-%m-%d') 09:29:59.999999")

或者你可以使用Arel或者clean sql来写正确的条件:

Cat.where(Cat.arel_table[:created_at].gteq(last_execution_time).and(Cat.arel_table[:created_at].lt(this_execution_time)))
Cat.where("created_at >= ? AND created_at < ?", last_execution_time, this_execution_time)

但是如果你写一些边界条件测试并在那里检查它会更好。

【讨论】:

【参考方案3】:

我不太了解 Rails,读过一些书,但仅此而已,但我确实了解一点 Postgres - 也许它会有所帮助。 Postgres 有一个 intervals 的概念,它允许设置像 BETWEEN 这样的结构,但也允许定义是否包含端点。在这种情况下,包括 start_time 并排除 end_time。下面创建了这样一个区间:

with date_period as 
     ( select current_date + interval '9:30:00' d1
            , current_date + interval '1 day' + interval '9:30:00' d2
     ) 
   , op_dates as 
     ( SELECT generate_series(current_date, current_date+interval '2 day', interval '.5 hours') run_dt)
select  run_dt
  from op_dates
     , date_period
 where 1=1
   and run_dt <@ tsrange(d1, d2, '[)');

将 where 子句中的 AND 谓词解读为“运行日期包含在 d1 和 d2 范围内,包括 d1 但不包括 d2”。您想要的是将该谓词包含在您的 where 中,而不是 between 谓词。您可以将 tsrange 函数更改为 (d1, d2, '(]')。这将排除范围的开头 (d1) 但包括范围的结尾 (d2)

为了比较,我将包含对 BETWEEN 的查询与相同的生成数据;

with date_period as 
     ( select current_date + interval '9:30:00' d1
            , current_date + interval '1 day' + interval '9:30:00' d2
     ) 
   , op_dates as 
     ( SELECT generate_series(current_date, current_date+interval '2 day', interval '.5 hours') run_dt)
select run_dt
  from op_dates
     , date_period
 where 1=1
   and run_dt between d1 and d2; 

【讨论】:

看其他回复,也许试试。如果我的语法正确 "Cat.where("created_at

以上是关于如何在 Rails 中使用 PostgreSQL 的 BETWEEN 来避免重复时间戳?的主要内容,如果未能解决你的问题,请参考以下文章

如何逃脱? (问号)运算符在 Rails 中查询 Postgresql JSONB 类型

如何让我的 Rails 应用程序使用我的 postgresql 数据库?

如何在 Rails 3 的 Postgres 数据库中使用枚举? [关闭]

如何从 SQLite 迁移到 PostgreSQL (Rails)

使用 PostgreSQL 的模式和 Rails 创建多租户应用程序

如何在 Rails 范围内模拟 Postgresql EXCEPT 运算符?