AWS / PHP / Python ちょいメモ

amazon web service , PHP, Python を使ったときのメモ。日本語でググってもわからなかった事を中心に。

Django と SQLite/MySQL : 最大値とUNIQUE

前回に引き続き、DBを移行する場合にひっかかりそうなTIPS第2弾。Djangoってよりは、Modelの裏側の意識が必要なケースありますね的な話です。

MySQL使っていて、こんなエラーが出た時には参考になるかもしれません。

Specified key was too long; max key length is 767 bytes (SQLState:S1000)


今回の結論としては、SQLite -> MySQLにマイグレートする場合は、こんなの気をつけましょうって所です。

  • VARCHARの最大値は 255 を使う
  • uniqe_together使ってる時は注意が必要

環境は Django 1.6.x で、settings.py のバックエンドの指定は、次のようにして確認しました。

VARCHARの最大値

ModelでCharField指定したとき、バックエンドがSQLiteの時は 256 が通ったのですが、MySQLの場合は 255 じゃないとダメなので255にしておきましょうというだけの事です。

     key = models.CharField(
-        max_length=256,
+        max_length=255,
         db_index = True,
         unique = True,
         )

マイグレートした時に触るのって、ちょっとしたことでも勇気がいるので、始めっからやれるのが一番ですね(;

後述のUNIQUE INDEXが最大767byteだという話とからんで出て来たのですが、ドキュメントには、そもそも255との記載もありましたので間違いなさそうです(なんで256が使えたのだろうか?)。

uniqe_together

Modelの定義の中で、2つのフィールド値を引っ付けてユニークな値として扱う unique_together があります。

    class Meta:
        app_label = 'sample'
        unique_together = (('contents', 'name'))
        get_latest_by = 'date'

便利な機能なのですが、バックエンドがMySQLの場合(かつUTF-8を扱うとき)は、次の制限に引っかかりやすくなります(SQLITEは、制限はもちっと揺るみたいで遭遇しなかった)。

d.hatena.ne.jp

MySQLのUNIQUEなINDEXは、長さ767byteまでしか使えない」という制限は、MySQLUTF-8を使ってると遭遇しやるいと思います。UTF-8MySQL上で、3byte/文字で処理されるので、最大の255文字*3byte = 765byteが最大だよという事になる。

でこれが最大なもんで、unique_togetherした時に、UTF-8 かつ MAXLENGTHまでもたせたCharFieldが含まれてると一発でオーバーするよというのがハマリポイントでした。Django側だけ見てると気づかないですよね。。。気づくのに苦労しました。

僕がとった回避方法は、MAXLENGTHから少し下の値を使うという形をとりました。該当カラムだけASCIIにするという方法もありですね。


たぶん manage.py syncdb の段階で落ちるので manage.py sqlall などで生成されるSQL文とにらめっこする等の対応になるかと思います(僕は、そうしてました)。

参考になったサイト

ここを読んでいて、自分の状況が理解できた(何かが閃いた)


最初から面倒がらずにMySQLを使っていれば、開発途中で避けれる問題という話もあります。はい。