Posted at

railsモデルを削除して新しくmodelを追加したらIDが飛ばされることなく削除したモデルのIDが設定されてしまった。

More than 5 years have passed since last update.

Category has_many goodsのような関連のデータでCategoryモデルを削除し、新規に作成した場合、削除したIDの次の数値がIDとなるのが期待される挙動です。(例 ID=23のモデルを削除したら次に作成されるモデルのIDは24)

しかし特定の状況下ではそうならずに削除したIDが新規に作成したモデルに割り振られる事があるようです。(例 ID=23のモデルを削除したら次に作成されるモデルのIDに23が割り振られる)

こうなると思わぬ関連付けされてしまうので困ります。

> Category.find(23).destroy

Category Load (0.7ms) SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 23 LIMIT 1
(0.1ms) BEGIN
SQL (0.3ms) DELETE FROM `categories` WHERE `categories`.`id` = 23

下に自分が起きたとき原因だろうと思われるところを上げてみます。



  1. railsはmysqlを使う際InnoDBをデフォルトで使う

    Ruby on Rails : migration 機能リファレンス


    なお、バックエンドデータベースとして MySQL を使用している場合、:options を指定しないと "ENGINE=InnoDB" がデフォルトで指定されます。




  2. mysqlのInnoDBは外部key制約で参照されているtableのデータを削除するときはTRUNCATEが使用される。

    12.2.9. TRUNCATE 構文



もしテーブルを参照する外部キー制約があれば、InnoDB テーブルに対しては、TRUNCATE TABLE が DELETE にマップされ、そうでなければ、高速切断(テーブルのドロップと再作成)が利用されます。外部キー制約の有無に関わらず、AUTO_INCREMENT カウンタが TRUNCATE TABLE によってリセットされます。



  1. テーブルでTRUNCATEを使用するとauto incrementがリセットされる。

メッチャ役に立つauto_incrementの話のスライド21~23ページ目を参考にした。


21. テーブルをTRUNCATEするとどうなる?(シーケンスが12まで振られてる状態で)TRUNCATE TABLE tbl;INSERT INTOtbl(user_name)VALUES(‘userXX’);

22. シーケンスが巻き戻った

23. DELETEでは巻き戻らないが、 TRUNCATEだと巻き戻る


上記の3つの条件が同時に起こっているとオートインクリメントの挙動が期待されていないものになるようです。

対策としてはrailsのhas_manyにはdependent オプションがあるのでそれを設定しとくべきだと思います。

設定すると親モデルを削除した際に子モデルも同時に削除されるというものです。

http://guides.rubyonrails.org/association_basics.html#options-for-belongs-to-dependent

railsでhas_manyで関連したモデルのデータを一気に消すとき