Eu tenho uma tabela MySQL que é a seguinte:
id | name | parent_id
19 | category1 | 0
20 | category2 | 19
21 | category3 | 20
22 | category4 | 21
......
Agora, eu quero ter uma única consulta MySQL para a qual simplesmente forneço o ID [por exemplo, diga 'id = 19'], então eu deveria obter todos os seus IDs filhos [isto é, o resultado deve ter os IDs '20, 21,22 ']. ... Além disso, a hierarquia das crianças não é conhecida, pode variar ....
Além disso, eu já tenho a solução usando o loop for ..... Deixe-me saber como obter o mesmo usando uma única consulta MySQL, se possível.
mysql
sql
hierarchical-data
recursive-query
Tarun Parswani
fonte
fonte
Respostas:
Para o MySQL 8+: use a
with
sintaxe recursiva .Para o MySQL 5.x: use variáveis embutidas, IDs de caminho ou associações automáticas.
MySQL 8+
O valor especificado em
parent_id = 19
deve ser definido comoid
o 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:
Aqui está um violino .
Aqui, o valor especificado em
@pv := '19'
deve ser definido como oid
do pai, do qual você deseja selecionar todos os descendentes.Isso funcionará também se um pai tiver vários filhos. No entanto, é necessário que cada registro atenda à condição
parent_id < id
, caso contrário, os resultados não serão completos.Atribuições variáveis dentro de uma consulta
Esta consulta usa sintaxe específica do MySQL: variáveis são atribuídas e modificadas durante sua execução. Algumas suposições são feitas sobre a ordem de execução:
from
cláusula é avaliada primeiro. Então é aí que é@pv
inicializado.where
cláusula é avaliada para cada registro na ordem de recuperação dosfrom
aliases. Portanto, é 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@pv
).where
cláusula são avaliadas em ordem e a avaliação é interrompida quando o resultado total é certo. Portanto, a segunda condição deve estar em segundo lugar, pois ela adicionaid
à lista pai, e isso só deve acontecer seid
passar na primeira condição. Alength
função é chamada apenas para garantir que essa condição seja sempre verdadeira, mesmo se apv
string, por algum motivo, produzir um valor falso.Em suma, pode-se achar essas suposições arriscadas demais para se confiar. A documentação alerta:
Portanto, mesmo que funcione de maneira consistente com a consulta acima, a ordem de avaliação ainda pode ser alterada, por exemplo, quando você adiciona condições ou usa essa consulta como uma exibição ou subconsulta em uma consulta maior. É um "recurso" que será removido em uma versão futura do MySQL :
Como mencionado acima, do MySQL 8.0 em diante, você deve usar a
with
sintaxe recursiva .Eficiência
Para conjuntos de dados muito grandes, essa solução pode ficar lenta, pois a
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 atinja um tamanho na mesma ordem de magnitude que o número de registros retornados.Alternativa 1:
with recursive
,connect by
Mais e mais bancos de dados implementam a sintaxe padrão SQL: 1999 ISO
WITH [RECURSIVE]
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 topo desta resposta para a sintaxe a ser usada.Alguns bancos de dados possuem uma sintaxe alternativa não padrão para pesquisas hierárquicas, como a
CONNECT BY
cláusula disponível no Oracle , DB2 , Informix , CUBRID e outros bancos de dados.O MySQL versão 5.7 não oferece esse recurso. Quando o mecanismo de banco de dados fornece essa sintaxe ou você pode migrar para um que sim, essa é certamente a melhor opção. Caso contrário, considere também as seguintes alternativas.
Alternativa 2: identificadores no estilo de caminho
As coisas se tornam muito mais fáceis se você atribuir
id
valores que contenham informações hierárquicas: um caminho. Por exemplo, no seu caso, isso pode ser assim:Então você
select
ficaria assim:Alternativa 3: auto-junções repetidas
Se você conhece um limite superior para a profundidade da sua árvore hierárquica, pode usar uma
sql
consulta padrão como esta:Veja este violino
A
where
condição especifica de qual pai você deseja recuperar os descendentes. Você pode estender essa consulta com mais níveis, conforme necessário.fonte
parent_id > id
que não poderá usar esta solução.WITH RECURSIVE
método, eu encontrei o seguinte artigo realmente útil com diferentes cenários, como a profundidade de recursão, distincts, e detectar e ciclos de fechamentoDo blog Gerenciando dados hierárquicos no MySQL
Estrutura da tabela
Inquerir:
Resultado
A maioria dos usuários, uma vez ou outra, lidou com dados hierárquicos em um banco de dados SQL e, sem dúvida, aprendeu que o gerenciamento de dados hierárquicos não é o objetivo do banco de dados relacional. As tabelas de um banco de dados relacional não são hierárquicas (como XML), mas são simplesmente uma lista simples. Os dados hierárquicos têm um relacionamento pai-filho que não é representado naturalmente em uma tabela de banco de dados relacional. Consulte Mais informação
Consulte o blog para mais detalhes.
EDITAR:
Resultado:
Referência: Como fazer a consulta SELECT Recursiva no Mysql?
fonte
Tente esse:
Definição da tabela:
Linhas experimentais:
Procedimento Recursivo Armazenado:
Função de invólucro para o procedimento armazenado:
Selecione um exemplo:
Resultado:
Filtrando linhas com determinado caminho:
Resultado:
fonte
(20, 'category2', 19), (21, 'category3', 20), (22, 'category4', 20),
A melhor abordagem que inventei é
Abordagem de linhagem descr. pode ser encontrado em qualquer lugar, por exemplo, Aqui ou aqui . Como função - foi isso que me inspirou.
No final, obteve uma solução mais ou menos simples, relativamente rápida e SIMPLES.
Corpo da função
E então você apenas
Espero que ajude alguém :)
fonte
Fiz o mesmo para outra frase aqui
Mysql select recursive get all child com nível múltiplo
A consulta será:
fonte
SELECT idFolder, (SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM ( SELECT @pv:=(SELECT GROUP_CONCAT(idFolder SEPARATOR ',') FROM Folder WHERE idFolderParent IN (@pv)) AS lv FROM Folder JOIN (SELECT @pv:= F1.idFolder )tmp WHERE idFolderParent IN (@pv)) a) from folder F1 where id > 10
; Eu não posso indicar F1.idFolder para @pvNULL
como resultado. Você sabe por que isso poderia ser? Existem pré-requisitos em termos de mecanismo de banco de dados ou algo mudou desde que você fez esta resposta que torna essa consulta desatualizada?Se você precisar de velocidade de leitura rápida, a melhor opção é usar uma tabela de fechamento. Uma tabela de fechamento contém uma linha para cada par ancestral / descendente. Portanto, no seu exemplo, a tabela de fechamento seria semelhante a
Depois de ter essa tabela, as consultas hierárquicas se tornam muito fáceis e rápidas. Para obter todos os descendentes da categoria 20:
Obviamente, há uma grande desvantagem sempre que você usa dados desnormalizados como este. Você precisa manter a tabela de fechamento ao lado da tabela de categorias. A melhor maneira é provavelmente usar gatilhos, mas é um pouco complexo rastrear corretamente inserções / atualizações / exclusões para tabelas de fechamento. Como em qualquer coisa, você precisa examinar seus requisitos e decidir qual abordagem é melhor para você.
Edit : See the question Quais são as opções para armazenar dados hierárquicos em um banco de dados relacional? para mais opções. Existem diferentes soluções ótimas para diferentes situações.
fonte
Consulta simples para listar os filhos da primeira recursão:
Resultado:
... com junção esquerda:
A solução do @tincot para listar todas as crianças:
Teste on-line com o Sql Fiddle e veja todos os resultados.
http://sqlfiddle.com/#!9/a318e3/4/0
fonte
Você pode fazer isso dessa maneira em outros bancos de dados com bastante facilidade com uma consulta recursiva (YMMV on performance).
A outra maneira de fazer isso é armazenar dois bits extras de dados, um valor esquerdo e direito. Os valores esquerdo e direito são derivados de um percurso de pré-ordem da estrutura em árvore que você está representando.
Isso é conhecido como Traversal de Árvore de Pré-encomenda Modificada e permite executar uma consulta simples para obter todos os valores pai de uma só vez. Também recebe o nome "conjunto aninhado".
fonte
Basta usar a classe BlueM / tree php para criar a árvore de uma tabela de auto-relação no mysql.
Aqui está um exemplo do uso do BlueM / tree:
fonte
É uma tabela de categorias .
Resultado::
fonte
É um pouco complicado, verifique se está funcionando para você
Link do violino do SQL http://www.sqlfiddle.com/#!2/e3cdf/2
Substitua por seu nome de campo e tabela adequadamente.
fonte
Algo não mencionado aqui, embora um pouco semelhante à segunda alternativa da resposta aceita, mas diferente e de baixo custo para consulta de grande hierarquia e itens fáceis (inserir atualização excluir), estaria adicionando uma coluna de caminho persistente para cada item.
alguns gostam:
Exemplo:
Otimize o comprimento do caminho e
ORDER BY path
usando a codificação base36 em vez do ID do caminho numérico realhttps://en.wikipedia.org/wiki/Base36
Suprimindo também a barra '/' separador usando comprimento fixo e preenchimento para o ID codificado
Explicação detalhada da otimização aqui: https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/
FAÇAM
construindo uma função ou procedimento para dividir o caminho para recuperar ancestrais de um item
fonte
base36
Isso funciona para mim, espero que funcione para você também. Ele fornecerá um conjunto de registros Raiz como Filho para qualquer Menu Específico. Mude o nome do campo conforme seus requisitos.
fonte
Eu achei mais fácil:
1) crie uma função que verificará se um item está em algum lugar na hierarquia pai de outro. Algo assim (não escreverei a função, faça-a com WHILE DO):
no seu exemplo
2) use uma sub-seleção, algo como isto:
fonte
Eu fiz uma consulta para você. Isso fornecerá uma categoria recursiva com uma única consulta:
Aqui está um violino .
fonte