我需要向具有大约400万行的数据库添加计数器缓存。我这样做的正常方法就是这样的迁移:
class AddClicksCounterCacheToPosts < ActiveRecord::Migration
def change
add_column :posts, :clicks_count, :integer
Post.find_each { |post| Post.reset_counters(post.id, :clicks) }
end
end
然而这太慢了。 I came across a way在纯SQL中执行此操作,但看起来它是为MySQL编写的,我似乎无法让它为Postgres工作。这是我正在尝试的:
class AddClicksCounterCacheToPosts < ActiveRecord::Migration
def change
add_column :posts, :clicks_count, :integer
execute <<-eos
update posts, (select
id as post_id, coalesce(count, 0) as count
from posts left join
(select post_id, count(id) as count from clicks group by post_id) as count_table
on
posts.id = count_table.post_id) as count_table
set
posts.clicks_count = count_table.count
where
posts.id = count_table.post_id
eos
end
end
这是我得到的错误:
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near ","
我很确定在postgres中允许使用逗号,但说实话,我不会写太多原始的postgres,所以我不确定。
知道如何将其转换为Postgres吗?
答案 0 :(得分:1)
从MySQL到Postgres的自动翻译(无需评估原始查询的正确性):
update posts
set posts.clicks_count = count_table.count
from (
select id as post_id, coalesce(count, 0) as count
from posts
left join (
select post_id, count(id) as count
from clicks group by post_id) as count_table
on posts.id = count_table.post_id) as count_table
where
posts.id = count_table.post_id;
答案 1 :(得分:1)
使用适当的UPDATE
syntax,在Postgres中看起来就像这样:
UPDATE posts p
SET clicks_count = ct.ct
FROM (
SELECT po.id, COALESCE(c.ct, 0) AS ct
FROM posts po
LEFT JOIN (
SELECT post_id, count(*) AS ct
FROM clicks
GROUP BY 1
) c ON c.post_id = po.id
) ct
WHERE p.id = ct.id
AND p.clicks_count <> ct.ct; -- avoid empty update
我添加了另一个条件AND p.clicks_count <> ct.ct
以避免空更新。详细说明:
改为运行这两个查询可能会更快:
UPDATE posts p
SET clicks_count = p.ct
FROM (
SELECT post_id, count(*) AS ct
FROM clicks
GROUP BY 1
) ct
WHERE p.id = p.category_id
AND clicks_count <> p.ct;
UPDATE posts p
SET clicks_count = 0
WHERE NOT EXISTS (SELECT 1 FROM clicks WHERE post_id = p.id)
AND p.clicks_count <> 0;