使用 Rails 和 Postgres 获取原子计数器(增量)的值
Get value of atomic counter (increment) with Rails and Postgres
我需要自动增加模型计数器并使用它的新值(由 Sidekiq 作业处理)。
目前,我使用
Group.increment_counter :tasks_count, @task.id
在我的模型中自动递增计数器。
但是我还需要它的新值来发送通知,如果计数器有,例如值 50
。有任何想法吗?锁定 table/row 还是有更简单的方法?
编辑/解决方案
基于mu is too short的答案和Rails的update_counters
方法,我实现了一个实例方法(用PostgreSQL测试)。
def self.increment_counter_and_return_value(counter_name, id)
quoted_column = connection.quote_column_name(counter_name)
quoted_table = connection.quote_table_name(table_name)
quoted_primary_key = connection.quote_column_name(primary_key)
quoted_primary_key_value = connection.quote(id)
sql = "UPDATE #{quoted_table} SET #{quoted_column} = COALESCE(#{quoted_column}, 0) + 1 WHERE #{quoted_table}.#{quoted_primary_key} = #{quoted_primary_key_value} RETURNING #{quoted_column}"
connection.select_value(sql).to_i
end
像这样使用它:
Group.increment_counter_and_return_value(:tasks_count, @task.id)
它使用 RETURNING
在同一查询中获取新值。
您的 Group.increment_counter
调用将 SQL 像这样发送到数据库:
update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = X
其中 X
是 @task.id
。 SQL 获取新 tasks_counter
值的方法是包含一个 RETURNING 子句:
update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = X
returning tasks_count
我不知道有什么方便的 Railsy 方法可以将 SQL 到数据库。通常的 Rails 方法是要么做一堆锁定并重新加载 @task
要么跳过锁定并希望最好:
Group.increment_counter :tasks_count, @task.id
@task.reload
# and now look at @task.tasks_count to get the new value
不过您可以像这样使用 RETURNING:
new_count = Group.connection.execute(%Q{
update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = #{Group.connection.quote(@task.id)}
returning tasks_count
}).first['tasks_count'].to_i
你可能想把那些乱七八糟的东西隐藏在 Group
的方法后面,这样你就可以这样说:
n = Group.increment_tasks_count_for(@task)
# or
n = @task.increment_tasks_count
我需要自动增加模型计数器并使用它的新值(由 Sidekiq 作业处理)。
目前,我使用
Group.increment_counter :tasks_count, @task.id
在我的模型中自动递增计数器。
但是我还需要它的新值来发送通知,如果计数器有,例如值 50
。有任何想法吗?锁定 table/row 还是有更简单的方法?
编辑/解决方案
基于mu is too short的答案和Rails的update_counters
方法,我实现了一个实例方法(用PostgreSQL测试)。
def self.increment_counter_and_return_value(counter_name, id)
quoted_column = connection.quote_column_name(counter_name)
quoted_table = connection.quote_table_name(table_name)
quoted_primary_key = connection.quote_column_name(primary_key)
quoted_primary_key_value = connection.quote(id)
sql = "UPDATE #{quoted_table} SET #{quoted_column} = COALESCE(#{quoted_column}, 0) + 1 WHERE #{quoted_table}.#{quoted_primary_key} = #{quoted_primary_key_value} RETURNING #{quoted_column}"
connection.select_value(sql).to_i
end
像这样使用它:
Group.increment_counter_and_return_value(:tasks_count, @task.id)
它使用 RETURNING
在同一查询中获取新值。
您的 Group.increment_counter
调用将 SQL 像这样发送到数据库:
update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = X
其中 X
是 @task.id
。 SQL 获取新 tasks_counter
值的方法是包含一个 RETURNING 子句:
update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = X
returning tasks_count
我不知道有什么方便的 Railsy 方法可以将 SQL 到数据库。通常的 Rails 方法是要么做一堆锁定并重新加载 @task
要么跳过锁定并希望最好:
Group.increment_counter :tasks_count, @task.id
@task.reload
# and now look at @task.tasks_count to get the new value
不过您可以像这样使用 RETURNING:
new_count = Group.connection.execute(%Q{
update groups
set tasks_count = coalesce(tasks_counter, 0) + 1
where id = #{Group.connection.quote(@task.id)}
returning tasks_count
}).first['tasks_count'].to_i
你可能想把那些乱七八糟的东西隐藏在 Group
的方法后面,这样你就可以这样说:
n = Group.increment_tasks_count_for(@task)
# or
n = @task.increment_tasks_count