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.