Como sempre:não há melhor solução. Cada solução torna coisas diferentes mais fáceis ou mais difíceis. A solução certa para você depende de qual operação você fará mais.
Abordagem ingênua com parent-id:
Prós:
-
Fácil de implementar
-
Fácil de mover uma grande subárvore para outro pai
-
A inserção é barata
-
Campos necessários diretamente acessíveis em SQL
Contras:
-
A recuperação de uma árvore inteira é recursiva e, portanto, cara
-
Encontrar todos os pais também é caro (o SQL não conhece recursões...)
Percurso de árvore de pré-encomenda modificado (salvando um ponto inicial e final):
Prós:
-
Recuperar uma árvore inteira é fácil e barato
-
Encontrar todos os pais é barato
-
Campos necessários diretamente acessíveis em SQL
-
Bônus:você está salvando a ordem dos childnodes dentro do parentnode também
Contras:
- Inserir/atualizar pode ser muito caro, pois talvez você precise atualizar muitos nós
Salvar um caminho em cada nó:
Prós:
-
Encontrar todos os pais é barato
-
Recuperar uma árvore inteira é barato
-
A inserção é barata
Contras:
-
Mover uma árvore inteira é caro
-
Dependendo da maneira como você salvar o caminho, você não poderá trabalhar com ele diretamente no SQL, então você sempre precisará buscá-lo e analisá-lo, se quiser alterá-lo.
Tabela de encerramento
Prós:
-
Fácil de implementar
-
Encontrar todos os pais é barato
-
A inserção é barata
-
Recuperar árvores inteiras é barato
Contras:
-
Precisa de uma mesa adicional
-
Ocupa muito espaço em comparação com outras abordagens
-
Mover uma subárvore é caro
Eu preferiria um dos dois últimos, dependendo da frequência com que os dados mudam.
Veja também:http://media.pragprog.com/titles/bksqla/trees. pdf