Israel Aéce

Microsoft MVP, MCP, MCAD, MCTS, MCPD e MCT

My Links

Blog Stats

Archives

Post Categories

Links

Login

Recursividade

Frequentemente tenho recebido e-mails pedindo a ajuda para realizar uma função para retornar os dados (na maioria das vezes tratado como categorias), em uma forma hierárquica, simulando por exemplo a navegação entre categorias, subcategorias e até mesmo sub-subcategorias. Exemplo: Informatica --> Computador --> Notebook.

Com isso, decidi criar um função recursiva que retorne esses dados e apresente para o usuário em um forma hierárquica. A idéia é termos uma tabela que contenha tres campos: CategoriaID, CategoriaIDRel e Nome. O campo CategoriaID é a chave primária da tabela. Se o campo CategoriaIDRel for Nulo, já saberemos que ele será o "pai", pois o registro que contenha o campo CategoriaIDRel Nulo, ele não está amarrado/não pertence à ninguém.

Como vemos na imagem abaixo, o campo CategoriaIDRel é relacionado com o campo CategoriaID:


Depois disso, temos que criar uma função recursiva para que seja retornado da Base de Dados os registros. Como é a primeira vez que a função é executada, ela retornará todos os registro, cujo campo CategoriaIDRel for igual à Nulo, com isso temos todos os registros "pai". Vejamos o código dessa função:

    Private _categorias As String
    Private _posicao As Integer

    Private Sub Categorias(ByRef _categorias As String, ByVal categoriaID As Integer, ByRef _posicao As Integer)
        Dim conn As SqlConnection = New SqlConnection("ConnectionString")
        Dim cmd As SqlCommand
        Dim dr As SqlDataReader
        Dim espacamento As String
        Dim i As Integer

        If categoriaID = 0 Then
            cmd = New SqlCommand("SELECT CategoriaID, Nome FROM Categorias WHERE CategoriaIDRel IS NULL ORDER BY Nome ASC", conn)
        Else
            cmd = New SqlCommand("SELECT CategoriaID, Nome FROM Categorias WHERE CategoriaIDRel = " & categoriaID & " ORDER BY Nome ASC", conn)
        End If

        For i = 0 To _posicao
            espacamento += " "
        Next

        Try
            conn.Open()
            dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
            While dr.Read()
                _categorias += espacamento & dr(1) & vbCrLf
                Categorias(_categorias, dr(0), (_posicao + 4))
            End While
        Catch ex As Exception
            MessageBox.Show("Erro.")
        Finally
            dr.Close()
            If conn.State = ConnectionState.Open Then
                conn.Close()
            End If
        End Try
    End Sub

Como podemos ver, a função chama ela mesma para que possa retornar as possíveis SubCategorias de um determinado registro. Temos também a variável _posicao que é responsável para efetuar o efeito de identação. Com isso, temos o efeito desejado, ou seja, uma hierarquia dos registros relacionados entre si. A imagem abaixo, mostra o resultado:



OBS.: Não me importei muito com a Query SQL, pois o correto é utilizar SqlParameters para executá-las. Não os utilizei, pois é somente para um exemplo.

posted on Thursday, March 25, 2004 1:53 AM

Feedback

# re: Recursividade 3/25/2004 12:06 PM José Almeida

Porreiro!

Uma outra abordagem é implementar um Composite (Design Patterns, Gamma et al.). Um Composite serve exactamente para compor objectos em estruturas hierarquicas tipo árvore.

Cheers!

# re: Recursividade 3/25/2004 12:09 PM Israel Aéce

Olá José,

Legal, mas ainda estou dando os primeiros passos em Design Patterns... Alias, preciso pegar firme nisso, pois acho que um sistema desenvolvido com elas torno-o além de profissional bastante elegante.

Abraços

# re: Recursividade 3/25/2004 10:28 PM Paulo Correia

Israel,

Uma abordagem que costuma ser mais eficiente (não vi se tem design patterns com isso), é carregar toda a estrutura que importa num dataset e fazer as consultas dentro do dataset. Desta forma como a consulta é feita na memória, é muito mais rápido e eficaz na utilização dos recursos da tua máquina. Já que evita teres que criar um DataReader para cada filho.

Abraços
Paulo Correia

# re: Recursividade 3/26/2004 12:05 AM Israel Aéce

Olá Paulo,

Pois é. DataSet?!? Mas ai te pergunto: E se essa estrutura for para ser exibida, acho o DataReader mais ideal visto que ele é mais rápido que o DataSet. Uma das grandes coisas que vejo nas listas e nos foruns, é que o pessoal costuma utilizar o DataSet para um simples "Bind" em algum controle por questão talvez até de comodidade.

Podemos também utilizar o Cache, para que a performance seja melhor.

É Paulo DataSet vs. DataReader dá muitas discussões :) alias, gostaria mesmo de discutir isso, pois ainda tenho uma série de dúvidas, pois ao meu ver, utilizaria o DataSet, somente quando a conexão com a internet não seria contínua ou o cliente sairia à campo com uma "cópia" dos dados (offline) e quando quiser, reestabelecia a conexão com a internet e atutalizasse a Base de Dados com esses novos dados.

Do contrário os DataReaders seriam mais rádpidos, e utilizando a combinação Custom Collections e Cache a performance é ótima.

# re: Recursividade 3/26/2004 5:02 AM João Paulo Carreiro

Israel, se tiveres 10 subcategorias iras a DB 11 vez ( uma pra ROOT).

Isto é terrivelmente ineficaz. O melhor é mesmo ir buscar tudo de uma vez e depois construir a "arvore".

Uma coisa a ter em atencao, se quizeremos ser picuinhas, é que funcoes recursivas normalmente sao a solucao mais facil mas menos eficiente.

É possivel alterar a maior parte de funcoes recursivas para serem loops, e o codigo nao perde legibilidade.

# re: Recursividade 3/26/2004 9:02 AM Israel Aéce

Olá João (quem é vivo sempre aparece :P)

Realmente está coberto de razão, ou seja, se tivermos N registros, haverão N conexões com a DB.

Mas para melhorar isso, talvez poderia implementar de uma forma que manteria a conexão aberta com a DB até o final da construção da "arvore".

# re: Recursividade 3/28/2004 1:28 AM Paulo Correia

Quando falei de DataSet era por isso mesmo.

O DataReader é muito mais rápido no acesso que usar um DataAdapter para fazer o Fill num DataSet. No entanto tu irás fazer vários acessos ao banco, enquanto com o DataSet farás apenas um.

Mesmo deixando a conexão aberta, os recursos consumidos são muito grandes. Não te esqueças que é uma conexão por user. Logo num exemplo básico de árvore com 10 nós, irás fazer 11 acessos por user, multiplicando por 10 users, por exemplo, sao 110 acesso ao banco. Na forma com o DataSet, farás apenas 10 acessos, um para cada user.

Talvez desta forma a diferença de performance seja mais evidente.

Abraços
Paulo Correia

# re: Recursividade 5/16/2007 6:02 PM Rafael - ( MSN: manojowr@hotmail.com

Caro Israel este artigo esta me ajudando muito, porem preciso jogar este resultado em um arquivo XML, poderia me ajudar?

# re: Recursividade 5/17/2007 9:20 AM Israel Aece

Ola Rafael,

Como o JPC falou acima, você pode salvar em um local temporário e depois persistir isso em um arquivo.

Att,

# re: Recursividade 5/17/2007 7:30 PM Rafael - ( MSN: manojowr@hotmail.com

Ola Israel, ja joquei ele em um local temporario mas nao estou achando nenhum artigo na net que me mostre como retirar ele deste local temporario e jogar no XML.

Valeu pela ajuda.

# re: Recursividade 5/20/2007 3:11 AM Israel Aece

Ola Rafael,

Já tentou dar uma olhada no namespace System.Xml?

# re: Recursividade 7/25/2007 11:34 PM Rafael

Consequi heheheh.. fiz varias pesquisas na net e deu certo... se alguem estive com duvida me add no msn: Manojowr@Hotmail.com, talvez possa ajudar.

Title  
Name  
Url
Box Code
Protected by FormShield
Comments