sql >> データベース >  >> RDS >> Mysql

MySQL:1つのステートメントで多くのテーブルを結合する

    MySqlで再帰クエリを使用してツリーノードからすべての子孫を取得するにはどうすればよいですか?

    これはMySqlにとって本当に問題であり、この質問の重要なポイントですが、まだいくつかの選択肢があります。

    あなたがそのようなサンプルデータを持っていると仮定すると、あなたのサンプルほどではありませんが、実証するのに十分です:

    create table treeNode(
    id int, parent_id  int,  name varchar(10), type varchar(10),level int);
    insert into treeNode 
    (id, parent_id, name, type, level) values 
    ( 1,  0,  'C1    ', 'CATEGORY', 1),
    ( 2,  1,  'C1.1  ', 'CATEGORY', 2),
    ( 3,  2,  'C1.1.1', 'CATEGORY', 3),
    ( 4,  1,  'C1.2  ', 'CATEGORY', 2),
    ( 5,  4,  'C1.2.1', 'CATEGORY', 3),
    ( 3,  8,  'G1.1.1',    'GROUP', 3),
    ( 4,  9,  'G1.2  ',    'GROUP', 2),
    ( 5,  4,  'G1.2.1',    'GROUP', 3),
    ( 8,  9,  'G1.1  ',    'GROUP', 2),
    ( 9,  0,  'G1    ',    'GROUP', 1);
    

    最初の選択肢:レベルコード

    treeNodeテーブルのname列のサンプルデータのように。 (英語での言い方がわかりません。level codeの正しい表現についてコメントしてください。 。)

    C1のすべての子孫を取得するには またはG1 このように単純な場合があります:

    select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
    select * from treeNode where type = 'GROUP' and name like 'G1%' ;
    

    私はこのアプローチを非常に好みます。treeNodeをアプリケーションに保存する前にこれらのコードを生成する必要さえあります。レコード数が多い場合は、再帰クエリやプロシージャよりも効率的です。これは非正規化の良いアプローチだと思います。

    このアプローチでは、ステートメント 参加が必要 可能性があります:

    SELECT distinct p.* --if there is only one tree node for a product, distinct is not needed
    FROM product p
    JOIN product_type pt
         ON pt.id= p.parent_id -- to get product type of a product
    JOIN linked_TreeNode LC
         ON LC.product_id= p.id -- to get tree_nodes related to a product
    JOIN (select * from treeNode where type = 'CATEGORY' and name like 'C1%' ) C --may replace C1% to concat('$selected_cat_name','%')
         ON LC.treeNode_id = C.id
    JOIN (select * from treeNode where type = 'GROUP' and name like 'G1%' ) G --may replace G1% to concat('$selected_group_name','%')
         ON LC.treeNode_id = G.id
    WHERE pt.name = '$selected_type'  -- filter selected product type, assuming using product.name, if using product.parent_id, can save one join by pt like your original sql
    

    甘いですね。

    2番目の選択肢:レベル番号

    DDLに示されているように、treeNodeテーブルにレベル列を追加します。

    レベル番号は、レベルコードよりも保守がはるかに簡単です。 アプリケーションで。

    C1のすべての子孫を取得するためのレベル番号付き またはG1 このようなちょっとしたトリックが必要です:

    SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids 
      FROM (select * from treeNode where type = 'CATEGORY' order by level) as t
      JOIN (select @pv:='1')tmp
     WHERE find_in_set(parent_id,@pv)
        OR find_in_set(id,@pv);
     -- get all descendants of `C1`
    
    SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids 
      FROM (select * from treeNode where type = 'GROUP' order by level) as t
      JOIN (select @pv:=',9,')tmp
     WHERE find_in_set(parent_id,@pv)
        OR find_in_set(id,@pv) ;
    

    このアプローチは最初のアプローチよりも低速ですが、再帰クエリよりも高速です。

    質問への完全なSQLは省略されています。 CとGの2つのサブクエリを上記の2つのクエリに置き換えるだけです。

    注:

    ここなど、多くの同様のアプローチがあります。 こちら 、またはここ 。レベル番号またはレベルコードで注文しない限り、機能しません。このSqlFiddle で最後のクエリをテストできます。 order by levelを変更する order by id 違いを確認します。

    別の選択肢:入れ子集合モデル

    このブログ を参照してください。 、まだテストしていません。しかし、それは最後の2つの選択に似ていると思います。

    ツリーノードテーブルに左の番号と右の番号を追加して、すべての子孫のIDをそれらの間に囲む必要があります。



    1. Ubuntuのbashスクリプトから純粋にmysql5.7をインストールします

    2. Javaでユーザー定義型を含むOracleストアドプロシージャを呼び出す方法は?

    3. リストを文字列に変換して、PythonScrapyの1行でSQLに挿入します

    4. アプリケーション、接続プール、PostgreSQL用の1つのセキュリティシステム-LDAPの場合