私は次の表を得ました:
col1 | col2 | col3
-----+------+-------
1 | a | 5
5 | d | 3
3 | k | 7
6 | o | 2
2 | 0 | 8
ユーザーが「1」を検索すると、プログラムは「1」を持つcol1
を確認し、col3
"5"の値を取得します。その後、プログラムは[ col1
に5 "、col3
に3を取得します。したがって、印刷されます:
1 | a | 5
5 | d | 3
3 | k | 7
ユーザーが「6」を検索すると、印刷されます。
6 | o | 2
2 | 0 | 8
SELECT
クエリを作成してそれを行う方法は?
編集
@leftclickbenが言及した解決策も有効です。同じためにストアドプロシージャを使用することもできます。
CREATE PROCEDURE get_tree(IN id int)
BEGIN
DECLARE child_id int;
DECLARE prev_id int;
SET prev_id = id;
SET child_id=0;
SELECT col3 into child_id
FROM table1 WHERE col1=id ;
create TEMPORARY table IF NOT EXISTS temp_table as (select * from table1 where 1=0);
truncate table temp_table;
WHILE child_id <> 0 DO
insert into temp_table select * from table1 WHERE col1=prev_id;
SET prev_id = child_id;
SET child_id=0;
SELECT col3 into child_id
FROM TABLE1 WHERE col1=prev_id;
END WHILE;
select * from temp_table;
END //
一時テーブルを使用して出力結果を保存していますが、一時テーブルはセッションベースであるため、出力データが正しくないという問題は発生しません。
SQL FIDDLE Demo
このクエリを試してください:
SELECT col1, col2, @pv := col3 as 'col3' FROM table1 JOIN (SELECT @pv := 1) tmp WHERE col1 = @pv
SQL FIDDLE Demo
:| COL1 | COL2 | COL3 | +------+------+------+ | 1 | a | 5 | | 5 | d | 3 | | 3 | k | 7 |
注
parent_id
の値は、このソリューションが機能するためにchild_id
よりも小さくなければなりません。
@Meherzadが受け入れた回答は、データが特定の順序にある場合にのみ機能します。 OP質問のデータを処理することが起こります。私の場合、データを操作するために変更する必要がありました。
注これは、すべてのレコードの「id」(質問のcol1)の値がそのレコードの「親id」(質問のcol3)よりも大きい場合にのみ機能します。通常、親を最初に作成する必要があるため、これがよくあります。ただし、アプリケーションが階層の変更を許可している場合、アイテムの別の場所に親が変更される可能性があるため、これに依存することはできません。
これは、誰かを助ける場合の私のクエリです。データは上記の必要な構造に従っていないため、指定された質問では機能しないことに注意してください。
select t.col1, t.col2, @pv := t.col3 col3
from (select * from table1 order by col1 desc) t
join (select @pv := 1) tmp
where t.col1 = @pv
違いは、table1
がcol1
によって順序付けられているため、親が後になるようにすることです(親のcol1
値が子の値よりも小さいため)。
leftclickbenの答えは私にとってはうまくいきましたが、特定のノードからツリーまでのパスをルートに戻したいと思っていました。そのため、いくつかのフィールドを反転させてわかりやすくするために名前を変更する必要がありましたが、これは他の誰かが望んでいる場合に役立ちます
item | parent
-------------
1 | null
2 | 1
3 | 1
4 | 2
5 | 4
6 | 3
そして
select t.item_id as item_id, @pv:=t.parent as parent
from (select * from item_tree order by item_id desc) t
join
(select @pv:=6)tmp
where t.item_id=@pv;
与える:
item | parent
-------------
6 | 3
3 | 1
1 | null
ストアドプロシージャは、それを行うための最良の方法です。 Meherzadのソリューションは、データが同じ順序に従っている場合にのみ機能するためです。
このようなテーブル構造がある場合
col1 | col2 | col3
-----+------+------
3 | k | 7
5 | d | 3
1 | a | 5
6 | o | 2
2 | 0 | 8
うまくいきません。 SQL Fiddle Demo
同じことを実現するためのサンプルプロシージャコードを次に示します。
delimiter //
CREATE PROCEDURE chainReaction
(
in inputNo int
)
BEGIN
declare final_id int default NULL;
SELECT col3
INTO final_id
FROM table1
WHERE col1 = inputNo;
IF( final_id is not null) THEN
INSERT INTO results(SELECT col1, col2, col3 FROM table1 WHERE col1 = inputNo);
CALL chainReaction(final_id);
end if;
END//
delimiter ;
call chainReaction(1);
SELECT * FROM results;
DROP TABLE if exists results;
親IDが子IDよりも低くなければならないという問題なくSELECTを使用できるようにするには、関数を使用できます。また、(ツリーが行うように)複数の子をサポートし、ツリーは複数のヘッドを持つことができます。また、データにループが存在する場合に確実にブレークします。
テーブル/カラム名を渡すことができるように動的SQLを使用したかったのですが、MySQLの関数はこれをサポートしていません。
DELIMITER $$
CREATE FUNCTION `isSubElement`(pParentId INT, pId INT) RETURNS int(11)
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE isChild,curId,curParent,lastParent int;
SET isChild = 0;
SET curId = pId;
SET curParent = -1;
SET lastParent = -2;
WHILE lastParent <> curParent AND curParent <> 0 AND curId <> -1 AND curParent <> pId AND isChild = 0 DO
SET lastParent = curParent;
SELECT ParentId from `test` where id=curId limit 1 into curParent;
IF curParent = pParentId THEN
SET isChild = 1;
END IF;
SET curId = curParent;
END WHILE;
RETURN isChild;
END$$
ここでは、テーブルtest
を実際のテーブル名に変更する必要があり、列(ParentId、Id)を実際の名前に合わせて調整する必要があります。
使用法 :
SET @wantedSubTreeId = 3;
SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;
結果:
3 7 k
5 3 d
9 3 f
1 5 a
テスト作成用のSQL:
CREATE TABLE IF NOT EXISTS `test` (
`Id` int(11) NOT NULL,
`ParentId` int(11) DEFAULT NULL,
`Name` varchar(300) NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into test (id, parentid, name) values(3,7,'k');
insert into test (id, parentid, name) values(5,3,'d');
insert into test (id, parentid, name) values(9,3,'f');
insert into test (id, parentid, name) values(1,5,'a');
insert into test (id, parentid, name) values(6,2,'o');
insert into test (id, parentid, name) values(2,8,'c');
編集:ここに fiddle があります。定義済みの区切り文字を使用して区切り文字を変更する必要がありましたが、機能します。
マスターDJonの構築
深さを返す追加ユーティリティを提供する単純化された関数を次に示します(親タスクを含めるためのロジックを使用したり、特定の深さで検索したい場合)
DELIMITER $$
FUNCTION `childDepth`(pParentId INT, pId INT) RETURNS int(11)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE depth,curId int;
SET depth = 0;
SET curId = pId;
WHILE curId IS not null AND curId <> pParentId DO
SELECT ParentId from test where id=curId limit 1 into curId;
SET depth = depth + 1;
END WHILE;
IF curId IS NULL THEN
set depth = -1;
END IF;
RETURN depth;
END$$
使用法:
select * from test where childDepth(1, id) <> -1;