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.