 VB 
- Exibindo dados no layout Mestre - Detalhes
VB 
- Exibindo dados no layout Mestre - Detalhes
Recebo muitas consultas sobre como exibir dados relacionados entre tabelas , o clássico layout Mestre/Detalhe). Na verdade a dificuldade as vezes não está em como fazer isto no VB mas em ter as tabelas normalizadas e prontas para que isto seja possível.
Se você estiver tentando exibir 
dados entre tabelas relacionadas e elas não estão relacionadas fica difícil não 
é mesmo ...
Vou mostrar como fazer isto usando ADO , depois DAO e a seguir no VB.NET. Lembrando que existem muitas outras formas de realizar esta tarefa.
Preparando as tabelas
Eu não vou perder tempo criando e normalizando tabelas vou pegar o banco de dados Nwind.mdb que já vem com o Access e com o VB e usar duas tabelas contidas neste banco de dados e que já estão relacionadas. Abaixo estou exibindo o relacionamento entre as tabelas do banco de dados Nwind.mdb.

Vou dar um zoom nas tabelas Pedidos e Detalhes do Pedido para ver o relacionamento entre elas:
|  | - Existe um 
    relacionamento entre a tabela Detalhes do Pedido e Pedidos do tipo : UM para 
    MUITOS ( 1-N ) - Temos a coluna NúmeroDoPedido como chave primária nas duas tabelas - A coluna NúmeroDoPedido estão relacionadas entre as duas tabelas Para cada registro na tabela Pedidos existem n registros na tabela Detalhes do Pedido relacionados pela coluna NúmeroDoPedido | 
Como podemos ver as tabelas estão preparadas e prontos para serem usadas. Espero que você entenda que a normalização de dados e o relacionamento entre as tabelas é fundamental para que possamos trabalhar com as tabelas no layout Mestre/Detalhe.
Agora você vai ver como é fácil tratar os dados no VB. Vou começar usando a tecnologia ADO...
Exibindo dados relacionados com ADO
Vou usar os controles vinculados para mostrar como é fácil acessar e exibir dados com ADO. Você já deve saber que o para ADO temos o controle de dados vinculado ADO Data Control - Adodc . Vou usar também o controle Data Grid para exibir os dados no formato de uma grade de dados. (Não podemos usar o DBGrid com o Adodc).
Nosso projeto constará de dois ado data control - adodc , dois datagrids e uma label ,conforme o layout abaixo:
No menu Project | Components inclua os controles Microsoft Ado Data Control e Microsot Data Grid Control no seu projeto.

Percebeu que eu destaquei a coluna Sub Total e a label - Total para o Pedido ? Pois bem , além de exibir os dados relacionados entre as tabelas Pedidos e Detalhes do Pedido vou calcular o subtotal para cada número pedido e o total para o pedido usando comandos SQL.
Na seção General Declarations do formulário vou declarar as variáveis objeto usadas para a conexão e o recordset:
| Option 
    Explicit 'define as variáveis objetos usadas no projeto Dim conn As ADODB.Connection Dim rs As ADODB.Recordset | 
No evento Load do formulário estou definindo a conexão , o recordset a string de conexão com o banco de dados nwind.mdb para os controles vinculados : datDetalhe e datMestre e atribuindo os controles a seus respectivos datagrids.
| Private Sub Form_Load() 'define objetos conn e rs Set conn = New ADODB.Connection Set rs = New ADODB.Recordset 'define o cursor e a string de conexão conn.CursorLocation = adUseClient conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\teste\nwind.mdb;Persist Security Info=False" ' Define o nome do banco de dados para o detalhe datDetalhe.ConnectionString = _ "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\teste\Nwind.mdb;Persist Security Info=False" ' define o nome do banco de dados para o master datMestre.ConnectionString = _ "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\teste\Nwind.mdb;Persist Security Info=False" datMestre.RecordSource = "Select NúmeroDoPedido, CódigoDoCliente , DataDoPedido, DataDeEntrega,PaísDeDestino,_ Frete FROM Pedidos" datMestre.Refresh Set grdDetalhe.DataSource = datDetalhe datDetalhe.Refresh Set grdMestre.DataSource = datMestre End Sub | 
No evento MoveComplete do controle 
vinculado datMestre estou definindo duas consultas 
SQL que são o coração da aplicação:
1- A consulta selecao :
| subtotal = "SELECT DISTINCTROW [Detalhes do Pedido].NúmeroDoPedido, _ Sum(CCur([Detalhes do Pedido].[PreçoUnitário]*[Quantidade]*(1-[Desconto])/100)*100) AS Subtotal" subtotal = subtotal & " From [Detalhes do Pedido]" subtotal = subtotal & " GROUP BY [Detalhes do Pedido].NúmeroDoPedido"  subtotal = subtotal & " HAVING [Detalhes do Pedido].NúmeroDoPedido=" & datMestre.Recordset.Fields("NúmeroDoPedido") | 
Esta selecionando os detalhes do pedido para cada pedido selecionado na tabela Pedidos. Nela estou fazendo o cálculo , usando a função agregada SQL Sum - Sum(CCur([Detalhes do Pedido].[PreçoUnitário]*[Quantidade]*(1-[Desconto])/100)*100) AS Subtotal"
2- A consulta total calcula o total do pedido fazendo uma soma de todos os produtos para o pedido relacionado na tabela pedidos:
| total = "SELECT DISTINCTROW [Detalhes do Pedido].NúmeroDoPedido, _ Sum(CCur([Detalhes do Pedido].[PreçoUnitário]*[Quantidade]*(1-[Desconto])/100)*100) AS Subtotal" total = subtotal & " From [Detalhes do Pedido]" total = subtotal & " GROUP BY [Detalhes do Pedido].NúmeroDoPedido"  total = subtotal & " HAVING [Detalhes do Pedido].NúmeroDoPedido=" & datMestre.Recordset.Fields("NúmeroDoPedido") | 
Nota : As principais funções agregadas para o SQL padrão Access são :
| 
 | ||||||||||||||||||||
O código do evento MoveComplete é dado abaixo:
| ' Quando selecinar um novo registro no master ' selecionar os registros correspondentes no detalhe Private Sub datMestre_MoveComplete(ByVal adReason As ADODB.EventReasonEnum, ByVal pError As ADODB.Error, adStatus As _ ADODB.EventStatusEnum, ByVal pRecordset As ADODB.Recordset) Dim selecao As String Dim total As String 'consulta que exibe os detalhes do pedido para cada pedido selecionado na tabela pedidos selecao = "SELECT NúmeroDoPedido, CódigoDoProduto, PreçoUnitário, quantidade, Desconto , _                 CCur([Detalhes do Pedido].[PreçoUnitário]*[Quantidade]*(1-[Desconto])/100)*100 AS PreçoTotal FROM [Detalhes do Pedido] _
                WHERE NúmeroDoPedido = " & _                datMestre.Recordset.Fields("NúmeroDoPedido")'esta consulta calcula o valor total para o pedido total = "SELECT DISTINCTROW [Detalhes do Pedido].NúmeroDoPedido, _ Sum(CCur([Detalhes do Pedido].[PreçoUnitário]*[Quantidade]*(1-[Desconto])/100)*100) AS Subtotal" total = subtotal & " From [Detalhes do Pedido]" total = subtotal & " GROUP BY [Detalhes do Pedido].NúmeroDoPedido"  total = subtotal & " HAVING [Detalhes do Pedido].NúmeroDoPedido=" & datMestre.Recordset.Fields("NúmeroDoPedido")'abre a conexão e executa a consulta retornando um recordset com duas colunas : ' rs(0) - número do pedido rs(1) - valor total para o pedido conn.Open Set rs = conn.Execute(total) 'exibe na label o valor total para o pedido lblTotalPedido.Caption = Format(rs(1), "##,##0.00") 'atribui a seleção SQL ao data control e refresca o controle datDetalhe.RecordSource = selecao datDetalhe.Refresh     'fecha a conexão
    rs.closeconn.Close Set rs = Nothing End Sub 
 | 
Note que eu estou abrindo uma conexão e executando uma consulta SQL usando o método Excecute do objeto Connection.
O método Execute executa uma consulta definida e armazena o resultado em um recordset do tipo read-only , forward-only. A sintaxe é a seguinte :
objconn.Execute commandtext,ra,options
onde :
| Parametro | Descrição | 
|---|---|
| commandtext | Requerido. O texto com a instrução SQL ou store procedure a ser executada. | 
| ra | Opcional. O número de registros afetados pela consulta. | 
| options | Opcional. Define como o 
    provedor irá avaliar o parâmetro commandtext . 
    Podemos ter os valores: - CommandTypeEnum - ExecuteOptionEnum O valor padrão é adCmdUnspecified | 
Os valores para - CommandTypeEnum - são :
| Constante | Valor | Descrição | 
|---|---|---|
| adCmdUnspecified | -1 | Não define o argumento de tipo de comando. | 
| adCmdText | 1 | Avalia CommandText como um Texto ou um chamada de procedimento armazenado. | 
| adCmdTable | 2 | Avalia CommandText como uma tabela. | 
| adCmdStoredProc | 4 | Avalia CommandText como um nome de procedimento armazenado. | 
| adCmdUnknown | 8 | Indica que o tipo de comando na propriedade CommandText não é conhecido. | 
| adCmdFile | 256 | Avalia CommandText como o nome do arquivo de um procedimento armazenado. | 
| adCmdTableDirect | 512 | Avalia CommandText como um nome de tabela cujas colunas serão todas retornadas. Para usar o método seek o recordset deve ser aberto com com adCmdTableDirect. | 
Os valores para - ExecuteOptionEnum - são :
| Constante | Valor | Descricão | 
|---|---|---|
| adOptionUnspecified | -1 | indica que o comando não esta especificado. | 
| adAsyncExecute | 16 | indica que o comando será executado de forma assíncrona. | 
| adAsyncFetch | 32 | indica as linhas restantes depois da quantidade inicial definida na propriedade CacheSize serão retornadas de forma assíncrona. | 
| adAsyncFetchNonBlocking | 64 | indica que a thread principal nunca bloqueia enquanto estiver retornando. | 
| adExecuteNoRecords | 128 | indica o texto do comando não retorna linhas. ( Ex: somente insere dados). | 
| adExecuteStream | 256 | indica que o resultado da execução do comando será retornado como um stream.(fluxo de dados) | 
| adExecuteRecord | 512 | indica que CommandText é um comando ou stored procedure que retorna uma única linha. | 
Rodando o projeto e selecionado um determinado pedido você irá obter os detalhes do pedido o sub-total para cada produto em detalhes do pedido e o valor total do pedido conforme figura abaixo:

Nota: Para configurar as colunas e a aparência do datagrid faça o 
seguinte :

Exibindo dados relacionados com DAO
Para fazer a mesma coisa com DAO vou usar os controles vinculados Data Control e o controle DBGrid pois não posso usar DataGrid com Data Control.
No menu Project | Components inclua os controles Microsoft Data Bound Grid Control no seu projeto.(O controle vinculado para DAO já esta presente na barra de ferramentas basta selecionar e incluir no formulário.)
A aparência será idêntica á do projeto ADO , por isto , só vou mostrar o código do projeto:
Código de declaração das variáveis objetos da seção - General Declarations do formulário:
Option Explicit
Dim daoDB As DAO.Database
Dim daoRS As DAO.Recordset
Código do evento Load do formulário :
| Private Sub Form_Load() ' define o nome do banco de dados para o master datMestre.DatabaseName = "C:\teste\nwind.mdb" datMestre.RecordsetType = 1     datMestre.RecordSource = "Select NúmeroDoPedido, CódigoDoCliente , DataDoPedido, DataDeEntrega,PaísDeDestino, Frete _
    FROM Pedidos"datMestre.Refresh ' define o nome do banco de dados para o detalhes datDetalhe.DatabaseName = "C:\teste\nwind.mdb" datDetalhe.RecordsetType = 1 datDetalhe.Refresh End Sub 
 | 
O controle vinculado Data Control possui o evento Reposition que é equivalente ao MoveComplete do Ado data control. O código do evento reposition é o seguinte :
| Private Sub datMestre_Reposition() ' Quando selecinar um novo registro no master ' selecionar os registros correspondentes no detalhe Dim selecao As String Dim total As String selecao = "SELECT NúmeroDoPedido, CódigoDoProduto, PreçoUnitário, quantidade, Desconto ,_                    CCur([Detalhes do Pedido].[PreçoUnitário]*[Quantidade]*(1-[Desconto])/100)*100 AS PreçoTotal  _
                   FROM [Detalhes do Pedido] WHERE NúmeroDoPedido = " & _                   datMestre.Recordset.Fields("NúmeroDoPedido")'esta consulta calcula o valor total para o pedido     total = "SELECT DISTINCTROW [Detalhes do Pedido].NúmeroDoPedido, _
              Sum(CCur([Detalhes do Pedido].[PreçoUnitário]*[Quantidade]*(1-[Desconto])/100)*100) AS Subtotal"total = subtotal & " From [Detalhes do Pedido]" total = subtotal & " GROUP BY [Detalhes do Pedido].NúmeroDoPedido"     total = subtotal & " HAVING [Detalhes do Pedido].NúmeroDoPedido=" & datMestre.Recordset.Fields("NúmeroDoPedido")'abre a conexão e executa a consulta retornando um recordset com duas colunas : ' rs(0) - número do pedido rs(1) - valor total para o pedido     Set daoDB = DBEngine.Workspaces(0).OpenDatabase("C:\teste\nwind.mdb")Set daoRS = daoDB.OpenRecordset(total, dbOpenSnapshot) 'exibe na label o valor total para o pedido lbTotal.Caption = Format(daoRS(1), "##,##0.00") 'atribui a seleção SQL ao data control e refresca o controle datDetalhe.RecordSource = selecao datDetalhe.Refresh 'fecha a conexão daoRS.Close daoDB.Close End Sub | 
Rodando o projeto teremos o mesmo resultado obtido com ADO:

Vou mostrar como fazer o mesmo com VB.NET em 
outro artigo , este já ficou muito extenso...
Nota: Reparem que a carga do projeto para DAO é ligeiramente mais rápida do que com ADO.
Até breve ...

José Carlos Macoratti