Tengo una siguiente tabla:
col1 | col2 | col3
-----+------+-------
1 | a | 5
5 | d | 3
3 | k | 7
6 | o | 2
2 | 0 | 8
Si un usuario busca "1", el programa verá el col1
que tiene "1", luego obtendrá un valor en col3
"5", luego el programa continuará buscando "5" col1
y obtendrá "3" en col3
, y así sucesivamente. Entonces se imprimirá:
1 | a | 5
5 | d | 3
3 | k | 7
Si un usuario busca "6", se imprimirá:
6 | o | 2
2 | 0 | 8
¿Cómo crear una SELECT
consulta para hacer eso?
Respuestas:
Editar
La solución mencionada por @leftclickben también es efectiva. También podemos usar un procedimiento almacenado para el mismo.
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 //
Estamos usando una tabla temporal para almacenar los resultados de la salida y, como las tablas temporales se basan en sesiones, no habrá ningún problema con los datos de salida que sean incorrectos.
SQL FIDDLE Demo
Prueba esta consulta: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 |
fuente
col3
tipo, por lo que si hay varios registros, entonces no funcionará.La respuesta aceptada por @Meherzad solo funciona si los datos están en un orden particular. Sucede que funciona con los datos de la pregunta OP. En mi caso, tuve que modificarlo para que funcionara con mis datos.
Nota: Esto solo funciona cuando el "id" de cada registro (col1 en la pregunta) tiene un valor MAYOR que el "id principal" de ese registro (col3 en la pregunta). Este suele ser el caso, porque normalmente será necesario crear primero el padre. Sin embargo, si su aplicación permite cambios en la jerarquía, donde un elemento puede volver a ser parental en otro lugar, entonces no puede confiar en esto.
Esta es mi consulta en caso de que ayude a alguien; tenga en cuenta que no funciona con la pregunta dada porque los datos no siguen la estructura requerida descrita anteriormente.
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
La diferencia es que
table1
está ordenado porcol1
para que el padre esté detrás de él (ya que elcol1
valor del padre es menor que el del hijo).fuente
leftclickben answer funcionó para mí, pero quería una ruta desde un nodo dado hasta la raíz del árbol, y estos parecían ir en sentido contrario, hacia abajo del árbol. Entonces, tuve que cambiar algunos de los campos y renombrarlos para mayor claridad, y esto funciona para mí, en caso de que esto sea lo que alguien más quiera también:
item | parent ------------- 1 | null 2 | 1 3 | 1 4 | 2 5 | 4 6 | 3
y
select t.item_id as item, @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;
da:
item | parent ------------- 6 | 3 3 | 1 1 | null
fuente
1 3 6
Yo usoarray_reverse()
en php en su lugar ... ¿alguna solución sql para eso?El procedimiento almacenado es la mejor manera de hacerlo. Porque la solución de Meherzad funcionaría solo si los datos siguen el mismo orden.
Si tenemos una estructura de tabla como esta
col1 | col2 | col3 -----+------+------ 3 | k | 7 5 | d | 3 1 | a | 5 6 | o | 2 2 | 0 | 8
No funcionará.
SQL Fiddle Demo
Aquí hay un código de procedimiento de muestra para lograr lo mismo.
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;
fuente
Si desea poder tener un SELECT sin problemas de que la identificación principal tenga que ser menor que la identificación secundaria, se podría usar una función. También admite varios niños (como debería hacer un árbol) y el árbol puede tener varias cabezas. También asegura que se rompa si existe un bucle en los datos.
Quería usar SQL dinámico para poder pasar los nombres de tablas / columnas, pero las funciones en MySQL no admiten esto.
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$$
Aquí, la tabla
test
debe modificarse al nombre real de la tabla y es posible que las columnas (ParentId, Id) tengan que ajustarse a sus nombres reales.Uso:
SET @wantedSubTreeId = 3; SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;
Resultado:
SQL para la creación de pruebas:
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');
EDITAR: Aquí hay un violín para probarlo usted mismo. Me obligó a cambiar el delimitador usando el predefinido, pero funciona.
fuente
Partiendo de Master DJon
Aquí hay una función simplificada que proporciona la utilidad adicional de devolver la profundidad (en caso de que desee utilizar la lógica para incluir la tarea principal o buscar a una profundidad específica)
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$$
Uso:
select * from test where childDepth(1, id) <> -1;
fuente