我有一个Order模型,其中有datetime列 start 和int列 arriving_dur , drop_off_dur 等,它们的持续时间以秒为单位从开始
然后在我的模型中
class Order < ApplicationRecord
def finish_time
self.start + self.arriving_duration + self.drop_off_duration
end
# other def something_time ... end
end
我希望能够做到这一点:
Order.where(finish_time: Time.now..(Time.now+2.hours) )
但是我当然不能,因为没有此类的finish_time。如何获得这样的结果?
我已经阅读了关于SA的4种可能的解决方案:
您是否有解决此问题的想法或“最佳实践”? 谢谢!
答案 0 :(得分:1)
您有不同的选择来实现此行为。
添加其他finish_time
列,并在每次更新/创建时间值时对其进行更新。这可以在rails中(使用before_validation
或after_save
回调)或作为psql触发器来完成。
class Order < ApplicationRecord
before_validation :update_finish_time
private
def update_finish_time
self.finish_time = start_time + arriving_duration.seconds + drop_off_duration.seconds
end
end
当您在整个应用中的许多地方需要finish_time
时,此功能特别有用。它的缺点是您需要使用额外的代码来管理该列,并且它存储您实际上已经拥有的数据。好处是,如果您有很多订单并且需要在其上进行搜索,则可以轻松地在该列上创建索引。
一个选项可能是将完成时间更新作为postgresql触发器而不是在rails中实现。这样做的好处是可以独立于Rails应用程序(例如,当其他源/脚本也访问您的数据库时),但是具有将业务逻辑划分到很多地方(红代码,Postgres代码)的缺点。
您的第二个选择是为查询添加虚拟列。
def orders_within_the_next_2_hours
finishing_orders = Order.select("*, (start_time + (arriving_duration + drop_off_duration) * interval '1 second') AS finish_time")
Order.from("(#{finishing_orders.to_sql}) AS orders").where(finish_time: Time.now..(Time.now+2.hours) )
end
上面的代码为finishing_order
创建了一个SQL查询,该查询是order
表,带有附加的finish_time
列。在第二行中,我们使用finishing_orders
SQL作为FROM子句(“聪明地”别名为orders
,因此rails很高兴)。这样,我们可以像finish_time
一样查询普通列。
SQL是为相对较旧的postgresql版本编写的(我想它适用于9.3+)。如果您使用make_interval
而不是与interval '1 second'
相乘,则SQL可能会更具可读性(但我需要更新的Postgresql版本,我认为是9.4 +)。