Boas visões gerais
De um modo geral, você está tomando uma decisão entre tempos de leitura rápidos (por exemplo, conjunto aninhado) ou tempos de gravação rápidos (lista de adjacências). Geralmente, você acaba com uma combinação das opções abaixo que melhor atendem às suas necessidades. A seguir, é apresentada uma leitura aprofundada:
- Mais uma comparação de intervalos aninhados vs. lista de adjacências : a melhor comparação que encontrei da lista de adjacências, caminho materializado, conjunto aninhado e intervalo aninhado.
- Modelos para dados hierárquicos : slides com boas explicações sobre tradeoffs e exemplos de uso
- Representando hierarquias no MySQL : visão geral muito boa do Nested Set em particular
- Dados hierárquicos em RDBMSs : conjunto de links mais abrangente e bem organizado que eu já vi, mas não muito na explicação
Opções
Conheço e tenho características gerais:
- Lista de adjacências :
- Colunas: ID, ParentID
- Fácil de implementar.
- O nó barato move, insere e exclui.
- Caro para encontrar o nível, ascendência e descendentes, caminho
- Evite N + 1 por meio de expressões de tabela comuns em bancos de dados que os suportam
- Conjunto aninhado (também conhecido como Traversal de árvore de pré-encomenda modificada )
- Colunas: Esquerda, Direita
- Ascendência barata, descendentes
O(n/2)
Movimentos, inserções e exclusões muito caros devido à codificação volátil
- Tabela de ponte (também conhecida como tabela de fechamento / gatilhos w )
- Usa tabela de junção separada com: ancestral, descendente, profundidade (opcional)
- Ascendência e descendentes baratos
- Escreve custos
O(log n)
(tamanho da subárvore) para inserção, atualizações e exclusões - Codificação normalizada: boa para estatísticas RDBMS e planejador de consultas em junções
- Requer várias linhas por nó
- Coluna de linhagem (também conhecida como caminho materializado , enumeração de caminho)
- Coluna: linhagem (por exemplo, / pai / filho / neto / etc ...)
- Descendentes baratos via consulta de prefixo (por exemplo
LEFT(lineage, #) = '/enumerated/path'
) - Escreve custos
O(log n)
(tamanho da subárvore) para inserção, atualizações e exclusões - Não relacional: depende do tipo de dados Array ou formato de seqüência de caracteres serializada
- Intervalos aninhados
- Como o conjunto aninhado, mas com real / float / decimal para que a codificação não seja volátil (movimentação / inserção / exclusão de baixo custo)
- Tem problemas reais / de flutuação / representação decimal / precisão
- A variante de codificação da matriz adiciona a codificação ancestral (caminho materializado) para "livre", mas com a dificuldade adicional da álgebra linear.
- Mesa plana
- Uma lista de adjacências modificada que adiciona uma coluna de nível e classificação (por exemplo, pedido) a cada registro.
- Barato para iterar / paginar
- Movimentação e exclusão caras
- Bom uso: discussão por tópicos - fóruns / comentários do blog
- Várias colunas de linhagem
- Colunas: uma para cada nível de linhagem, refere-se a todos os pais até a raiz, os níveis inferiores ao nível do item são definidos como NULL
- Ancestrais baratos, descendentes, nível
- Inserção barata, excluir, mover as folhas
- Inserção cara, exclusão, movimentação dos nós internos
- Limite rígido para a profundidade da hierarquia
Notas específicas do banco de dados
MySQL
Oráculo
- Use CONNECT BY para percorrer as Listas de Adjacência
PostgreSQL
- tipo de dados ltree para caminho materializado
servidor SQL
- Resumo geral
- O ano de 2008 oferece que o tipo de dados HierarchyId parece ajudar na abordagem da coluna Lineage e expandir a profundidade que pode ser representada.
sql
database
tree
relational-database
hierarchical-data
orangepips
fonte
fonte
Closure Tables
são superiores aAdjacency List
,Path Enumeration
eNested Sets
em termos de facilidade de uso (e eu estou supondo que o desempenho também).Respostas:
Minha resposta favorita é a sugerida pela primeira frase deste tópico. Use uma lista de adjacências para manter a hierarquia e use conjuntos aninhados para consultar a hierarquia.
O problema até agora é que o método de cobertura de uma lista de adjacências para conjuntos aninhados tem sido terrivelmente lento porque a maioria das pessoas usa o método RBAR extremo conhecido como "Push Stack" para fazer a conversão e é considerado caro demais para alcançar o Nirvana da simplicidade de manutenção da Lista de Adjacências e do incrível desempenho dos Conjuntos Aninhados. Como resultado, a maioria das pessoas acaba tendo que se contentar com um ou outro, especialmente se houver mais do que, digamos, uns péssimos 100.000 nós. O uso do método push stack pode levar um dia inteiro para fazer a conversão no que os MLM'ers considerariam uma pequena hierarquia de milhões de nós.
Eu pensei em dar à Celko um pouco de concorrência criando um método para converter uma Lista de Adjacências em conjuntos aninhados em velocidades que parecem impossíveis. Aqui está o desempenho do método push stack no meu laptop i5.
E aqui está a duração do novo método (com o método push stack entre parênteses).
Sim esta correto. 1 milhão de nós convertidos em menos de um minuto e 100.000 nós em menos de 4 segundos.
Você pode ler sobre o novo método e obter uma cópia do código no seguinte URL. http://www.sqlservercentral.com/articles/Hierarchy/94040/
Também desenvolvi uma hierarquia "pré-agregada" usando métodos semelhantes. Os MLM e as pessoas que fazem listas de materiais estarão particularmente interessados neste artigo. http://www.sqlservercentral.com/articles/T-SQL/94570/
Se você der uma olhada em qualquer um dos artigos, vá para o link "Participar da discussão" e deixe-me saber o que você pensa.
fonte
Esta é uma resposta muito parcial à sua pergunta, mas espero que ainda seja útil.
O Microsoft SQL Server 2008 implementa dois recursos extremamente úteis para gerenciar dados hierárquicos:
Veja "Modelar suas hierarquias de dados com o SQL Server 2008", de Kent Tegels, no MSDN, para iniciar. Consulte também minha própria pergunta: Consulta recursiva da mesma tabela no SQL Server 2008
fonte
Este design ainda não foi mencionado:
Várias colunas de linhagem
Embora tenha limitações, se você pode suportá-las, é muito simples e muito eficiente. Recursos:
Segue um exemplo - árvore taxonômica dos pássaros, de modo que a hierarquia é Classe / Ordem / Família / Gênero / Espécie - a espécie é o nível mais baixo, 1 linha = 1 táxon (que corresponde às espécies no caso dos nós das folhas):
e o exemplo dos dados:
Isso é ótimo porque, dessa maneira, você realiza todas as operações necessárias de uma maneira muito fácil, desde que as categorias internas não alterem seu nível na árvore.
fonte
Modelo de adjacência + Modelo de conjuntos aninhados
Fui a ele porque eu poderia inserir novos itens na árvore facilmente (você só precisa da identificação de um ramo para inserir um novo item) e também consultá-lo rapidamente.
parent
coluna.lft
entrelft
ergt
do pai.lft
menos do que o nólft
ergt
maiores que o nórgt
e classifique-os porparent
.Eu precisava tornar o acesso e a consulta à árvore mais rápidos do que as inserções, por isso escolhi esse
O único problema é corrigir as colunas
left
eright
ao inserir novos itens. bem, eu criei um procedimento armazenado para ele e o chamei toda vez que inseri um novo item que era raro no meu caso, mas é muito rápido. Eu obtive a idéia do livro de Joe Celko, e o procedimento armazenado e como eu o criei são explicados aqui no DBA SE https://dba.stackexchange.com/q/89051/41481fonte
children
edescendants
.left
eright
são usados para encontrar os descendentes.Se o seu banco de dados suportar matrizes, você também poderá implementar uma coluna de linhagem ou caminho materializado como uma matriz de IDs pai.
Especificamente com o Postgres, você pode usar os operadores set para consultar a hierarquia e obter um excelente desempenho com os índices GIN. Isso torna a localização de pais, filhos e profundidade bastante trivial em uma única consulta. As atualizações também são bastante gerenciáveis.
Tenho uma descrição completa do uso de matrizes para caminhos materializados, se você estiver curioso.
fonte
Esta é realmente uma questão de estaca quadrada, furo redondo.
Se bancos de dados relacionais e SQL são o único martelo que você tem ou deseja usar, as respostas postadas até agora são adequadas. No entanto, por que não usar uma ferramenta projetada para lidar com dados hierárquicos? O banco de dados de gráficos é ideal para dados hierárquicos complexos.
As ineficiências do modelo relacional, juntamente com as complexidades de qualquer solução de código / consulta para mapear um modelo gráfico / hierárquico em um modelo relacional, simplesmente não compensa o esforço, quando comparado à facilidade com que uma solução de banco de dados gráfico pode resolver o mesmo problema.
Considere uma lista de materiais como uma estrutura de dados hierárquica comum.
Caminho mais curto entre dois subconjuntos : algoritmo transversal de gráfico simples. Caminhos aceitáveis podem ser qualificados com base em critérios.
Semelhança : Qual é o grau de semelhança entre duas montagens? Execute um percurso nas duas subárvores calculando a interseção e a união das duas subárvores. O percentual semelhante é a interseção dividida pela união.
Fechamento transitivo : Percorra a subárvore e resuma o (s) campo (s) de interesse, por exemplo: "Quanto alumínio há em uma submontagem?"
Sim, você pode resolver o problema com o SQL e um banco de dados relacional. No entanto, existem abordagens muito melhores se você estiver disposto a usar a ferramenta certa para o trabalho.
fonte
Estou usando o PostgreSQL com tabelas de fechamento para minhas hierarquias. Eu tenho um procedimento armazenado universal para todo o banco de dados:
Em seguida, para cada tabela em que tenho uma hierarquia, crio um gatilho
Para preencher uma tabela de fechamento da hierarquia existente, eu uso este procedimento armazenado:
As tabelas de fechamento são definidas com 3 colunas - ANCESTOR_ID, DESCENDANT_ID, DEPTH. É possível (e até aconselho) armazenar registros com o mesmo valor para ANCESTOR e DESCENDANT e um valor zero para DEPTH. Isso simplificará as consultas para recuperação da hierarquia. E eles são realmente muito simples:
fonte