2015-10-06 2 views
0

Мне нужно добавить кэш-счетчик к базе данных, содержащей около 4 миллионов строк. Обычный способ, который я сделал бы это, заключается в миграции следующим образом:Обновление кэша счетчика Postgres с чистым SQL

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?

ответ

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

При правильном 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; 
+0

Кажется, вы заменили запрос на новый в течение льготного периода. –

Смежные вопросы