如何避免在 Rails 中使用 PostgreSQL 的 BETWEEN 时间戳重复?
How to avoid duplicates using PostgreSQL's BETWEEN for timestamps in Rails?
我的 Rails 应用程序中有一个如下所示的查询。本质上,我想获取在昨天 9:30am 和今天 9:30am 之间创建的记录。我打算在每天运行一次的任务中使用此查询。
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
包括 运行ge 边界 (docs):
The BETWEEN predicate simplifies range tests:
a BETWEEN x AND y
is equivalent to
a >= x AND a <= y
Notice that BETWEEN treats the endpoint values as included in the range.
如果上面的代码可能会导致重复,我该如何避免呢?
- 我应该把
this_execution_time
的时间改成 9:29:59
吗?
- 或者是否有更多 g运行 单位因素需要考虑,比如毫秒?
[编辑] 我使用 rails 5.2.3
和 pg 1.1.4
.
您可以这样查询:
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)
不过我不确定这是否会影响性能。
是的,如果将 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)
但是如果你写一些边界条件测试并在那里检查就更好了。
我知道的不多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谓词读作"run date is contained within the range d1 and d2, include d1 but exclude 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;
我的 Rails 应用程序中有一个如下所示的查询。本质上,我想获取在昨天 9:30am 和今天 9:30am 之间创建的记录。我打算在每天运行一次的任务中使用此查询。
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
包括 运行ge 边界 (docs):
The BETWEEN predicate simplifies range tests:
a BETWEEN x AND y
is equivalent to
a >= x AND a <= y
Notice that BETWEEN treats the endpoint values as included in the range.
如果上面的代码可能会导致重复,我该如何避免呢?
- 我应该把
this_execution_time
的时间改成9:29:59
吗? - 或者是否有更多 g运行 单位因素需要考虑,比如毫秒?
[编辑] 我使用 rails 5.2.3
和 pg 1.1.4
.
您可以这样查询:
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)
不过我不确定这是否会影响性能。
是的,如果将 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)
但是如果你写一些边界条件测试并在那里检查就更好了。
我知道的不多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谓词读作"run date is contained within the range d1 and d2, include d1 but exclude 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;