Um esquema de otimização de consulta de subcategoria para classificação de vários níveis

fundo

No projeto, muitas vezes encontramos problemas de consulta de subcategoria de classificação multinível, como estrutura organizacional, menu multinível, tipo de mercadoria, etc. A estrutura de dados comum é a seguinte:

imagem.png

Problemas com este projeto:

  • A consulta de subcategoria para classificação multinível precisa ser implementada por meio de recursão, a complexidade de tempo é 0(n) e a eficiência é relativamente baixa.

  • A recursão muito profunda pode facilmente causar estouro de pilha.

    Portanto, para consulta de subcategoria de classificação em vários níveis, existe uma solução melhor? Podemos resolver o problema de consulta de subcategoria de classificação multinível pré-ordenando o algoritmo de travessia de árvore (MPTT).

Introdução

MPTT (Modified Preorder Tree Taversal) algoritmo de árvore de pré-ordenação de pré-ordenação, usado principalmente no armazenamento e travessia de relacionamentos hierárquicos.

Fundamental

O princípio do algoritmo de árvore transversal de pré-ordenação: a relação do nível da árvore é representada pelo valor dos nós esquerdo e direito do número.

Como mostrado abaixo:

imagem.png

A estrutura em árvore é convertida nos dados de estrutura do banco de dados da seguinte forma:

imagem.png

Descrição: A estrutura de dados MPTT contém informações como nó esquerdo, nó direito, nível, nó pai, etc.

  • treeid: O id da árvore é usado para identificar uma árvore no banco de dados.
  • nível: indica o nível da árvore, o nível do nó raiz é 1 e o nível do nó filho é o nível do nó pai mais 1.
  • parentId: ID do nó pai, já que o nó raiz não tem nó pai, o valor é -1.
  • lftNode: valor do nó esquerdo.
  • rgtNode: valor do nó direito.

Aplicativo básico

atravessar a árvore inteira

Para percorrer toda a árvore, basta consultar a condição treeId.

Localizar todos os nós descendentes em um nó

Para encontrar todos os nós descendentes em Fruit, você só precisa descobrir que o valor do nó esquerdo é maior que 2 e o valor do nó direito é menor que 11.

SELECT * FROM t_tree  t WHERE t.`lftNode`>2 AND t.`rgtNode`<11;
复制代码

resultado da pesquisa

imagem.png

Uma consulta com uma eficiência de 0(1) é muito mais eficiente do que uma consulta recursiva.

Localizar todos os nós filhos em um nó

Para encontrar todos os subpontos em Frutas, você só precisa consultar o nível igual a 2

SELECT * FROM t_tree  t  WHERE t.`level`='2';
复制代码

resultado da pesquisa

imagem.png

Encontrar o caminho de um nó

Para encontrar o caminho do nó Banana, você só precisa consultar se o nó esquerdo é menor que 8 e o nó direito é maior que 9.

SELECT * FROM t_tree  t WHERE t.`lftNode`<8 AND t.`rgtNode`>9;
复制代码

resultado da pesquisa

imagem.png

Através do resultado da consulta, o caminho do nó da Banana é: Food->Fruit->Yellow

Quantos descendentes tem um nó?

Encontre o número de todos os nós descendentes em Fruit, a fórmula específica é a seguinte:

 子孙总数=(右值-左值-1)/2 
复制代码

Através do cálculo da fórmula, sabemos que o número total de nós descendentes de Fruit é (18-1-1)/2=8

Adicionar nó filho

Embora a eficiência da classificação pré-compilada seja muito alta em nós de consulta, ela é muito ineficiente em adicionar e excluir nós, porque adicionar e excluir nós requer recálculo e movimentação de nós, os quais serão mais lentos.

implementar lógica

  • Adicione um novo nó a uma árvore que não existe e crie uma nova árvore. Então o parentid é -1, o nível é 1 e o treeId é baseado no treeId máximo da árvore existente mais 1.

  • Adicione um novo nó a uma árvore existente. parentId é o id do nó pai, level é o nível do nó pai mais 1 e treeId é o mesmo que o nó pai.

  • Repare o valor esquerdo de outros nós cujo saldo está quebrado e adicione 2 ao valor esquerdo de todos os nós maiores que o valor direito de parentId.

  • Repare os rvalues ​​de outros nós cujo saldo está quebrado e adicione 2 aos rvalues ​​de todos os nós maiores que o rvalue de parentId.

Implementação

DELIMITER $$

DROP PROCEDURE IF EXISTS `SP_TREE_ADD`$$

CREATE PROCEDURE `SP_TREE_ADD`(
IN p_oid INT (11),
IN p_name VARCHAR (30),
IN p_parentOid INT (11)
)
BEGIN
       #获取父节点的相关数据信息
        SELECT @myRight :=rgtNode,@oid :=oid,@level :=LEVEL,@treeid :=treeid FROM t_tree t WHERE oid = p_parentOid;
        #大于parentId右值的所有节点的右值加2。
	UPDATE t_tree SET rgtNode = rgtNode + 2 WHERE rgtNode > @myRight;
        #大于parentId右值的所有节点的左值加2
        UPDATE t_tree SET lftNode = lftNode + 2 WHERE lftNode > @myRight;
        
        UPDATE t_tree SET rgtNode=rgtNode+2 WHERE oid=@oid;
        #插入数据
	INSERT INTO t_tree(oid,NAME,parentOid,LEVEL,lftNode,rgtNode,treeid) 
	VALUES(p_oid, p_name, p_parentOid,@level+1,@myRight, @myRight +1,@treeid);
END$$

DELIMITER ;
复制代码

teste

Adicione um subnó de maçã no nó Vermelho e execute o procedimento armazenado acima

CALL SP_TREE_ADD('11','apple','3');
复制代码

Após a inserção bem-sucedida, o resultado é o seguinte

imagem.png

Para verificar novamente o número de nós em Vermelho, você só precisa consultar se o nó esquerdo for maior que 3 e o nó direito for menor que 8.

SELECT * FROM t_tree  t WHERE t.`lftNode`>3 AND t.`rgtNode`<8;
复制代码

imagem.png

excluir nó filho

implementar lógica

  • Excluir nós cujo valor esquerdo é maior que o nó pai e menor que o valor direito
  • Repare o valor esquerdo de outros nós cujo saldo está quebrado e subtraia o valor da diferença do valor esquerdo de todos os nós que são maiores que o valor direito de parentId.
  • Repare os rvalues ​​de outros nós cujo saldo está quebrado e subtraia a diferença dos rvalues ​​de todos os nós que são maiores que o rvalue de parentId.

Implementação

DELIMITER $$
DROP PROCEDURE IF EXISTS `SP_TREE_DEETE`$$

CREATE PROCEDURE `SP_TREE_DEETE`(
       IN p_Oid INT (11)
    )
BEGIN      
        SELECT 
            @myleft := lftNode,
            @myright :=rgtNode,
            @oid :=oid,
            @mywidth := rgtNode-lftNode+1
        FROM t_tree WHERE oid = p_Oid;
        
        DELETE FROM t_tree WHERE lftNode BETWEEN @myleft AND @myright;
        UPDATE t_tree SET rgtNode = rgtNode - @mywidth WHERE rgtNode > @myright; 
        UPDATE t_tree SET lftNode = lftNode - @mywidth WHERE lftNode > @myright;
    END$$

DELIMITER ;
复制代码

teste

Exclua o subnó da maçã no nó Vermelho e execute o procedimento armazenado acima.

CALL SP_TREE_DEETE(11);
复制代码

O resultado após a exclusão é o seguinte

imagem.png

Para verificar novamente o número de nós em Vermelho, você só precisa consultar se o nó esquerdo for maior que 3 e o nó direito for menor que 6.

SELECT * FROM t_tree  t WHERE t.`lftNode`>3 AND t.`rgtNode`<6;
复制代码

imagem.png

Vantagens e desvantagens

vantagem

A eficiência da consulta de árvore transversal de pré-classificação é alta, a eficiência da consulta não é afetada pelo aumento do nível de classificação e é adequada para cenários com muitas operações de consulta.

deficiência

Adicionar e excluir requer recalcular os valores esquerdo e direito do nó, portanto, a eficiência de execução é baixa

Resumir

Este artigo explica o algoritmo de pré-ordenação. Nenhuma solução é perfeita. Precisamos dominar suas vantagens e desvantagens. Em projetos reais, precisamos escolher a solução adequada de acordo com cenários de negócios específicos.

Estou participando do recrutamento do programa de assinatura de criadores da Comunidade de Tecnologia Nuggets, clique no link para se cadastrar e enviar .

Acho que você gosta

Origin juejin.im/post/7119407970491826190
Recomendado
Clasificación