9

MySQL データベースで、ある種のツリーのようなものが発生しています。

カテゴリを持つデータベースがあり、各カテゴリにはサブキャットがあります。すべてのカテゴリを 1 つのテーブルにまとめているので、列は次のようになります。

*categories table*
id | name  | parent_id
1  | Toys  | 0
2  | Dolls | 1
3  | Bikes | 1

データベース内の各アイテムは、次のいずれかのカテゴリに割り当てられます。

*items table*
item   | category_id
barbie | 2
schwinn| 3

問題は、誰かがすべての TOY (親カテゴリ) を見たい場合、アイテム データベースから情報を取得する最良の方法は何かということです。私が方法を知っている唯一の方法は、次のようなことをすることです

SELECT * 
FROM items 
WHERE category_id = 2 
JOIN SELECT * 
     FROM items 
     WHERE category_id = 3
     etc... 

しかし、Toys の下に 10 個のカテゴリがある場合、この結合とクエリを 10 回実行する必要があります。

これを処理するより良い方法はありますか?

4

8 に答える 8

20

親 ID を取得したい場合:

だからあなたが与えられたと仮定してください

set @parentId = 1 /*toys*/

select 
  *
from
  Items i
inner join Categories c on c.id = i.categoryId
where
  c.parentId = @parentId

これにより、必要な項目が得られますが、大きな設計上の欠陥が 1 つあります。それは、複数レベルの階層カテゴリを処理できないことです。

次の Categories テーブルがあるとします。

*Categories table*
id | name    | parentId
1  | Toys    | 0
2  | Dolls   | 1
3  | Bikes   | 1
4  | Models  | 2
5  | Act.Fig.| 2
6  | Mountain| 3
7  | BMX     | 3

そしてアイテム:

*items table*
item   | category_id
Barbie | 4
GIJoe  | 5
Schwinn| 6
Huffy  | 7

関連するすべてのアイテムを取得する唯一の方法は、自己結合を行うことです。

select 
  *
from
  Items i 
inner join Categories c on c.id = i.categoryId
inner join Categories c2 on c.parentId = c2.id
where
  c2.parentId = @parentId

複数レベルの階層を持つことができるため、このパターンはスケーラブルではありません。

階層を処理する一般的な方法の 1 つは、「フラット化された」テーブルを作成することです。つまり、各ノードをそのすべての子孫にリンクする行です。

Categories テーブルに加えて、2 つ目のテーブルを作成します。

*CategoriesFlat table*  The Name column is here only for readability
id | name    | parentId
1  | Toys    | 1
-----------------
2  | Dolls   | 1
2  | Dolls   | 2
-----------------
4  | Models  | 1
4  | Models  | 2
4  | Models  | 4
5  | Act.Fig.| 1
5  | Act.Fig.| 2
5  | Act.Fig.| 5
-----------------
3  | Bikes   | 1
3  | Bikes   | 3
-----------------
6  | Mountain| 1
6  | Mountain| 3
6  | Mountain| 6
7  | BMX     | 1
7  | BMX     | 3
7  | BMX     | 7

したがって、次のように書くことができます。

select 
  *
from
  Items i
inner join CategoriesFlat c on c.id = i.categoryId
where
  c.parentId = @parentId

そして、関連するすべてのカテゴリとアイテムを取得します。

これは、SQL のアンチパターンとその解決策に関する素晴らしいスライドショーです。(SQL の階層データはアンチパターンですが、がっかりしないでください。誰もがこのパターンに遭遇します)

于 2009-07-06T04:17:42.857 に答える
4

しかし、Toys の下に 10 個のカテゴリがある場合、この結合とクエリを 10 回実行する必要があります。これを処理するより良い方法はありますか?

はい、「ネストされたセット」と呼ばれるデータを保存する方法があります。データを挿入するのは少し難しくなりますが、1 つのselectステートメントを使用して複数レベルのブランチ全体を簡単に選択できます。

また、Celko はこの主題についての本を書き、ネストされたセットに関する章と他の方法に関する章があります。

于 2009-07-06T04:01:37.927 に答える
3

ID番号を取得する方法を知っていると思いますが、それは質問のポイントではありません。また、parent_idFK 参照もする必要がありid、最上位レイヤーには 0 ではなく NULL を使用します。

最上位のカテゴリにサブカテゴリのレベルが 1 つしかない場合、次のクエリを使用してすべてのおもちゃを取得できます。

SELECT *
FROM items
WHERE items.category_id IN (SELECT id FROM categories
                            WHERE categories.parent_id = 1
                            OR categories.id = 1);

カテゴリにサブカテゴリをネストできる場合は、ストアド プロシージャを使用して再帰的に呼び出す必要があります。擬似コード:

Procedure getItemsInCategory
Input: @category_id integer
Output: items rows
{
    For each item in (SELECT *
                      FROM items
                      WHERE items.category_id = @category_id):
        return the row;

    For each id in (SELECT id 
                    FROM categories
                    WHERE categories.parent_id = @category_id):
        return the rows in getItemsInCategory(id);
}
于 2009-07-06T03:43:17.263 に答える
0

Toys カテゴリの ID を知っていて、トップレベルの Toys カテゴリには何もないと仮定します。

SELECT * FROM items WHERE category_id IN (SELECT id FROM categories WHERE parent_id = 1)
于 2009-07-06T03:30:10.033 に答える
0
  1. IN 演算子を使用します。
  2. ストアド プロシージャを使用します。
  3. テーブルがどのように使用される傾向があるかをよりよく反映するように、テーブルを最適化します。
于 2009-07-06T03:32:02.073 に答える
0

私はMySQLに精通していませんが、TSQL(SQL SERVER)で行う方法は次のとおりです。MySQLで同等の方法を見つけてみてください。

1) すべてのカテゴリをループして、特定のアイテムの子を取得します。この場合、categorie id = 1

2) Hierarchy CTE (Common Table Expression) の子に関連するアイテムにフィルターをかけます。

With Hierarchy As
(

SELECT    id, name, parent_id
from         categories
where        id = 1
UNION ALL
SELECT      child.id, child.name, child.parent_id
from           categories child
inner join  Hierarchy parent on child.parent_id = parent.id
)
SELECT * FROM items
WHERE category_id IN 
(
   Select id
   from Hierarchy 
 )
于 2009-07-06T04:13:54.080 に答える
0

このスレッドが役立つかもしれません: http://forums.mysql.com/read.php?10,32818,32818#msg-32818

本当に必要なのは START WITH および CONNECT BY 構文ですが、これは Oracle でのみサポートされており、MySQL ではサポートされていません。

于 2009-07-06T04:14:02.067 に答える
0

私の考えをあなたと共有したいと思います。

隣接モデルの制限: MySQL の階層データの管理に従って ください 既に説明したように、隣接モデルには、パスを取得する前にレベルを知る必要があるという制限があります。

ネストされたモデルを使用する: ただし、データ構造をネストされたセット モデルに変換する場合でも、自己結合を使用してツリーを取得できます。

階層モデルから入れ子モデルへの変換: ここで、入れ子モデルにインデックスを付けるためにツリー トラベル アルゴリズムが必要です。これはmysql関数で実装できます(変換にはアルゴリズムの実装が必要です:ツリートラバーサルアルゴリズム。どれが最適かはわかりません)。

ありがとう :)

于 2014-02-16T07:57:01.017 に答える