Rails Postgres查询项目相对于其他人的位置?

时间:2018-03-16 20:32:24

标签: ruby-on-rails postgresql

我正在构建针对Amazon's SQS Standard Queue的模型,该模型可以不按顺序发送更新。

我的目标是正确订购它们。

我很想将队列中的所有数据复制到我的数据库中。

表示例 - 假设我提取了一些消息并处理它们

id | published_at | run_at | payload
1  | 1:11pm       | nil    | ...
2  | 1:12pm       | nil    | ...
3  | 1:13pm       | nil    | ...
4  | 1:14pm       | nil    | ...
5  | 1:15pm       | nil    | ...

然后我再拿一些,我们可以看到一些奇怪的消息现在已经过时了。

id | published_at | run_at | payload
1  | 1:11pm       | 1:15   | ...
2  | 1:12pm       | 1:15   | ...
3  | 1:13pm       | 1:15   | ...
4  | 1:14pm       | 1:15   | ...
5  | 1:15pm       | 1:15   | ...
6  | 1:13pm       | nil    | ...
7  | 1:14pm       | nil   | ...
8  | 1:16pm       | nil   | ...

如果我要通过published_at订购,您可以看到需要从ID=6开始重新处理队列,以确保按顺序处理消息。

id | published_at | run_at | payload
1  | 1:11pm       | 1:15   | ...
2  | 1:12pm       | 1:15   | ...
3  | 1:13pm       | 1:15   | ...
6  | 1:13pm       | nil    | ...
4  | 1:14pm       | 1:15   | ...
7  | 1:14pm       | nil   | ...
5  | 1:15pm       | 1:15   | ...
8  | 1:16pm       | nil   | ...

处理数据准确有价值,处理两次的问题很少,因此重新运行不是问题。

我最好奇怪如何最好地找到尚未运行的最旧项目,并从那一刻起开始运行。

目前正在做:

#  fetch oldest publish_time that has not been ran
first_publish_time = AnyOfferChange.where(run_at: nil).minimum(:publish_time)

if first_publish_time
  #  start there, and process in ascending order
  AnyOfferChange.order("publish_time DESC").where("publish_time >= ?",first_publish_time).reverse.each(&:process!)
end

感觉非常脆弱,我想取得这个位置并将其作为限制。

limit = AnyOfferChange.where(run_at: nil).order("publish_time ASC").pluck("POSITION SOMETHIN(SOMETHING)").first

if limit > 0
  #  start there, and process in ascending order
  AnyOfferChange.order("publish_time DESC").limit(limit).reverse.each(&:process!)
end

1 个答案:

答案 0 :(得分:0)

以下SQL查询将为您提供最早的 publish_time

AnyOfferChange.where(run_at: nil).minimum(:publish_time)

或者,如果你想要一条记录:

AnyOfferChange.where(run_at: nil).order(publish_time: :asc).first

这会将SQL查询限制为尚未运行的最旧行。

获取所有未从旧到新的记录:

result = AnyOfferChanges.where(run_at: nil).order(publish_time: :asc)
# or
result = AnyOfferChanges.where(run_at: nil).order(:publish_time) # Defaults to :asc

result.each(&:process!) # Process result. See note below for batch info.

获取所有未使用最早的publish_time(未运行)运行的记录:

# See note below to prevent unwanted SQL execution for the statements 
# below when executing in the terminal.

# Create shorthand.
any_offer_changes = AnyOfferChange.arel_table

# Build query parts.
not_ran = AnyOfferChange.where(run_at: nil)
oldest_publish_time = not_ran.select(any_offer_changes[:publish_time].minimum)

# All records that not ran with with the oldest publish time.
result = not_ran.where(publish_time: oldest_publish_time)
result.each(&:process!) # Process result. See note below for batch info.

这将导致使用子查询在一个SQL查询中获取具有最低发布时间的所有记录。

我使用除AnyOfferChange.where(run_at: nil).minimum(:publish_time)以外的其他方式获取最后一部分的最小值的原因。这个查询是否会破坏链并创建多个SQL查询而不是一个。鉴于AnyOfferChange.where(run_at: nil).select(any_offer_changes[:run_at].minimum) where 语句中使用时,链将保持完整。

备注:

不需要的SQL执行

当逐个运行时,这将导致多个查询,因为 #inspect (用于显示结果)将触发SQL执行。在终端中使用;nil跟随每个语句,以防止在构建 #where 链时执行。在脚本中执行时不需要这样做。

使用"批次"

对于大量记录,您可能必须限制结果值。 Rails支持批处理,但他们不尊重给定的订单。为了保持订单,您可以创建自己的批次,尽管可能效率较低。这可以这样做:

result = AnyOfferChange.where(run_at: nil).order(:publish_time).limit(100)
result.each(&:process!) while result.reload.any?

假设您在 #process!中设置 run_at 属性,否则上述操作将导致无限循环。