我在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
。
答案 0 :(得分:0)
您可以查询类似:
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)
我不确定这是否会影响性能。
答案 1 :(得分:0)
是的,如果您将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)
但是如果编写一些边界条件测试并在那里进行检查,那就更好了。
答案 2 :(得分:0)
我对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”。您想要的是将该谓词包含在您的位置中,以代替介于谓词之间。您可以将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;