RailsでDBに直接データを保存すると、何回createアクションをしても、idが重複するようになった?
Active Record便利ですよね。テーブルの結合なども簡単にできるし、一度リレーション作っておけば、多少テーブルの仕様が変わっても、変更のための工数はめっちゃ少なくすみます。
レコードの新規作成等もラクラクで、
例えば、Groupモデル(groupsテーブル)があったとして、データを新規作成しようとした場合、SQLであれば、
INSERT INTO groups(id,name) VALUES(1,'所属A')
と書くところを、
Group.create(name:'所属A')
と書くことができます。
でも、開発用にいくつかデータを登録したいという時に、
Group.create(name:'所属A')
Group.create(name:'所属B')
Group.create(name:'所属C')
とか書くのはめんど臭いし、かといってseedでデータを生成するのもなぁ・・・
データに投入できるようなエクセルファイルも持っているし、
CSVとかで簡単にDBにデータを投入できたら良いのに・・・
とか思うことがあると思います。
そんな時に、例えばpostgresqlならpgadminや、mysqlならsequel proなどのツールを利用すると、簡単にCSVからデータをDBに流し込むことが出来ます。
(例)csvのデータ
id | name |
---|---|
1 | 飯塚部 |
2 | 豊本部 |
3 | 角田部 |
4 | 東京本部 |
5 | 03部隊 |
6 | 人力の車輪部 |
じゃあ、これでDBにCSVでデータを流し込んでと・・・
これで、開発の時用のデータが簡単に登録できだぞ!!
よし、create
のデータ新規作成の動きでも確かめるか!とか、思ったら、
Group.create(name:'劇団ふたり部')
RollBack!
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "groups_pkey"
DETAIL: Key (id)=(1) already exists.
#↑ん?
なんで、新規作成が通らないの!?
絶対カラム名とか間違えてないのに。
っていうか、なぜ7行目のはずなのに、idが7で登録しようとするの!?だから、idが重複してるって怒られるんじゃん!!
とかなって困った時には、
**「Railsのidの自動採番機能(シーケンス、シーケンシャル)」**という物に目を向けてみてください。
idの自動採番とは(シーケンス,、シーケンシャル)機能とは
知っての通り、RailsはActive Record
を介して、簡単にDBの操作を行うことができます。
そのActive Record
は、上の例でいうGroup.create(name:'劇団ふたり')
のように、プログラマーがidを意識しなくても、idを採番し、レコードの新規作成が容易にできるようにしてくれているようです。
(これがSQL文だと、きちんとidまで指定しないと、insert文を実行することができませんね)
この自動採番機能ですが、Rails側はどうやって、
次に採番すべきidの番号を把握することが出来るのか!?という所に目を向けてみると、
シーケンシャル(シーケンス)と呼ばれる、「今どの番号まで払い出しているか。」という番号を参照しているようです。
このidのシーケンスですが、ActiveRecord(モデル)
を介して、レコードの作成が行われた時には、プログラマーが意識することなく、自動で番号を上げてくれるのですが、
今回のように、Railsの機能を通さずに、DBへデータを直接登録した時には、自動で番号を上げてくれません。(そりゃそうですが)
つまり、↑の例でいうと、プログラマー側としては、もうidは6まで採番されているので、次は7に進んで欲しいところが、Rails的にはまだidのシーケンスが1であるため、「idが重複するぞ!!」と怒って新規データが保存されないという状況。
なので、以下のように今のidのシーケンス値はこれですよ〜という風に手動でセットしてあげる必要があります。
psql --dbに接続
select setval('groups_id_seq',6)
setval
--------
6
--setval('テーブル名_id_seq',現在振られているidの最大値)という風に設定
ということをすれば、Rails側も次のidは7番だな!
ということが分かり、idの重複もなく、レコードの新規作成が出来るようになります。
ちなみに、このsetvalはpostgresqlの書き方であり、次のようにシーケンスに関する操作を行うことができます。
--現在のシーケンシャルを確認
select currval('groups_id_seq');
--次のシーケンシャルを確認
select nextval('groups_id_seq');
まとめ
- Railsの機能を通さずに、DBにレコードを登録した時は要注意
- idの今の最大値を登録してあげて、Railsが次に採番するidの番号を教えてあげる必要がある
- 可能ならば、データはseedファイル等で用意してあげるとよいと思う
- チーム開発の場合、メンバー全員のデータが必要になるし、idのシーケンシャルも気にしなくていいし