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

Qual modelo hierárquico devo usar? Adjacência, aninhada ou enumerada?


Normalmente, existem três tipos de consultas nas hierarquias que causam problemas:
  1. Devolver todos os ancestrais
  2. Devolver todos os descendentes
  3. Retorne todos os filhos (descendentes imediatos).

Aqui está uma pequena tabela que mostra o desempenho de diferentes métodos em MySQL :
                        Ancestors  Descendants  Children        Maintainability InnoDB
Adjacency list          Good       Decent       Excellent       Easy            Yes
Nested sets (classic)   Poor       Excellent    Poor/Excellent  Very hard       Yes
Nested sets (spatial)   Excellent  Very good    Poor/Excellent  Very hard       No
Materialized path       Excellent  Very good    Poor/Excellent  Hard            Yes

Em children , poor/excellent significa que a resposta depende se você está misturando o método com a lista de adjacências, i. e. armazenando o parentID em cada registro.

Para sua tarefa, você precisa de todas as três consultas:
  1. Todos os ancestrais para mostrar a coisa Terra / Reino Unido / Devon
  2. Todas as crianças devem mostrar "Destinos na Europa" (os itens)
  3. Todos os descendentes devem mostrar "Destinos na Europa" (as contagens)

Eu iria por caminhos materializados, já que esse tipo de hierarquia raramente muda (só em caso de guerra, revolta etc).

Crie uma coluna varchar chamada path , indexe-o e preencha-o com o valor assim:
1:234:6345:45454:

onde os números são chaves primárias dos pais apropriados, na ordem correta (1 para a Europa, 234 para o Reino Unido etc.)

Você também precisará de uma tabela chamada levels para manter os números de 1 para 20 (ou qualquer nível máximo de aninhamento desejado).

Para selecionar todos os ancestrais:
SELECT   pa.*
FROM     places p
JOIN     levels l
ON       SUBSTRING_INDEX(p.path, ':', l.level) <> p.path
JOIN     places pa
ON       pa.path = CONCAT(SUBSTRING_INDEX(p.path, ':', l.level), ':') 
WHERE    p.id = @id_of_place_in_devon

Para selecionar todos os filhos e contagens de lugares dentro deles:
SELECT  pc.*, COUNT(pp.id)
FROM    places p
JOIN    places pc
ON      pc.parentId = p.id
JOIN    places pp
ON      pp.path BETWEEN pc.path AND CONCAT(pc.path, ':')
        AND pp.id NOT IN
        (
        SELECT  parentId
        FROM    places
        )
WHERE   p.id = @id_of_europe
GROUP BY
        pc.id