MySQL-この階層データを処理するのに最適な方法は?
-
01-10-2019 - |
質問
これは次のフォローアップです。
MySQL-すべてのサブ項目を階層で取得することは可能ですか?
私は任意の深さを持っています 隣接リストモデル テーブル(私はその時点にいます できる それをaに変換します ネストされたセットモデル.
ネストされたセットモデルの使用方法に関するMySQLデータを読みましたが、挿入、更新、削除などの基本的な機能を実行するにはますます複雑になり、非常に複雑になっているように見えました。
隣接リストモデルを備えたトリガーシステムを使用して、各オブジェクトを祖先に関連付ける先祖のテーブルを保持する方法を示す別のブログ。
今、私は特定のノードのすべての子供のリストを返し、それらを変更または削除することができる必要があります。この階層構造は、一度作成された時間は常に変化することはありませんが、階層構造の量は大量にあります。
私が見る3つの方法は次のとおりです。
ストアドプロシージャを作成しました これは、すべての子供を返す再帰クエリを行います。
ネストされたセットモデルに変換します これには、複雑さに入り、その中に追加、編集、削除するストアドプロシージャを作成する必要があります。
祖先テーブルを作成します 上記のすべてのデータを処理する挿入/削除トリガーで説明しました。
私が探求していない他の方法がある場合は、私に知らせてください、そして私はこのリストを更新します。
解決
クスノイ ネストされたセットモデルと隣接リストモデルでいくつかのパフォーマンステストを実行し、彼のブログ投稿で結果と推奨事項を文書化しました 隣接リストとネストされたセット:mysql. 。エグゼクティブサマリーは次のとおりです。
- ネストされたセットは、すべての子ノードまたはすべての親ノードを取得するために高速です。
- ネストされたセットは、テーブルを頻繁に更新する必要がある場合、悪い考えです。
これが彼の記事からの結論です:
MySQLでは、象徴構造の更新がまれであり、更新期間中にテーブルをロックするのが手頃な価格である場合、ネストされたセットモデルを優先する必要があります(長いテーブルで数分かかる場合があります)。
これは、MyISAMストレージエンジンを使用してテーブルを作成し、上記のジオメトリタイプの境界ボックスを作成し、空間インデックスでインデックスを作成し、テーブル内のレベルを持続することを意味します。
テーブルの更新が頻繁に発生している場合、またはアップデートで暗示される長期間テーブルをロックすることができない場合は、隣接リストモデルを使用して階層データを保存する必要があります。
これには、テーブルを照会する関数を作成する必要があります。
記事の残りの部分では、テーブルを定義し、クエリを実装し、パフォーマンス測定を提供する方法を示しています。空間インデックスの使用は、あなたにとって新しいかもしれないネストされたセットモデルのパフォーマンスを改善するための巧妙なアイデアです。
mysqlなしでアプローチを検討している場合は、見たいかもしれません postgreSql これは別の無料でオープンソースデータベースです。 PostgreSQLは、の形式の再帰クエリをサポートします 再帰的な一般的なテーブル式 これにより、HeirarchicalデータのクエリがMySQLよりも簡単になり、パフォーマンスが向上します。 Quassnoiも記事を書きました 隣接リストとネストされたセット:postgreSql それは詳細を示しています。
他のアプローチを見ることについて話している間、Oracleのデータベースも言及する価値があります。 Oracleにはカスタム拡張機能もあります CONNECT BY
これにより、クエリの相続データが非常に簡単かつ高速になります。 Quassnoiの記事 隣接リストとネストされたセット:Oracle 再びパフォーマンスの詳細をカバーします。この場合、すべての子供を獲得するために必要なクエリは非常に簡単です。
SELECT *
FROM yourtable
START WITH id = 42
CONNECT BY parent = PRIOR id
他のヒント
私はいつも一緒に行きます ネストされたセット せん断のシンプルさと説得力。私はいつも提案します この記事. 。このような階層データを使用して作業に必要なクエリを拡大しています。私がここで見ている唯一の欠点は、Hierachryが特定のレベルの複雑さに達したときに新しいレコードを挿入/更新することで遅くなる可能性があることですが、読みは私が見た他の多くのソリューションよりも速いです。
上記の記事の例を挙げるだけで:
SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';
+-------------+----------------------+--------------+-------+
| lev1 | lev2 | lev3 | lev4 |
+-------------+----------------------+--------------+-------+
| ELECTRONICS | TELEVISIONS | TUBE | NULL |
| ELECTRONICS | TELEVISIONS | LCD | NULL |
| ELECTRONICS | TELEVISIONS | PLASMA | NULL |
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS | FLASH |
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS | NULL |
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL |
+-------------+----------------------+--------------+-------+
6 rows in set (0.00 sec)
sql賢明で、私はそれがよりきれいでよりシンプルになることができるとは思わない;)
私にはわかりません ストアドプロシージャ 仕方。しかし、それは(あなたの場合)再帰を伴うので、階層内の多くのレベルでそれが速くなるかどうかはわかりません。試してみることができると思います。
たぶん、ドキュメント指向のデータベースのような使用を検討する必要があります mongodb. 。それはあなたの人生をずっと楽にすることができます。
階層データセットを扱うとき、キャッシングを念頭に置いてアプローチするのが最善だと思います。この方法でこの方法を扱うこの方法の主な利点の1つは、データベースを変異させるのが難しいかもしれないものに通常の定型化を必要としないことです。
メモリヒープ '(memcache、redisなど)ルックアップは、単純な場合はSQLよりもはるかに高速です id -> data
解像度では、各ノードの直接子供のIDのリストをキャッシュするためにそれらを使用します。これにより、再帰アルゴリズムを介して適切なパフォーマンスを取得して、任意のノードの完全なリストを作成できます。
新しいノードを追加/削除するには、直接の親キャッシュを無効にするだけが必要です O(1)
.
それが十分に速くない場合は、各ノードのノードのすべての子のリストに別のキャッシュレイヤーを追加できます。これがきちんと変動するデータセットで動作するためには、各ノードのキャッシュパフォーマンス(フレッシュ/キャッシュヒットの比率)を記録し、キャッシュを保存する時期の許容レベルを設定する必要があります。これは、非生じるデータであるため、メモリヒープに保存することもできます。
このより高度なキャッシュモデルを使用する場合、これらの完全な子供ノードリストは、その子供のいずれかが変更されたときに無効にする必要があることに注意する必要があります O(log n)
.
子供IDのリストを取得したら、SQLを使用できます WHERE id IN( id1, id2, .... )
あなたが望むものをクエリする構文。
私はかつて、複雑な階層的なarbitrary意的な積層法案をSQLのようなデータベースマネージャーに保存する必要がありました。 。ゼロから再起動した後、DBマネージャーを使用して、単純なインデックス付きキーに関するレコード読み取りと書き込みのAPIのみを提供し、実際の入力/操作/レポートをすべて外部コードで実行すると、最終結果はより迅速で実装しやすく、維持と強化を理解し、よりシンプルにします。必要な最も複雑なクエリは、基本的にBからaを選択することでした。
したがって、MySQLの制限内にロジックと操作を埋め込む代わりに、コードを叩いて必要なことを行うことを検討し、MySQLに依存して最低レベルのGetS/Putsに依存することを検討してください。