2010-06-16 2 views
2

Чтобы исправить ошибку, мне нужно выполнить итерацию по всем строкам таблицы, обновив кешированный счетчик детей до его реального значения. Структура вещей в таблице формирует дерево.Обновление кэшированных подсчетов в MySQL

В рельсах, следующий делает то, что я хочу:

Thing.all.each do |th| 
    Thing.connection.update(
    " 
     UPDATE #{Thing.quoted_table_name} 
     SET children_count = #{th.children.count} 
     WHERE id = #{th.id} 
    " 
) 
end 

Есть ли способ сделать это в одном запросе MySQL? Альтернативно, есть ли способ сделать это в нескольких запросах, но в чистом MySQL?

Я хочу что-то вроде

UPDATE table_name 
    SET children_count = (
    SELECT COUNT(*) 
     FROM table_name AS tbl 
     WHERE tbl.parent_id = table_name.id 
) 

кроме выше не работает (я понимаю, почему он не делает).

ответ

0

Возможно, вы получили эту ошибку, не так ли?

ERROR 1093 (HY000): You can't specify target table 'table_name' for update in FROM clause 

Самый простой способ обойти это, вероятно, чтобы выбрать ребенка считается во временную таблицу, а затем присоединиться к этой таблице для обновлений.

Это должно работать, предполагая, что глубина отношения родитель/ребенок всегда 1. На основе вашего первоначального обновления это похоже на безопасное предположение.

Я добавил явное блокирование записи в таблице, чтобы гарантировать, что никакие строки не будут изменены после создания таблицы temp. Вы должны делать это только в том случае, если можете позволить себе заблокировать его на время обновления, что будет зависеть от объема данных.

lock tables table_name write; 

create temporary table temp_child_counts as 
select parent_id, count(*) as child_count 
from table_name 
group by parent_id; 

alter table temp_child_counts add unique key (parent_id); 

update table_name 
inner join temp_child_counts on temp_child_counts.parent_id = table_name.id 
set table_name.child_count = temp_child_counts.child_count; 

unlock tables; 
+0

создание временной таблицы для решения этой проблемы. –

+0

@Steven Почему вы думаете, что это плохая идея? –

+0

, потому что вы могли бы решить это проще и быстрее с помощью подзапроса. –

0

ваше обновление подзаголовка должно работать; давайте попробуем прикасаясь к нему немного:

UPDATE table_name 
    SET children_count = (
    SELECT COUNT(sub_table_name.id) 
     FROM sub_table_name 
     WHERE sub_table_name.parent_id = table_name.id 
) 

Или, если суб-таблица та же таблица:

UPDATE table_name as top_table 
    SET children_count = (
    SELECT COUNT(sub_table.id) 
     FROM (select * from table_name) as sub_table 
     WHERE sub_table.parent_id = top_table.id 
) 

Но это не сверхэффективной я угадал.

+0

Чтобы уточнить, sub_table здесь такая же, как исходная таблица. Это древовидная структура, поэтому parent_id в каждой строке ссылается на идентификатор другой строки в той же таблице. Кроме того, могут быть выбраны подвыборы, относящиеся к вещам за пределами области выбора? В этом случае table_name.id –

+0

да, они могут. Вам просто нужно их псевдонимы по-разному. Я обновил свой ответ еще одним примером выше –

+1

. MySQL не допустит этого: «ERROR 1093 (HY000): вы не можете указать целевую таблицу« top_table »для обновления в разделе FROM. –