web-dev-qa-db-ja.com

MySQLと同等の以前の接続

すべて、

MySQLデータベースバージョン5.0に存在する親子関係を定義するテーブルに3つのフィールドがあります。テーブル名はtb_Treeで、次のデータがあります。

Table Name: tb_Tree

Id | ParentId | Name
--------------------
1  | 0        | Fruits
2  | 0        | Vegetables
3  | 1        | Apple
4  | 1        | Orange
5  | 2        | Cabbage
6  | 2        | Eggplant

ParentIdが指定されている場合、すべての子を取得するクエリを作成するにはどうすればよいですか。指定されたテーブルエントリは単なるサンプルデータであり、さらに多くの行を含めることができることに注意してください。 Oracleには「CONNECT BY PRIOR」句がありますが、MySQLに似たものは見つかりませんでした。誰かアドバイスしてくれませんか?

ありがとう

18
Jake

MySQLは再帰クエリをサポートしていないため、難しい方法で行う必要があります。

  1. ParentID = Xここで、Xはルートです。
  2. (1)からId値を収集します。
  3. (2)のIdごとに(1)を繰り返します。
  4. すべての葉ノードが見つかるまで、手作業で再帰を続けます。

最大の深さがわかっている場合は、テーブルをそれ自体に(LEFT OUTER JOINを使用して)可能な最大の深さまで結合し、NULLをクリーンアップできます。

ツリー表現を ネストされたセット に変更することもできます。

10
mu is too short

また、mysqlで同様の結果を得る方法を示すこの興味深いブログを調べることもできます。

http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/

2
Yashpal Singla

これは古いスレッドですが、別のフォーラムで質問を受けたので、ここに追加したいと思いました。このケースでは、特定のケースを処理するようにハードコーディングされたストアドプロシージャを作成しました。もちろん、すべてのユーザーがストアドプロシージャを自由に作成できるわけではありませんが、それでもなお、いくつかの欠点があります。

ノードと子を含む次の表について考えてみます。

CREATE TABLE nodes (
       parent INT,
       child INT
);

INSERT INTO nodes VALUES
       ( 5,  2), ( 5, 3),
       (18, 11), (18, 7),
       (17,  9), (17, 8),
       (26, 13), (26, 1), (26,12),
       (15, 10), (15, 5),       
       (38, 15), (38, 17), (38, 6),
       (NULL, 38), (NULL, 26), (NULL, 18);

このテーブルを使用すると、次のストアドプロシージャは、指定されたノードのすべての子孫で構成される結果セットを計算します。

delimiter $$
CREATE PROCEDURE find_parts(seed INT)
BEGIN
  -- Temporary storage
  DROP TABLE IF EXISTS _result;
  CREATE TEMPORARY TABLE _result (node INT PRIMARY KEY);

  -- Seeding
  INSERT INTO _result VALUES (seed);

  -- Iteration
  DROP TABLE IF EXISTS _tmp;
  CREATE TEMPORARY TABLE _tmp LIKE _result;
  REPEAT
    TRUNCATE TABLE _tmp;
    INSERT INTO _tmp SELECT child AS node
      FROM _result JOIN nodes ON node = parent;

    INSERT IGNORE INTO _result SELECT node FROM _tmp;
  UNTIL ROW_COUNT() = 0
  END REPEAT;
  DROP TABLE _tmp;
  SELECT * FROM _result;
END $$
delimiter ;
2
Mats Kindahl

以下のselectは、すべての植物とそのparentidを最大4レベルまでリストします(もちろん、レベルを拡張できます)。

select id, name, parentid
,(select parentid from tb_tree where id=t.parentid) parentid2
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4 
from tb_tree t

次に、このクエリを使用して最終結果を取得できます。たとえば、次のSQLを使用すると、「フルーツ」のすべての子を取得できます。

select id ,name from (
    select id, name, parentid
    ,(select parentid from tb_tree where id=t.parentid) parentid2
    ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
    ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4 
    from tb_tree t) tt
where ifnull(parentid4,0)=1 or ifnull(parentid3,0)=1 or ifnull(parentid2,0)=1 or ifnull(parentid,0)=1
2
user5132533

投稿が遅れる場合があります。

MySQL8では、再帰的な句でそれを実現できます。以下がその例です。

 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;

さらにヘルプが必要な場合は、別の thread を見つけてください。誰かに役立つことを願っています。

1

以下のストアドプロシージャは、前のテーブルへの後方参照を持つ行を持つテーブルを並べ替えます。最初のステップで行を一時テーブルにコピーしていることに注意してください。これらの行はいくつかの条件に一致します。私の場合、これらは同じ線形(GPSナビゲーションで使用される道路)に属する行です。事業領域は重要ではありません。私の場合、同じ道路に属するセグメントを並べ替えています

DROP PROCEDURE IF EXISTS orderLocationsが存在する場合。区切り文字//

CREATE PROCEDURE orderLocations(_full_linear_code VARCHAR(11))BEGIN

DECLARE _code VARCHAR(11);
DECLARE _id INT(4);
DECLARE _count INT(4);
DECLARE _pos INT(4);

DROP TEMPORARY TABLE IF EXISTS temp_sort;

CREATE TEMPORARY TABLE temp_sort (
  id              INT(4) PRIMARY KEY,
  pos             INT(4),
  code            VARCHAR(11),
  prev_code       VARCHAR(11)
);

-- copy all records to sort into temp table - this way sorting would go all in memory
INSERT INTO temp_sort SELECT
                         id, -- this is primary key of original table
                         NULL, -- this is position that still to be calculated
                         full_tmc_code, -- this is a column that references sorted by
                         negative_offset -- this is a reference to the previous record (will be blank for the first)
                       FROM tmc_file_location
                       WHERE linear_full_tmc_code = _full_linear_code;

-- this is how many records we have to sort / update position
SELECT count(*)
FROM temp_sort
INTO _count;

-- first position index
SET _pos = 1;

-- pick first record that has no prior record
SELECT
  code,
  id
FROM temp_sort l
WHERE prev_code IS NULL
INTO _code, _id;

-- update position of the first record
UPDATE temp_sort
SET pos = _pos
WHERE id = _id;

-- all other go by chain link
WHILE (_pos < _count) DO
  SET _pos = _pos +1;

  SELECT
    code,
    id
  FROM temp_sort
  WHERE prev_code = _code
  INTO _code, _id;


  UPDATE temp_sort
  SET pos = _pos
  WHERE id = _id;

END WHILE;

-- join two tables and return position along with all other fields
SELECT
  t.pos,
  l.*
FROM tmc_file_location l, temp_sort t
WHERE t.id = l.id
ORDER BY t.pos;

END;
0
Stan Sokolov