web-dev-qa-db-ja.com

データベースにJSONを格納するのと、各キーに新しい列を用意するのとの比較

私は自分のテーブルにユーザー関連のデータを格納するために次のモデルを実装しています - 私は2つの列を持っています - uid(主キー)とJSON形式でユーザーに関する他のデータを格納するmeta列。

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------

これは、uidnameemailidのように多くの列を持つ、プロパティごとの列1つのモデルよりも優れた方法(パフォーマンス上、設計上)です。

最初のモデルについて私が気に入っているのは、できるだけ多くのフィールドを追加できることです。制限はありません。

また、最初のモデルを実装したので、今は思っていませんでした。 'foo'のような名前を持つすべてのユーザーを取得したいのですが。

Question - JSONとかcolumn-per-fieldを使ってデータベースにユーザー関連のデータを保存する(フィールドの数は固定されていないことに注意して)より良い方法はどれですか?また、最初のモデルが実装されている場合、上記のようにデータベースに問い合わせる方法は?両方のモデルを使用する必要があります。クエリによって検索される可能性のあるすべてのデータを別の行に格納し、他のデータをJSONに格納することによって(別の行です)?


更新

検索に必要な列が多すぎることはないので、両方のモデルを使用するのが賢明でしょうか。検索する必要があるデータの列ごとのキーと他の人のためのJSON(同じMySQLデータベース内)?

165
ShuklaSannidhya

2017年6月4日更新

この質問/回答が人気を博していることを考えると、私はそれが更新の価値があると考えました。

この質問が最初に投稿されたとき、MySQLはJSONデータ型をサポートしておらず、PostgreSQLでのサポートはまだ始まったばかりでした。 5.7以降、MySQL 現在はJSONデータ型をサポート (バイナリストレージ形式)、そし​​てPostgreSQL JSONB はかなり成熟しています。どちらの製品も、JSONオブジェクトの特定のキーのインデックス作成のサポートなど、任意のドキュメントを格納できる高性能なJSON型を提供します。

ただし、リレーショナルデータベースを使用している場合は、デフォルトの設定を[値ごとの列]にしてください。リレーショナルデータベースは、その中のデータがかなりよく正規化されているという仮定のもとに構築されています。クエリプランナは、JSONドキュメントのキーを見るときよりもカラムを見るときの方が最適化情報が優れています。外部キーは列間に作成できます(ただし、JSON文書のキー間には作成できません)。重要:スキーマの大部分がJSONの使用を正当化するのに十分なほど変動しやすい場合は、少なくともリレーショナルデータベースが正しい選択であるかどうかを検討する必要があります。

とは言っても、完全にリレーショナルまたはドキュメント指向のアプリケーションはほとんどありません。ほとんどのアプリケーションには、両方が混在しています。私が個人的にリレーショナルデータベースでJSONが有用であると思ったいくつかの例を示します。

  • 連絡先の電子メールアドレスと電話番号をJSON配列に値として格納すると、複数の別々のテーブルよりも管理がはるかに簡単になります。

  • 任意のキー/値のユーザー設定を保存する(値はブール値、テキスト、数値のいずれかになり、データ型ごとに別々の列を作成したくない場合)

  • スキーマが定義されていない構成データを保管する(Zapier、またはIFTTTを構築していて、統合ごとに構成データを保管する必要がある場合)

他にもあると思いますが、これらはほんの数例です。

元の答え

(任意のドキュメントサイズの制限を除いて)制限なしで必要なだけフィールドを追加できるようにしたい場合は、MongoDBなどのNoSQLソリューションを検討してください。

リレーショナルデータベースの場合:値ごとに1つの列を使用します。 JSON BLOBを列に入れると、実質的にクエリを実行できなくなります(実際に機能するクエリが見つかった場合は非常に遅くなります)。

リレーショナルデータベースは、インデックス作成時にデータ型を利用し、正規化構造で実装することを目的としています。

ちなみに、これはJSONをリレーショナルデータベースに格納してはいけないという意味ではありません。真のメタデータを追加している場合、またはJSONが照会する必要がなく、表示にのみ使用される情報を記述している場合は、次のようになります。すべてのデータポイントに対して別々の列を作成するには、やり過ぎます。

165
Colin M

ほとんどのものがそうであるように、それは「依存します」。データを列またはJSONに格納するのは、それ自体が正しいこと、間違っていること、正しくないこと、悪いことではありません。それはあなたが後でそれをどうする必要があるかによって異なります。このデータにアクセスするためのあなたの予想される方法は何ですか?他のデータを相互参照する必要がありますか?

技術的なトレードオフについては、他の人がかなりよく答えています。

あなたのアプリや機能は時間の経過とともに進化し、このデータ保存の決定がチームにどのような影響を与えるかについて議論した人はあまりいません。

JSONを使用することの誘惑の1つはスキーマの移行を避け、チームが懲戒処分を受けていない場合は、別のキーと値のペアをJSONフィールドに追加するのは非常に簡単です。移行はありません。目的を覚えている人はいません。検証はありません。

私のチームはpostgresの伝統的なコラムと一緒にJSONを使っていましたが、最初はスライスされたパン以来最高のものでした。 JSONは魅力的で強力でした。ある日、柔軟性が犠牲になることに気付いたのですが、それが突然本当の問題点になりました。時にはその点がすぐに忍び寄ってから変更するのが難しくなります。なぜなら、この設計上の決定の上に他にもたくさんのことが組み込まれているからです。

残念ながら、新しい機能を追加し、JSONのデータを使用すると、従来のコラムにとどまっていた場合に追加されたものよりも複雑に見えるクエリが発生しました。そこで、結合したり値を比較したりできるように、特定のキー値を列に戻して釣り始めました。悪いアイデア。今我々は重複していました。新しい開発者が参加して混乱しますか?私が節約しておくべき価値はどれですか? JSONのものかそれとも列?

JSONフィールドは、これとそれのほんの一部のためのジャンク引き出しになりました。データベースレベルでのデータ検証、ドキュメント間の一貫性または整合性はありません。そのため、従来のコラムから型や制約のチェックを難しくするのではなく、すべての責任をアプリに取り入れました。

振り返ってみると、JSONによって、私たちは非常に迅速に反復し、何か問題を解決することができました。よかった。しかし、一定のチームサイズに到達した後は、柔軟性があるため、長い技術的な借金を抱えて、その後の機能の進化の進行を遅らせることができました。慎重に使用してください。

あなたのデータの性質が何であるかについて長くそして懸命に考えなさい。それがあなたのアプリの基盤です。データはどのように使用されますか。そしてそれはどのように変化する可能性がありますか?

52
Homan

ただそこに投げ入れてください、しかしWordPressはこの種のもののための構造を持っています(少なくともWordPressは私がそれを観察した最初の場所でした、それはおそらく他の所で起きました)。

無制限のキーを使用でき、JSON BLOBを使用するよりも検索は高速ですが、一部のNoSQLソリューションほど高速ではありません。

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

EDIT

履歴/複数キーの保存用

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

そしてこのようなものを通して問い合わせます:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc
27
Adam

アプローチの欠点はまさにあなたが述べたものです。

毎回あなたはそれに対してテキスト検索を実行する必要があるのでそれは物事を見つけることを非常に遅くします。

列ごとの値は、代わりに文字列全体と一致します。

あなたのアプローチ(JSONベースのデータ)はあなたが検索する必要のないデータには大丈夫です、そしてあなたの通常のデータと共に表示する必要があるだけです。

編集:わかりやすくするために、上記は古典的なリレーショナルデータベースの場合です。 NoSQLは内部的にJSONを使用しており、それが望ましい振る舞いであればおそらくより良い選択肢です。

13

基本的に、最初に使用しているモデルはドキュメントベースのストレージと呼ばれます。あなたは人気のあるMongoDBやCouchDBのようなNoSQLドキュメントベースのデータベースを見てみるべきです。基本的に、ドキュメントベースのdbでは、データをjsonファイルに格納してから、これらのjsonファイルに対してクエリを実行できます。

2番目のモデルは、一般的なリレーショナルデータベース構造です。

もしあなたがMySqlのようなリレーショナルデータベースを使いたいのであれば、私はあなたにセカンドモデルだけを使うように勧めます。 最初のモデルのようにMySqlを使用してデータを保存する意味はありません

2番目の質問に答えるには、最初のモデルを使用している場合は 'foo'のように名前を照会する方法はありません

8
Girish

リレーショナルモデルを使うかどうかは、あなたが主に躊躇しているようです。

現状では、あなたの例はリレーショナルモデルに合理的によく適合しますが、このモデルを進化させる必要があるときに問題は当然起こるかもしれません。

メインエンティティ(ユーザー)に対して1つ(またはいくつかの所定のレベル)の属性しかない場合でも、リレーショナルデータベースでエンティティ属性値(EAV)モデルを使用できます。 (これにも長所と短所があります。)

アプリケーションを使用して検索する構造化された値が少なくなると予想される場合は、ここではMySQLが最善の選択ではないかもしれません。

あなたがPostgreSQLを使用していたなら、あなたは潜在的に両方の長所を得ることができます。 (これ本当にはここのデータの実際の構造に依存します。MySQLも必ずしも間違った選択ではありません、そしてNoSQLオプションは興味があるかもしれません、私はちょうど代替案を提案する。)

確かに、PostgreSQLは(不変の)関数(これは私の知る限りでは不可能な関数)にインデックスを作成することができ、最近のバージョンでは 直接JSONデータにPLV8を使う 関心のある要素。これにより、そのデータを検索するときのクエリの速度が向上します。

編集:

検索に必要な列が多すぎることはないので、両方のモデルを使用するのが賢明でしょうか。検索する必要があるデータの列ごとのキーと他の人のためのJSON(同じMySQLデータベース内)?

2つのモデルを混在させることは必ずしも間違っているわけではありません(余分なスペースは無視できると仮定します)が、2つのデータセットが同期していることを確認しないと問題が発生する可能性があります。 。

これを実現するための良い方法は、更新または挿入が行われるたびにデータベースサーバ内でストアドプロシージャを実行することによって、トリガに自動更新を実行させることです。私が知っている限りでは、MySQLストアドプロシージャ言語はおそらくあらゆる種類のJSON処理をサポートしていません。 PLV8をサポートするPostgreSQL(そしておそらくもっと柔軟なストアドプロシージャ言語を持つ他のRDBMS)もまたもっと便利であるべきです(トリガを使ってリレーショナルカラムを自動的に更新することは同じ方法でインデックスを更新することと全く同じです)。

4
Bruno

テーブルへの結合にはしばらく時間がかかります。 OLAPのために言うことができます。私は2つのテーブルがある場合は1つはORDERSテーブルであり、他の1つはORDER_DETAILSです。 2つのテーブルを結合する必要がある注文の詳細をすべて取得するには、テーブル内の行数が増加しない場合にクエリが遅くなります。 JSON文字列/ ObjectをそれぞれのORDERSエントリに追加すると、JOINが回避されると思います。レポート生成を追加すると速くなります...

1
Ravindra

あなたがそれらの間で混合しなければならない短い答えは、あなたが連絡先データ、住所、製品などのようにそれらと関係を作るつもりはないというデータのためにJSONを使用してください

1
Ahmedfraije Aa

他の人が指摘したように、クエリは遅くなります。その代わりに、少なくとも '_ID'列をクエリに追加することをお勧めします。

0
Pants

あなたはリレーショナルデータベースに非リレーショナルモデルを当てはめようとしています、私はあなたが MongoDB のようなNoSQLデータベースを使った方が良いと思うでしょう。フィールドの数に制限がないというあなたの要求に合う定義済みのスキーマはありません(典型的なMongoDBコレクションの例を見てください)。 MongoDB documentation をチェックして、あなたがどのようにしてあなたのドキュメントを問い合わせるのかについてのアイデアを得てください。

db.mycollection.find(
    {
      name: 'sann'
    }
)
0
Chris L