LoginSignup
3
3

More than 3 years have passed since last update.

PostgreSQLのjsonbを使うときの落とし穴

Posted at

皆さん、PostgreSQL使ってますか?
オープンソースRDBであれば、ほぼこれ一択。というプロダクトですよね。

と、まずはじめに MySQL 派に喧嘩を売ってみました。ちなみに当方は喧嘩を売られても買いません。

さて、 PostgreSQL にはJSONデータを格納するための json型と jsonb 型の2つがサポートされています。
一般的にはバイナリデータで保存される jsonb 型のほうがパフォーマンスが高いので、通常は jsonb 型を使うと思うのですが、 jsonb 型にはいくつかの落とし穴があります。今回はそれをお伝えします。

jsonbの落とし穴

  1. 同じキーが集約される。
  2. キーが自動的にソートされてしまう。

になります。順番に見てみましょう。

同じキーが集約される。

JSONの事実上の仕様である RFC 8259 によると

When the names within an object are not unique,
the behavior of software that receives such an object is unpredictable.
Many implementations report the last name/value pair only.
Other implementations report an error or fail to parse the object,
and some implementations report all of the name/value pairs, including duplicates.

上記の参考訳

オブジェクト内の名前が一意でなければ、そのようなオブジェクトを受け取るソフトウェアの動作は予測できません。
多くの実装は、最後の名前/値のペアのみを報告します。
その他の実装は、エラーを報告するかオブジェクトの解析に失敗しますが、重複を含むすべての名前/値のペアを報告する実装もあります。

となっており、キーの重複については許容されている。と読めます。

しかしながら、 jsonb 型にストアする場合は、重複キーは集約されます。ちなみにどの値が残されるのかは不定です。現在の実装(PostgreSQL 12)だと後勝ちのようですね。

insert into json_tables (data) values ('{"key": 1, "key": 2, "key": 3}');
select * from json_tables;
    data
------------
 {"key": 3}
(1 row)

これ自体はそういう実装でもまぁそうだよね。で済むんですが、次が厄介です。

キーが自動的にソートされてしまう

なんと jsonb 型はストア時にキーがソートがされてしまいます。

insert into json_tables (data) values ('{ "c": "c", "a": "a", "b": "b"}');
select * from json_tables;
              data
--------------------------------
 {"a": "a", "b": "b", "c": "c"}
(1 rows)

これは jsonb 型ではハッシュの 順番が保持できない。 ということを意味しています。

本来ハッシュの順番は保証されないので冷静に考えれば当然ですが、見た目上の順番があるのでそれが保持されると思いがちです。
表示の順番がなんかおかしい。というバグの原因を探していたら実はこれだった。ということがありました。

特に配列とハッシュの区別が無いPHPプログラマーはその罠に陥りがちかも。
rubyのハッシュも1.9から順番が保証されるようになったので同じ罠に陥るかもしれません。
順番を保持したい場合は配列を使う必要があります。

ちなみに json 型であればこのような問題はありません。これは、入力されたデータをそのままテキストデータとして保持しているからです。
その代わり容量の効率や検索時のパフォーマンスが犠牲になります。

insert into json_tables (data) values ('{"key": 1, "key": 2, "key": 3}');
select * from json_tables ;
              data
--------------------------------
 {"key": 1, "key": 2, "key": 3}
(1 row)

insert into json_tables (data) values ('{"a": "a", "c": "c", "b": "b"}');
select * from json_tables ;
              data
--------------------------------
 {"a": "a", "c": "c", "b": "b"}
(1 rows)

こうやって json 型にするか jsonb 型にするかで悩めるのも PostgreSQl が MySQL に優れているところですね。
それでは良い PostgreSQL ライフを。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3