データベースへのJSONの保存と各キーの新しい列の作成


211

テーブルにユーザー関連データを格納する次のモデルを実装しています。2つの列uid(主キー)とmeta、ユーザーに関する他のデータをJSON形式で格納する列があります。

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['foo@bar.com','bar@foo.com']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['sann@bar.com','sann@foo.com']}
--------------------------------------------------

これは良い方法である(性能面、デザイン的に)テーブルのような多くの列を持っています1列あたりの財産モデル、よりuidnameemailid

最初のモデルで気に入っているのは、制限はなく、できるだけ多くのフィールドを追加できることです。

また、最初のモデルを実装したので不思議に思っていました。「foo」のような名前を持つすべてのユーザーをフェッチしたいなど、クエリをどのように実行しますか?

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


更新

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


40
すばらしい質問です!しかし、なぜあなたは答えを受け入れなかったのですか?それは他のユーザー(私のような)を助けるでしょう
Sahar Ch。

回答:


197

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ブロブを配置すると、クエリを実行することが事実上不可能になります(実際に機能するクエリを見つけると、非常に遅くなります)。

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

余談ですが、これはリレーショナルデータベースにJSONを保存してはならないということではありません。真のメタデータを追加する場合、またはJSONが照会する必要がなく、表示のみに使用される情報を記述しいる場合は、すべてのデータポイントに個別の列を作成するのはやり過ぎかもしれません。


1
検索を実行する必要がある列が多すぎないので、両方のモデルを使用するのが賢明ですか?検索する必要があるデータの列ごとのキーと他の人のJSON(同じMySQLデータベース内)
ShuklaSannidhya 2013年

3
@Sann頻繁に読み取りまたはクエリするデータには、値ごとに列を使用する必要があります。あなたはそれに基づいて、クエリにそうじゃないにもかかわらず、ので意味がありませんJSONで誰かの名前を入れて、あなたはそれを必要とする可能性が高いですが非常に頻繁に。これは、アプリケーション側での多くの無駄なデコードです。データがJSONとしてより適切に表現されていると本当に感じない限り(そして私を信じてください、そうではないかもしれません)、それに頼るべきではありません。
Colin M

5
virtually impossible to query」 -今日psqlは、あなたがそのjsonb検索およびインデックスすることができます
テッド

1
@ted true。ただし、この回答を書いている時点では、この回答は実際には利用できませんでした。また、この質問は、機能が存在しないMySQLを参照しています。
コリンM

3
@ColinM、はい、私のコメントはあなたの投稿より3年若いことに気づきました。私がそれを残した理由は、それが他の人にとって有益で決定が変わるかもしれないからです。MySQLへの参照用として:本当である、しかし、持っている可能性があり"For relational databases"、あなたの答え= Pに
テッド

68

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

他の人々は技術的なトレードオフが何であるかかなりよく答えました。

アプリと機能が時間の経過とともに進化すること、およびこのデータストレージの決定がチームにどのように影響するかについて、多くの人が議論していません。

JSONを使用する誘惑の1つはスキーマの移行を回避することであるため、チームが統制されていない場合でも、別のキーと値のペアをJSONフィールドに挿入するのは非常に簡単です。それのための移行はありません、それが何のためにあるのか誰も覚えていません。検証はありません。

私のチームはpostgresの従来の列と一緒にJSONを使用しましたが、最初はスライスされたパン以来、それが最高でした。JSONは魅力的で強力でしたが、ある日、柔軟性が犠牲になり、それが突然本当の悩みの種になることに気づきました。時々、その点は非常に速く忍び寄り、この設計上の決定に加えて他の多くのものを構築したため、変更が困難になります。

時間の経過とともに、新しい機能が追加され、JSONのデータを持つことで、従来の列に固執した場合に追加されたクエリよりも複雑なクエリが発生しました。そこで、特定のキー値を列にフィッシングして、結合を作成し、値を比較できるようにしました。悪いアイデア。今、重複がありました。新しい開発者が参加して混乱しますか?私が節約すべき値はどれですか?JSONの1つですか、それとも列ですか?

JSONフィールドは、これとその一部のジャンクドロワーになりました。データベースレベルでのデータ検証、ドキュメント間の整合性や整合性はありません。これにより、従来の列から厳密な型および制約チェックを行う代わりに、すべての責任がアプリに押し込まれました。

振り返ってみると、JSONを使用すると、非常に迅速に反復して、ドアから何かを取り出すことができました。よかった。ただし、特定のチームサイズに達した後は、その柔軟性により、技術的な負債の長いロープに身を投じることができ、その後の機能の進化の進行が遅くなりました。注意して使用してください。

データの性質について詳しく、長く考えてください。これがアプリの基盤です。時間の経過に伴うデータの使用方法。そして、どのように変化する可能性がありますか?


6
「柔軟性があるため、技術的な借金の長い縄にぶら下がることもできました」非常に素晴らしい比喩!
Antoine Gallix

長年の開発とさまざまな人々との仕事の後、この主題について書くべきなら私は同じことを書くでしょう。現在、非常に多くの開発者がいます。彼らの多くは、長年の経験があっても、実際にはレベルアップしません。私たちはすべてをシンプルに保つ必要があります。私にとって常に成功を「フレームワーク」できる2つのことは、コードのスケーラビリティとメンテナンス性です。
JohnnyJaxs

27

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

それは無制限のキーを許可し、JSONブロブを使用するよりも高速ですが、一部のNoSQLソリューションほど高速ではありません。

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

編集

履歴/複数のキーを保存するため

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

1
適切にインデックス付けされたキーでリレーショナルクエリよりもNoSQLソリューションが実際に優れているかどうかを知りたいと思います。私はそれがこのような1レベルの例で多かれ少なかれ同じであるべきだと思います。
ブルーノ

+1。私もそれに気づきました!しかし、それはあなたに(行に関して)巨大なテーブルを与えます。また、たとえばユーザーが名前を変更した場合など、複数の値を格納することできませんが、古い名前も保持したい場合は、JSONタイプのデータモデルが必要になります。
ShuklaSannidhya 2013年

@Sann、古い値をJSONで保持したい場合は、キーの名前も変更する必要があります。EAV(この例はこれです)またはJSONで行うことができます。特に違いはありません。
Bruno

巨大なテーブルを提供しますが、値の重複に関しては、JSONで同じ問題が発生します-同じレベルで重複するキー(たとえば、2つの「名前」キー)を持ち、予測可能な動作を期待することはできません。
アダム

確かに重複したキーを持つことはできませんが、そのキーに関連付けられた配列を持つことができます。emailid質問で挙げた例のキーを確認してください。
ShuklaSannidhya 2013年

13

このアプローチの欠点は、あなたが述べたとおりです。

毎回テキスト検索を行う必要があるため、物を見つけるのが非常に遅くなります。

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

あなたのアプローチ(JSONベースのデータ)は、検索する必要がなく、通常のデータと一緒に表示するだけのデータに適しています。

編集:明確にするために、上記は古典的なリレーショナルデータベースに当てはまります。NoSQLは内部でJSONを使用しており、それが望ましい動作である場合はおそらくより良いオプションです。


1
つまり、両方を使用する必要があります。私が検索する必要があるデータの列ごとのキーと他の人のためのJSON、そうですか?
ShuklaSannidhya 2013年

4
はい。そうすることで、data-per-columnフィールドを検索して必要なパフォーマンスを取得し、必要に応じてコードで使用するJSON blobを取得します。
Nick Andriopoulos 2013年

9

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

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

MySqlのようなリレーショナルデータベースを使用する場合は、2番目のモデルのみを使用することをお勧めします。MySqlを使用して、最初のモデルのようにデータを保存する意味はありません。

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


両方のモデルを使用することは賢明ですか?検索する必要があるデータの列ごとのキーと他の人のJSON(同じデータベース内)
ShuklaSannidhya 2013年

@サン-はは。それがデータの複製です。両方のデータが常に同じであることを確認する必要があります。データのいずれかがいつでも異なる場合でも、データはクリーンではなく、深刻な問題につながる可能性があります。だから、私の答えはノーです
ギリッシュ

しかし、冗長データが小さい場合、冗長性は高価ではありません。たとえば、検索を実行する必要があるフィールドが2つしかない場合は、2つの新しい列を作成し、[多分] JSONデータから削除します[/多分] 。それはコストのかかる複製ではないでしょうか?
ShuklaSannidhya

パフォーマンスを検討している場合、MongoDBとCouchDBは、MySqlよりも高速な読み取りおよび書き込み操作を提供します。これは、ほとんどのユースケースで必要とされないリレーショナルデータベースの多くの機能を提供しないためです。
ギリッシュ

APIからのJSONオブジェクト/コールバックを格納することのメリットはありませんか?たとえば、URLやサムなどのYouTubeのAPIを呼び出す代わりに、JSONオブジェクトについてローカルDB(mysql、liteなど)にクエリを実行できますか?わかりません。特に、キャッシュを作成したり、アプリをより高速に実行したりする場合は、理にかなっています。しかし、私はプロではありません:/
markbratanov 2015年

4

リレーショナルモデルを使用するかどうかを主にためらっているようです。

現状では、この例はリレーショナルモデルに適度に適合しますが、このモデルを進化させる必要がある場合はもちろん問題が発生する可能性があります。

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

アプリケーションを使用して検索する必要がある構造化された値が少なくなることが予想される場合、MySQLはここでの最良の選択ではない可能性があります。

PostgreSQLを使用している場合は、両方の長所を最大限に活用できる可能性があります。(これは本当に、実際のデータの実際の構造に依存します。MySQLも必ずしも間違った選択ではありません。NoSQLオプションに関心がある可能性があります。代替案を提案しています。)

実際、PostgreSQLは(不変の)関数(MySQLは私の知る限りでは不可能)にインデックスを構築でき、最近のバージョンでは、JSONデータでPLV8を直接使用できます、関心のある特定のJSON要素にインデックスを構築できます。そのデータを検索するときのクエリの速度。

編集:

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

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

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


上記で述べたことに加えて、PostgreSQL 9.4以降のJSONBデータ型の演算子を見る価値があります。
Bruno

1

テーブルでのいくつかの時間の結合はオーバーヘッドになります。OLAPについて言うことができます。2つのテーブルがある場合、1つはORDERSテーブルで、もう1つはORDER_DETAILSです。すべての注文の詳細を取得するには、2つのテーブルを結合する必要があります。これにより、テーブルの行が増加しない場合にクエリが遅くなります。たとえば、数百万程度です。左/右結合は内部結合よりも遅すぎます。それぞれのORDERSエントリにJSON文字列/オブジェクトを追加すると、JOINが回避されると思います。追加レポートの生成が高速になります...


1

それらの間で混合する必要がある短い答え、連絡先データ、住所、製品のバリエーションなど、それらと関係を作らないデータにはjsonを使用してください


0

非リレーショナルモデルをリレーショナルデータベースに適合させようとしているのですが、MongoDBなどのNoSQLデータベースを使用したほうがよいでしょう。フィールド数に制限がないという要件に適合する事前定義されたスキーマはありません(典型的なMongoDBコレクションの例を参照)。MongoDBのドキュメントをチェックして、ドキュメントにクエリを実行する方法のアイデアを取得してください。

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

2
好奇心から、彼のモデルは非リレーショナルであるとあなたに思わせたもの。彼が上に置いた情報は私にとても関係があるようです。
Colin M

0

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.