すべて、
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に似たものは見つかりませんでした。誰かアドバイスしてくれませんか?
ありがとう
MySQLは再帰クエリをサポートしていないため、難しい方法で行う必要があります。
ParentID = X
ここで、X
はルートです。Id
値を収集します。Id
ごとに(1)を繰り返します。最大の深さがわかっている場合は、テーブルをそれ自体に(LEFT OUTER JOINを使用して)可能な最大の深さまで結合し、NULLをクリーンアップできます。
ツリー表現を ネストされたセット に変更することもできます。
また、mysqlで同様の結果を得る方法を示すこの興味深いブログを調べることもできます。
http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/
これは古いスレッドですが、別のフォーラムで質問を受けたので、ここに追加したいと思いました。このケースでは、特定のケースを処理するようにハードコーディングされたストアドプロシージャを作成しました。もちろん、すべてのユーザーがストアドプロシージャを自由に作成できるわけではありませんが、それでもなお、いくつかの欠点があります。
ノードと子を含む次の表について考えてみます。
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 ;
以下の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
投稿が遅れる場合があります。
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 を見つけてください。誰かに役立つことを願っています。
以下のストアドプロシージャは、前のテーブルへの後方参照を持つ行を持つテーブルを並べ替えます。最初のステップで行を一時テーブルにコピーしていることに注意してください。これらの行はいくつかの条件に一致します。私の場合、これらは同じ線形(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;