如何在 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.3
和pg 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 "<"
。除了&&
之外,您是否可能必须使用其他语法进行链接?
其实,我自己才想出来的——它是AND
。像这样:Cat.where("cats.created_at >= ? AND cats.created_at < ?", 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)