Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como criar uma consulta recursiva hierárquica do MySQL?


Para MySQL 8+: use a recursiva com sintaxe.
Para MySQL 5.x: use variáveis ​​em linha, IDs de caminho ou autojunções.

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;

O valor especificado em parent_id =19 deve ser definido para o id do pai do qual você deseja selecionar todos os descendentes.

MySQL 5.x


Para versões do MySQL que não suportam Expressões de Tabela Comuns (até a versão 5.7), você faria isso com a seguinte 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))

Aqui está um violino .

Aqui, o valor especificado em @pv :='19' deve ser definido para o id do pai do qual você deseja selecionar todos os descendentes.

Isso também funcionará se um pai tiver vários crianças. No entanto, é necessário que cada registro cumpra a condição parent_id , caso contrário os resultados não serão completos.

Atribuições de variáveis ​​dentro de uma consulta


Esta consulta usa uma sintaxe específica do MySQL:as variáveis ​​são atribuídas e modificadas durante sua execução. Algumas suposições são feitas sobre a ordem de execução:
  • O de cláusula é avaliada primeiro. Então é aí que @pv é inicializado.
  • O onde A cláusula é avaliada para cada registro na ordem de recuperação do from apelido. Então é aqui que uma condição é colocada para incluir apenas registros para os quais o pai já foi identificado como estando na árvore descendente (todos os descendentes do pai primário são adicionados progressivamente a @pv ).
  • As condições neste onde cláusula são avaliadas em ordem, e a avaliação é interrompida uma vez que o resultado total é certo. Portanto, a segunda condição deve estar em segundo lugar, pois adiciona o id para a lista pai, e isso só deve acontecer se o id passa a primeira condição. O comprimento A função é chamada apenas para garantir que esta condição seja sempre verdadeira, mesmo que o pv string por algum motivo produziria um valor falso.

Em suma, pode-se achar essas suposições muito arriscadas para se confiar. A documentação avisa:

você pode obter os resultados esperados, mas isso não é garantido [...] a ordem de avaliação para expressões envolvendo variáveis ​​de usuário é indefinida.

Portanto, mesmo que funcione consistentemente com a consulta acima, a ordem de avaliação ainda pode mudar, por exemplo, quando você adiciona condições ou usa essa consulta como uma visualização ou subconsulta em uma consulta maior. É um "recurso" que será removido no futuro Versão do MySQL :

As versões anteriores do MySQL tornaram possível atribuir um valor a uma variável de usuário em instruções diferentes de SET . Essa funcionalidade é suportada no MySQL 8.0 para compatibilidade com versões anteriores, mas está sujeita a remoção em uma versão futura do MySQL.

Como dito acima, do MySQL 8.0 em diante você deve usar a recursiva with sintaxe.

Eficiência


Para conjuntos de dados muito grandes, esta solução pode ficar lenta, pois o find_in_set operação não é a maneira mais ideal de encontrar um número em uma lista, certamente não em uma lista que atinge um tamanho na mesma ordem de grandeza do número de registros retornados.

Alternativa 1:com recursiva , conectar por


Cada vez mais bancos de dados implementam o SQL:1999 ISO padrão COM [RECURSIVE] sintaxe para consultas recursivas (por exemplo, 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+ ). E a partir da versão 8.0, também o MySQL suporta . Veja o início desta resposta para a sintaxe a ser usada.

Alguns bancos de dados têm uma sintaxe alternativa e não padrão para pesquisas hierárquicas, como o CONNECT BY cláusula disponível em Oracle , DB2 , Informix , CUBRID e outros bancos de dados.

O MySQL versão 5.7 não oferece esse recurso. Quando seu mecanismo de banco de dados fornece essa sintaxe ou você pode migrar para um que o faça, essa é certamente a melhor opção. Se não, considere também as seguintes alternativas.

Alternativa 2:identificadores de estilo de caminho


As coisas ficam muito mais fáceis se você atribuir id valores que contêm as informações hierárquicas:um caminho. Por exemplo, no seu caso, isso pode ficar assim:
ID NOME
19 categoria1
19/1 categoria2
19/1/1 categoria3
19/1/1/1 categoria4

Em seguida, seu selecionar ficaria assim:
select  id,
        name 
from    products
where   id like '19/%'

Alternativa 3:autojunções repetidas


Se você conhece um limite superior de quão profunda sua árvore de hierarquia pode se tornar, você pode usar um padrão sql consulta assim:
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;

Veja este violino

O onde condição especifica de qual pai você deseja recuperar os descendentes. Você pode estender essa consulta com mais níveis conforme necessário.