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

MySQL階層再帰クエリを作成するにはどうすればよいですか?

    MySQL 8以降の場合: 再帰的な withを使用します 構文。
    MySQL 5.xの場合: インライン変数、パスID、または自己結合を使用します。

    MySQL 8+

    with recursive cte (id, name, parent_id) as (
      select     id,
                 name,
                 parent_id
      from       products
      where      parent_id = 19
      union all
      select     p.id,
                 p.name,
                 p.parent_id
      from       products p
      inner join cte
              on p.parent_id = cte.id
    )
    select * from cte;
    

    parent_id =19で指定された値 idに設定する必要があります すべての子孫を選択する親の。

    MySQL 5.x

    Common Table ExpressionsをサポートしないMySQLバージョン(バージョン5.7まで)の場合、次のクエリでこれを実現します。

    select  id,
            name,
            parent_id 
    from    (select * from products
             order by parent_id, id) products_sorted,
            (select @pv := '19') initialisation
    where   find_in_set(parent_id, @pv)
    and     length(@pv := concat(@pv, ',', id))
    

    これがフィドル です。 。

    ここで、 @pv:='19'で指定された値 idに設定する必要があります すべての子孫を選択する親の。

    これは、親が複数ある場合にも機能します 子供。ただし、各レコードが parent_id の条件を満たす必要があります。 、そうでない場合、結果は完全ではありません。

    クエリ内の可変割り当て

    このクエリは特定のMySQL構文を使用します。変数は実行中に割り当てられ、変更されます。実行の順序についていくつかの仮定があります:

    • from 句が最初に評価されます。ここで@pv 初期化されます。
    • where 句は、 fromから取得した順序でレコードごとに評価されます。 エイリアス。したがって、これは、親が子孫ツリーにあるとすでに識別されているレコードのみを含むように条件が設定される場所です(プライマリ親のすべての子孫は、 @pvに段階的に追加されます。 。
    • このwhereの条件 条項は順番に評価され、全体的な結果が確実になると評価は中断されます。したがって、2番目の条件は id を追加するため、2番目に配置する必要があります 親リストに追加します。これは、 idの場合にのみ発生します。 最初の条件に合格します。 長さ 関数は、 pv であっても、この条件が常に真であることを確認するためにのみ呼び出されます。 文字列は何らかの理由で偽の値を生成します。

    全体として、これらの仮定はリスクが高すぎて信頼できないと感じるかもしれません。 ドキュメント 警告:

    期待どおりの結果が得られる可能性がありますが、これは保証されていません[...]ユーザー変数を含む式の評価の順序は定義されていません。

    したがって、上記のクエリと一貫して機能する場合でも、たとえば条件を追加したり、このクエリをより大きなクエリのビューまたはサブクエリとして使用したりすると、評価の順序が変わる可能性があります。これは、が将来削除される「機能」です。 MySQLリリース

    MySQLの以前のリリースでは、 SET以外のステートメントでユーザー変数に値を割り当てることができました。 。この機能は、下位互換性のためにMySQL 8.0でサポートされていますが、MySQLの将来のリリースで削除される可能性があります。

    上記のように、MySQL 8.0以降では、再帰的な withを使用する必要があります。 構文。

    効率

    find_in_set 操作は、リスト内の数値を見つけるための最も理想的な方法ではありません。確かに、返されるレコード数と同じ桁数のサイズに達するリスト内ではありません。

    代替案1:再帰的 connect by

    ますます多くのデータベースが SQL:1999ISO標準WITH[RECURSIVE]<を実装しています。 / code> 構文 再帰クエリの場合(例: Postgres 8.4+ SQL Server 2005+ DB2 Oracle 11gR2 + SQLite 3.8.4+ Firebird 2.1+ H2 HyperSQL 2.1.0+ Teradata > 、 MariaDB 10.2.2+ )。また、バージョン8.0以降、MySQLもサポートしています 。使用する構文については、この回答の上部を参照してください。

    一部のデータベースには、 CONNECT BY など、階層ルックアップ用の代替の非標準構文があります。 Oracle で利用可能な句 、 DB2 Informix > 、 CUBRID およびその他のデータベース。

    MySQLバージョン5.7はそのような機能を提供していません。データベースエンジンがこの構文を提供する場合、または提供する構文に移行できる場合は、それが最適なオプションです。そうでない場合は、次の代替案も検討してください。

    代替案2:パススタイルの識別子

    id を割り当てると、作業がはるかに簡単になります 階層情報を含む値:パス。たとえば、あなたの場合、これは次のようになります。

    ID 名前
    19 category1
    19/1 category2
    19/1/1 category3
    19/1/1/1 category4

    次に、 select 次のようになります:

    select  id,
            name 
    from    products
    where   id like '19/%'
    

    代替3:繰り返し自己結合

    階層ツリーの深さの上限がわかっている場合は、標準の sqlを使用できます。 このようなクエリ:

    select      p6.parent_id as parent6_id,
                p5.parent_id as parent5_id,
                p4.parent_id as parent4_id,
                p3.parent_id as parent3_id,
                p2.parent_id as parent2_id,
                p1.parent_id as parent_id,
                p1.id as product_id,
                p1.name
    from        products p1
    left join   products p2 on p2.id = p1.parent_id 
    left join   products p3 on p3.id = p2.parent_id 
    left join   products p4 on p4.id = p3.parent_id  
    left join   products p5 on p5.id = p4.parent_id  
    left join   products p6 on p6.id = p5.parent_id
    where       19 in (p1.parent_id, 
                       p2.parent_id, 
                       p3.parent_id, 
                       p4.parent_id, 
                       p5.parent_id, 
                       p6.parent_id) 
    order       by 1, 2, 3, 4, 5, 6, 7;
    

    このフィドル をご覧ください

    where 条件は、子孫を取得する親を指定します。必要に応じて、このクエリをより多くのレベルに拡張できます。



    1. Atand()がPostgreSQLでどのように機能するか

    2. SpringBootアプリケーションがHikariでスタックします-Pool-1-開始しています...

    3. MySQL Orderby a number、Nulls last

    4. 11gR2圧縮アドバイザー=悪