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

¿Cómo crear una consulta recursiva jerárquica de MySQL?

Para MySQL 8+: use el recursivo with sintaxis.
Para MySQL 5.x: use variables en línea, ID de ruta o autocombinaciones.

MySQL 8+

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;

El valor especificado en parent_id = 19 debe establecerse en el id del padre del que desea seleccionar todos los descendientes.

MySQL 5.x

Para las versiones de MySQL que no son compatibles con Common Table Expressions (hasta la versión 5.7), podría lograr esto con la siguiente consulta:

select  id,
        name,
        parent_id 
from    (select * from products
         order by parent_id, id) products_sorted,
        (select @pv := '19') initialisation
where   find_in_set(parent_id, @pv)
and     length(@pv := concat(@pv, ',', id))

Aquí hay un violín .

Aquí, el valor especificado en @pv := '19' debe establecerse en el id del padre del que desea seleccionar todos los descendientes.

Esto también funcionará si un padre tiene múltiples niños. Sin embargo, se requiere que cada registro cumpla la condición parent_id < id , de lo contrario los resultados no estarán completos.

Asignaciones de variables dentro de una consulta

Esta consulta utiliza una sintaxis específica de MySQL:las variables se asignan y modifican durante su ejecución. Se hacen algunas suposiciones sobre el orden de ejecución:

  • El from la cláusula se evalúa primero. Así que ahí es donde @pv se inicializa.
  • El where La cláusula se evalúa para cada registro en el orden de recuperación de from alias. Entonces, aquí es donde se establece una condición para incluir solo registros para los cuales el padre ya estaba identificado como parte del árbol de descendientes (todos los descendientes del padre principal se agregan progresivamente a @pv ).
  • Las condiciones en este where cláusula se evalúan en orden, y la evaluación se interrumpe una vez que el resultado total es seguro. Por lo tanto, la segunda condición debe estar en segundo lugar, ya que agrega el id a la lista principal, y esto solo debería suceder si el id pasa la primera condición. La length La función solo se llama para asegurarse de que esta condición sea siempre cierta, incluso si pv por alguna razón, la cadena produciría un valor falso.

En general, uno puede encontrar estas suposiciones demasiado arriesgadas para confiar en ellas. La documentación advierte:

puede obtener los resultados esperados, pero esto no está garantizado [...] el orden de evaluación de las expresiones que involucran variables de usuario no está definido.

Entonces, aunque funciona de manera consistente con la consulta anterior, el orden de evaluación aún puede cambiar, por ejemplo, cuando agrega condiciones o usa esta consulta como una vista o subconsulta en una consulta más grande. Es una "característica" que se eliminará en un futuro Versión de MySQL :

Las versiones anteriores de MySQL hicieron posible asignar un valor a una variable de usuario en declaraciones distintas de SET . Esta funcionalidad es compatible con MySQL 8.0 por compatibilidad con versiones anteriores, pero está sujeta a eliminación en una versión futura de MySQL.

Como se indicó anteriormente, a partir de MySQL 8.0 en adelante, debe usar el recursivo with sintaxis.

Eficiencia

Para conjuntos de datos muy grandes, esta solución puede volverse lenta, ya que find_in_set La operación no es la forma más ideal de encontrar un número en una lista, ciertamente no en una lista que alcanza un tamaño en el mismo orden de magnitud que la cantidad de registros devueltos.

Alternativa 1:with recursive , connect by

Cada vez más bases de datos implementan el SQL:1999 ISO standard WITH [RECURSIVE] sintaxis para consultas recursivas (por ejemplo, Postgres 8.4+ , SQL Server 2005+ , DB2 , Oracle 11gR2+ , SQLite 3.8.4+ , Firebird 2.1+ , H2 , HyperSQL 2.1.0+ , Teradata , MariaDB 10.2.2+ ). Y a partir de la versión 8.0, también MySQL lo admite . Consulte la parte superior de esta respuesta para ver la sintaxis que debe usar.

Algunas bases de datos tienen una sintaxis alternativa no estándar para búsquedas jerárquicas, como CONNECT BY cláusula disponible en Oracle , DB2 , Informix , CUBRID y otras bases de datos.

MySQL versión 5.7 no ofrece esa función. Cuando su motor de base de datos proporciona esta sintaxis o puede migrar a uno que lo haga, entonces esa es sin duda la mejor opción para elegir. Si no es así, considere también las siguientes alternativas.

Alternativa 2:Identificadores de estilo de ruta

Las cosas se vuelven mucho más fáciles si asignas id valores que contienen la información jerárquica:una ruta. Por ejemplo, en su caso, esto podría verse así:

ID NOMBRE
19 categoría1
19/1 categoría2
19/1/1 categoría3
19/1/1/1 categoría4

Entonces tu select se vería así:

select  id,
        name 
from    products
where   id like '19/%'

Alternativa 3:Auto-uniones repetidas

Si conoce un límite superior de cuán profundo puede llegar a ser su árbol de jerarquía, puede usar un sql estándar consulta como esta:

select      p6.parent_id as parent6_id,
            p5.parent_id as parent5_id,
            p4.parent_id as parent4_id,
            p3.parent_id as parent3_id,
            p2.parent_id as parent2_id,
            p1.parent_id as parent_id,
            p1.id as product_id,
            p1.name
from        products p1
left join   products p2 on p2.id = p1.parent_id 
left join   products p3 on p3.id = p2.parent_id 
left join   products p4 on p4.id = p3.parent_id  
left join   products p5 on p5.id = p4.parent_id  
left join   products p6 on p6.id = p5.parent_id
where       19 in (p1.parent_id, 
                   p2.parent_id, 
                   p3.parent_id, 
                   p4.parent_id, 
                   p5.parent_id, 
                   p6.parent_id) 
order       by 1, 2, 3, 4, 5, 6, 7;

Ver este fiddle

El where condition especifica de qué padre desea recuperar los descendientes. Puede ampliar esta consulta con más niveles según sea necesario.