sql >> Base de Datos >  >> RDS >> Mysql

MySQL:unir muchas tablas en una Declaración

¿Cómo obtener todos los descendientes de un nodo de árbol con consulta recursiva en MySql?

Realmente es un problema para MySql, y es un punto clave para esta pregunta, pero aún tiene algunas opciones.

Suponiendo que tenga tales datos de muestra, no tantos como su muestra pero suficientes para demostrar:

create table treeNode(
id int, parent_id  int,  name varchar(10), type varchar(10),level int);
insert into treeNode 
(id, parent_id, name, type, level) values 
( 1,  0,  'C1    ', 'CATEGORY', 1),
( 2,  1,  'C1.1  ', 'CATEGORY', 2),
( 3,  2,  'C1.1.1', 'CATEGORY', 3),
( 4,  1,  'C1.2  ', 'CATEGORY', 2),
( 5,  4,  'C1.2.1', 'CATEGORY', 3),
( 3,  8,  'G1.1.1',    'GROUP', 3),
( 4,  9,  'G1.2  ',    'GROUP', 2),
( 5,  4,  'G1.2.1',    'GROUP', 3),
( 8,  9,  'G1.1  ',    'GROUP', 2),
( 9,  0,  'G1    ',    'GROUP', 1);

Primera opción:código de nivel

Como los datos de muestra de la columna de nombre en la tabla treeNode. (No se como decirlo en ingles, comentenme sobre la expresion correcta de level code .)

Para obtener todos los descendientes de C1 o G1 podría ser simple como esto:

select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;

Prefiero mucho este enfoque, incluso necesito que generemos este código antes de que treeNode se guarde en la aplicación. Será más eficiente que la consulta o el procedimiento recursivo cuando tengamos una gran cantidad de registros. Creo que este es un buen enfoque de desnormalización.

Con este enfoque, la declaración quieres con unirse podría ser:

SELECT distinct p.* --if there is only one tree node for a product, distinct is not needed
FROM product p
JOIN product_type pt
     ON pt.id= p.parent_id -- to get product type of a product
JOIN linked_TreeNode LC
     ON LC.product_id= p.id -- to get tree_nodes related to a product
JOIN (select * from treeNode where type = 'CATEGORY' and name like 'C1%' ) C --may replace C1% to concat('$selected_cat_name','%')
     ON LC.treeNode_id = C.id
JOIN (select * from treeNode where type = 'GROUP' and name like 'G1%' ) G --may replace G1% to concat('$selected_group_name','%')
     ON LC.treeNode_id = G.id
WHERE pt.name = '$selected_type'  -- filter selected product type, assuming using product.name, if using product.parent_id, can save one join by pt like your original sql

Dulce, ¿no?

Segunda opción:número de nivel

Agregue una columna de nivel a la tabla treeNode, como se muestra en el DDL.

El número de nivel es mucho más fácil de mantener que el código de nivel en aplicación.

Con número de nivel para obtener todos los descendientes de C1 o G1 Necesito un pequeño truco como este:

SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids 
  FROM (select * from treeNode where type = 'CATEGORY' order by level) as t
  JOIN (select @pv:='1')tmp
 WHERE find_in_set(parent_id,@pv)
    OR find_in_set(id,@pv);
 -- get all descendants of `C1`

SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids 
  FROM (select * from treeNode where type = 'GROUP' order by level) as t
  JOIN (select @pv:=',9,')tmp
 WHERE find_in_set(parent_id,@pv)
    OR find_in_set(id,@pv) ;

Este enfoque es más lento que el primero, pero aún más rápido que la consulta recursiva.

El sql completo a la pregunta omitida. Solo necesita reemplazar esas dos subconsultas de C y G con dos consultas anteriores.

Nota:

Hay muchos enfoques similares, como aquí , aquí , o incluso aquí . No funcionarán a menos que se ordenen por número de nivel o código de nivel. Puede probar la última consulta en este SqlFiddle cambiando el order by level para order by id para ver las diferencias.

Otra elección:el modelo de conjuntos anidados

Consulte este blog , No probé todavía. Pero creo que es similar a las dos últimas opciones.

Necesita agregar un número a la izquierda y un número a la derecha a la tabla de nodos de árbol para encerrar las identificaciones de todos los descendientes entre ellos.