Um caso de recursividade
Autor: Fabiano Cores | Publicado: 2010-12-23T01:22:00-02:00

Aproveitando um recente trabalho realizado, gostaria de descrever um relatório que rapidamente desenvolvi em uma base de dados SQL Server. A situação era a seguinte: eu havia importado a estrutura de um produto, e precisava exibir esta estrutura na forma de um relatório. Primeiro, vamos entender o termo “estrutura de um produto”. A empresa em questão é uma fábrica de impressoras de plotagem (1,8m a 3,2m). A ficha técnica da impressora é composta por vários níveis de componentes. Vou ilustrar uma composição: &nbsp;  Impressora XPTO modelo 1  Conjunto eletrônica XPTO 1804  Tomada de embutir 3 pinos Cinta nylon média Fusível pequeno 10A Sensor de temperatura Ventilador – secagem  Espaguete termo retrátil Ventilador 120x120x38 c/ rosca Conector modular miniatura H89F09 Blower – vácuo  Blower ( Siroco ) AD xxxx 24v Conector modular miniatura H90F23 Aquecimento – cabeças  Resistor Fio 120R x 10W Cabo paralelo 0,20mm Apoio de silicone 13mm x 4mm Terminal KK pequeno importado &nbsp; O relatório precisa aparecer formatado como está acima (indentado nos níveis). Porém, a quantidade de níveis é indefinida. Desta forma, utilizei o conceito de “recursividade”. Definições de recursividade na internet:  Recursão é um método de programação no qual uma função pode chamar a si mesma. O termo é usado de maneira mais geral para descrever o processo de repetição de um objeto de um jeito similar ao que já fora mostrado. ...pt.wikipedia.org/wiki/Recursividade A recursividade na programação de computadores envolve a definição de uma função que pode invocar a si própria. Um exemplo de aplicação da recursividade pode ser encontrado nos analisadores gramaticais recursivos para linguagens de programação. ...pt.wikipedia.org/wiki/Recursividade_(ciência_da_computação) iteratividade; propriedade de formar infinitos elementos à partir de finitos aplicando-se regras lógicas ou matemáticaspt.wiktionary.org/wiki/Recursividade que chama a si mesmo (programa ou função)pt.wiktionary.org/wiki/Recursivo Para desenvolver este relatório, eu utilizei este conceito de recursividade. Eu criei um procedimento insere os componentes, e para cada componente que está inserindo, chama a si mesmo para repetir a explosão, independente da quantidade de níveis que houver na estrutura.O primeiro código que vamos analisar é o que retorna o relatório ao usuário. Ele somente recebe os parâmetros, e chama o procedimento de explosão da estrutura, chamado spcAdhExpEst (procedimento recursivo).   Code Snippet   &nbsp; CREATE proc spcAdhExplodeProduto   @cd_item char(13)    as &nbsp;   /*    Autor: Fabiano Cores    Data : 23/12/2010    Site : http://sqlburger.blogspot.com    Desenvolvido para demonstrar a utilização de procedimentos    recursivos no SQL Server. A reprodução deste artigo só é     permitida se forem mantidos os créditos do autor.    */ &nbsp;   -- Esta tabela possui papel fundamental na recursividade. Esta   -- tabela irá existir durante toda a execução deste procedimento   -- mesmo quando chamamos executamos um segundo procedimento.    create table #retorno (     cd_componente char(18) collate database_default null   , qt_aplicada decimal(10,4) null   , nivel smallint null   , ordem int not null identity)      -- Insiro o item pai, para que o usuário saiba de qual produto   -- está tirando o relatório   insert into #retorno (cd_componente, qt_aplicada, nivel)     select @cd_item, 1, 1      -- Este é o procedimento recursivo. Ele fará referencia a ele   -- mesmo, enquanto preenche a tabela #retorno   exec spcAdhExpEst @cd_item &nbsp;   -- Aqui, eu pego os registros da tabela #retorno, e retorno   -- ao software .NET, para exibição ao usuário.   select convert(char(18), space((nivel-1) * 2) + a.cd_Componente) as &#39;Código&#39;        , convert(char(65), space((nivel-1) * 2) + b.descricao) as &#39;Descrição&#39;        , a.qt_aplicada &#39;Quantidade&#39;        , a.nivel -1 as &#39;Nível&#39;     from #retorno a, item b    where a.cd_componente = b.cd_item    order by a.ordem &nbsp; --      Este é o código do procedimento recursivo. Ele insere na tabela #retorno, que foi criada pelo seu procedimento antecessor (spcAdhExplodeEstrutura), e faz referência a si mesma:    Code Snippet   --  &nbsp; CREATE proc spcAdhExpEst   @cd_item char(13)    as &nbsp;    /*    Autor: Fabiano Cores    Data : 23/12/2010    Site : http://sqlburger.blogspot.com    Desenvolvido para demonstrar a utilização de procedimentos    recursivos no SQL Server. A reprodução deste artigo só é     permitida se forem mantidos os créditos do autor.    */ &nbsp;   declare @vcd_componente char(13)   declare @vqt_aplicada decimal(19,4) &nbsp;   -- Insere os componentes simples (não possuem estrutura abaixo)   insert into #retorno (cd_componente, qt_aplicada, nivel)     select a.cd_componente, a.qt_aplicada, @@nestlevel       from processoitem a, item b      where a.cd_item = @cd_item        and a.cd_componente = b.cd_item        and not exists (select 0                          from processoitem c                         where c.cd_item = a.cd_componente)      order by b.descricao &nbsp;   -- Aqui é montado um cursor nos itens complexos (que possuem   -- estrutura). É utilizado um cursor por que para cada item   -- que possui estrutura, eu vou fazer a chamada recursiva.   declare c1 cursor local fast_forward read_only for     select a.cd_componente, a.qt_aplicada       from processoitem a, item b      where a.cd_item = @cd_item        and a.cd_componente = b.cd_item        and     exists (select 0                          from processoitem c                         where c.cd_item = a.cd_componente)      order by b.descricao         open c1      while 1 = 1   begin     fetch next from c1 into @vcd_componente, @vqt_aplicada     if @@fetch_status &lt;&gt; 0 break          insert into #retorno (cd_componente, qt_aplicada, nivel)       select @vcd_componente, @vqt_aplicada, @@nestlevel            -- Chamada recursiva (executando ele mesmo)     exec spcAdhExpEst @vcd_componente        end   close c1   deallocate c1 &nbsp; --     &nbsp; Detalhes importantes que devem ser observados: 1. A tabela temporária foi criada no primeiro procedimento. Se ela fosse criada no procedimento recursivo, daria erro, pois ele tentaria criar mais de uma vez a mesma tabela, desta forma, ela sendo criada apenas uma vez pelo procedimento pai, não haverá problemas. 2. O procedimento filho (spcAdhExpEst) consegue enxergar a tabela #retorno. 3. A variável @@nestlevel é uma variável de ambiente, que retorna o nível de aninhamento* da execução do procedimento. Isto é uma forma de saber quantas vezes o procedimento já foi disparado por ele mesmo (na primeira execução, @@nestlevel é igual a 0). Aproveitei esta variável para incluir no relatório a que nível o componente pertence, e também a utilizei como parâmetro para adicionar espaços à esquerda do código e da descrição, para melhorar visualmente a identificação dos níveis na leitura do relatório.*Definições de aninhar na internet:  aninhado - Quando se tem um objeto dentro do outro, como uma tabela, por exemplo , você possui um aninhamento , ou seja, um objeto funcionando como ninho para outro objeto.www.juliobattisti.com.br/tutoriais/keniareis/dicionarioinfo001.asp &nbsp;   &nbsp; Eu estou postando esta dica por que hoje tive uma experiência muito desagradável… um certo programador teve a capacidade de fazer 9 cursores (loops), um dentro do outro, para tratar possíveis 9 níveis do produto. Além de ser um código absurdamente mal pensado, é gigantesco, lento, péssimo de realizar manutenção, entre outros comentários que não vêem ao caso. E como padrão, quando pego código assim, tenho o hábito de selecionar tudo, apagar, refazer… e claro, compartilhar!