Access Avançado Alta Interação com Consultas de Referências Cruzadas Paulo Sergio Sarraino * Artigo para Access 97, 2000, XP, 2003 e Excel 97, 2000, XP, 2003 Pré-requisitos: Conhecimentos de Access e VBA QUANDO TRABALHEI NUM BANCO PRIVADO HÁ ALGUNS ANOS, A EQUIPE DE CONTROLE FINANCEIRO DE TI TINHA A NECESSIDADE DE GERENCIAR O CONSUMO DE RECURSOS DE TI PELAS VÁRIAS ÁREAS DA DIRETORIA, COMO CUSTOS DE IMPRESSÃO E ALOCAÇÃO DE SERVIDORES. EIS ALGUMAS CARACTERÍSTICAS DO PROJETO QUE LEVANTEI: - consultas com múltiplos critérios definidos on-line; - acompanhamento evolutivo durante períodos definidos on-line; - extração desses dados para relatórios e planilhas; - tempo de desenvolvimento rápido e eficiente; - ambiente utilizado: Access 97. Como no projeto da equipe de TI há variáveis específicas desse ambiente, construí um exemplo mais simples para facilitar e ilustrar esta matéria. Acompanhe. NOTA: Cabe ressaltar que, a partir da versão XP (2002), o Access suporta a construção de Tabelas e Gráficos Dinâmicos de forma muito parecida com o Excel. Mas isso é assunto para outra matéria. CONSULTA E RELACIONAMENTOS Precisamos identificar no mínimo 3 campos para a estrutura: Campo de Linha: identifica o cabeçalho de linha (quantos campos desejar) Campo de Coluna: identifica o cabeçalho de coluna (apenas 1 campo) Campo de Valor: o cálculo da consulta (apenas 1 campo) Juntando essas informações ao meu conhecimento da época, concluí que as consultas de referência cruzada ajudariam muito no desenvolvimento do projeto e, utilizando o Access, teria ainda diversas opções para o ambiente do aplicativo que essa ferramenta proporciona. Um dos recursos mais importantes do Access (em todas as versões) são as consultas de referência cruzada. A visualização de dados de forma agrupada em 2 dimensões possibilita uma experiência mais rica do usuário com o sistema. Comparativamente, o Excel, com seus recursos de tabela dinâmica, oferece bem mais opções de controle e formatação do que o Access, mas precisávamos ir mais longe, padronizando o tratamento das informações, otimizando e protegendo o banco de dados e possibilitando uma série de interfaces para os usuários. Nesta matéria, a idéia é mostrar como utilizá-las com grande interação usando parâmetros dinâmicos. Pode-se usar o Access 97 ou superior, porém o aplicativo disponível para essa matéria está na versão 2000. Veja a estrutura de uma consulta de referência cruzada: Figura 1 – Criando uma consulta de referência cruzada Veja o comando SQL da mesma consulta: cálculo do valor TRANSFORM Sum(Nz([dblPreco],0)*Nz([dblQuantidade],0)) AS Total 32 cabeçalhos de SELECT tbl_Grupo.nomGrupo AS Grupo, 2 AS TipoRegistro, DISPONÍVEL PARA DOWNLOAD linha Space(4) & tbl_Produto.nomProduto AS Produto PIVOTTABLES.EXE (ACCESS 2000) relacionamentos FROM tbl_Grupo INNER JOIN (((tbl_Produto INNER JOIN Disponível para download em www.forumaccess.com.br entre as tabelas MAIO/JUNHO 2006 - REVISTA Nº 72 WWW.FORUMACCESS.COM.BR qry_Periodos ON tbl_Produto.codProduto = Access Avançado qry_Periodos.codProduto) LEFT JOIN tbl_Consumo ON (qry_Periodos.codProduto = tbl_Consumo.codProduto) AND (qry_Periodos.datPeriodo = tbl_Consumo.datPeriodo)) LEFT JOIN tbl_Preco ON (qry_Periodos.codProduto = tbl_Preco.codProduto) AND (qry_Periodos.datPeriodo = próprios dados. Não tratarei aqui as interfaces para entrada desses dados. Para otimizar a performance das tabelas, acesse a janela de Relacionamentos e defina as ligações entre as tabelas conforme o desenho a seguir: tbl_Preco.datPeriodo)) ON tbl_Grupo.codGrupo = tbl_Produto.codGrupo filtro WHERE tbl_Grupo.codGrupo In (1) agrupamentos GROUP BY tbl_Grupo.nomGrupo, 2, Space(4) & tbl_Produto.nomProduto classificação ORDER BY tbl_Grupo.nomGrupo, Space(4) & tbl_Produto.nomProduto cabeçalho de PIVOT Format([qry_Periodos].[datPeriodo],'mm/yyyy') In coluna (03/2005); Para nosso projeto precisaremos das tabelas abaixo: Figura 2 – Definindo o relacionamento tbl_Grupo Campo Tipo Descrição codGrupo AutoNumeração [chave] Identifica o Grupo de Produtos nomGrupo Texto (30) Nome do Grupo de Produtos Campo Tipo Descrição codProduto AutoNumeração [chave] Identifica o Produto tbl_Produto nomProduto Texto (30) Nome do Produto codGrupo Número (Inteiro) Identifica o Grupo do Produto Campo Tipo Descrição codProduto Número (Inteiro) [chave] Identifica o Produto datPeriodo Data [chave] Período do Preço dblPreco Número (Duplo) Preço do Produto Campo Tipo Descrição codProduto Número (Inteiro) [chave] Identifica o Produto datPeriodo Data [chave] Período do Consumo dblQuantidade Número (Duplo) Quantidade consumida do tbl_Preço SOBRE FORMULÁRIOS E SEUS CONTROLES Os dados do sistema referem-se a consumo de produtos e preços praticados. Com eles poderemos consultar: • A quantidade de produtos vendidos pelo tempo • Os preços praticados para os produtos vendidos pelo tempo • O volume de vendas dos produtos (Preço x Quantidade) pelo tempo • Sub-totais por Grupo de Produtos • Um gráfico de barras mostrando a relação entre os Produtos • Um gráfico tipo pizza mostrando a relação de um Produto com toda a base de dados Vamos ver a estrutura do formulário principal de consulta aos dados: tbl_Consumo Produto Também será necessária uma consulta especial: qun_Periodos. Ela une os períodos registrados nas tabelas de Preços e Consumo para termos todos os itens disponíveis para a pesquisa de dados. Nesse projeto, esse tipo de consulta previne pesquisas de consumo sem preço e vice-versa. A ausência desses dados é tratada com zeros nas consultas finais. Precisaremos também carregar os dados. Você pode usar os dados do exemplo disponível para essa matéria ou gerar seus Figura 3 – Formulário principal Para cada parametrização das consultas, criamos controles com tratamentos e validações. FÓRUMACCESS MAIO/JUNHO 2006 - REVISTA Nº 72 33 Access Avançado Access Avançado O grupo de opção grpOrient (identificado pela legenda “Por...”) permite definir o Valor da nossa pesquisa, pode-se selecionar Preços ou Consumo para uma pesquisa simples na base de dados ou Vendas para realizar o cálculo Preços x Consumo e assim termos uma evolução de Vendas dos nossos dados. A caixa de listagem lstGrupo (identificado pela legenda “Grupos:”) exibe os grupos de produtos cadastrados na base e permite selecioná-los para filtrar os dados pesquisados, pode-se selecionar múltiplos itens para a pesquisa. A caixa de listagem lstPeriodo (identificado pela legenda “Período:”) exibe os períodos cadastrados na base através da consulta qun_Periodos e permite selecioná-los para os cabeçalho de coluna da consulta, pode-se selecionar múltiplos itens para a pesquisa. Essa seleção múltipla das caixas de listagem é tratada pelas caixas de texto ocultas strGrupo e strPeriodo que, usando a função GetListItems (controle, delimitador), retorna os itens selecionados preparados para uso, por exemplo, em cláusulas In() de strings do SQL.Como a quantidade de períodos disponíveis tende a aumentar e esse critério será “casado” com as caixas de texto dos subformulários, limitou-se a leitura desses dados na geração das consultas da seguinte forma: para visualização em tela pode-se selecionar até 49 períodos e para visualização em relatórios apenas 12 períodos. Porém, esses limites podem ser redefinidos conforme a necessidade do seu aplicativo. O botão Pesquisar realiza a validação dos parâmetros e chama a rotina “AlteraRefCz” (detalhada adiante) que está em cada subformulário da tela. A alteração de qualquer dos controles citados solicita o recálculo dos dados. Os subformulários interagem também com os botões disponibilizados no rodapé do formulário. O controle frm_Evolucaod_sub exibe os dados e subtotais por Grupo ou um dos gráficos solicitados. O controle frm_Evolucaot_sub exibe os totais gerais dos dados (para os gráficos ele fica invisível). Os subformulários de dados e de totais têm a mesma estrutura, diferenciando apenas a geração dos dados, veja abaixo. Existem 50 campos com suas respectivas legendas identificadas como “Legenda1...Legenda50” e “Campo1... Campo50” que serão exibidos ou não conforme o resultado da consulta gerada. Para o layout ser ajustável a essa estrutura é necessário definir sua propriedade “Modo Padrão” como “folha de dados”. Apesar da disposição dos campos na estrutura do formulário parecer irrelevante, ela facilita a montagem da ordem de tabulação dos controles que define o posicionamento dos campos na visualização “folha de dados” do subformulário. 34 MAIO/JUNHO 2006 - REVISTA Nº 72 Figura 4 - Formulário que mostra os dados Dentro do formulário, como dito antes, há uma única função, “AlteraRefCz”. Ela realiza todo o trabalho de formatação dos campos, consulta aos dados e totalizações. Option Compare Database Option Explicit Function AlteraRefCz() On Error GoTo AlteraRefCz_Err 'analisa, define e formata o formulário com os campos da consulta de ref. cruzada Dim i As Integer 'contador para campos Dim nmCampo As String 'variável para nome de campo Dim strSQL As String Dim vlTotal As String 'string SQL 'identifica o valor desejado para a consulta Dim totPrd As String 'string para acumulado dos períodos 'esconde colunas For i = 1 To 50 With Me("Campo" & i) .ControlSource = "" .ColumnHidden = True End With Me("Legenda" & i).Caption = "" Next 'esconde/exibe formulário If (Me.Parent!strPeriodo = "") Then Me.Visible = False WWW.FORUMACCESS.COM.BR Access Avançado "ORDER BY tbl_Grupo.nomGrupo, Space(4) & Application.Echo True tbl_Produto.nomProduto " & _ Exit Function "PIVOT Else Me.Visible = True Format([qun_Periodos].[datPeriodo],'mm/yyyy') In (" & Me.Parent!strPeriodo & ");" End If With CurrentDb.QueryDefs("qrc_Evolucao_dados") Me.RecordSource = "" .SQL = strSQL .Close Select Case Me.Parent!grpOrient End With Case 1: vlTotal = "Nz([dblPreco],0)" Case 2: vlTotal = "Nz([dblQuantidade],0)" Me.RecordSource = "qun_Evolucao" Case 3: vlTotal = "Nz([dblPreco],0)*Nz([dblQuantidade],0)" End Select 'atribui todos os campos da consulta ao formulário (campo e legenda) vlTotal = "Sum(" & vlTotal & ")" For i = 1 To Me.RecordsetClone.Fields.Count With Me("Campo" & i) nmCampo = Me.RecordsetClone.Fields(i - 1).Name strSQL = "TRANSFORM " & IIf(Me.Parent!grpOrient = 3, vlTotal, 0) & " AS .ColumnHidden = False Total " & _ "SELECT .ColumnOrder = i tbl_Grupo.nomGrupo AS Grupo, 1 AS TipoRegistro, Select Case i tbl_Grupo.nomGrupo AS Produto " & _ "FROM Case 1, 2 tbl_Grupo INNER JOIN (((tbl_Produto INNER JOIN .ColumnHidden = True qun_Periodos ON tbl_Produto.codProduto = qun_Periodos.codProduto) LEFT .ControlSource = nmCampo JOIN tbl_Consumo ON (qun_Periodos.datPeriodo = tbl_Consumo.datPeriodo) Case 3 AND (qun_Periodos.codProduto = tbl_Consumo.codProduto)) LEFT JOIN tbl_Preco ON (qun_Periodos.datPeriodo = tbl_Preco.datPeriodo) AND .TextAlign = 1 'esquerda (qun_Periodos.codProduto = tbl_Preco.codProduto)) ON tbl_Grupo.codGrupo .ColumnWidth = 2000 .ControlSource = nmCampo = tbl_Produto.codGrupo " & _ "WHERE Case Is >= 4 tbl_Grupo.codGrupo In (" & Me.Parent!strGrupo & ") " & _ "GROUP BY tbl_Grupo.nomGrupo, 1, tbl_Grupo.nomGrupo " & _ .TextAlign = 3 'direita "ORDER BY tbl_Grupo.nomGrupo, tbl_Grupo.nomGrupo " & _ .ColumnWidth = 1500 "PIVOT .ControlSource = "=IIf((Campo1=Campo3) And (Parent!grpOrient Format([qun_Periodos].[datPeriodo],'mm/yyyy') In (" & Me.Parent!strPeriodo & ");" <> 3), Null, Format([" & nmCampo & "],'Standard'))" totPrd = totPrd & "CDbl(Nz([" & nmCampo & "],0))+" End Select With CurrentDb.QueryDefs("qrc_Evolucao_grupos") .FormatConditions.Delete .SQL = strSQL .FormatConditions.Add acExpression, , "Campo1=Campo3" .Close .FormatConditions(0).FontBold = True End With End With strSQL = "TRANSFORM " & vlTotal & " AS Total " & _ "SELECT tbl_Grupo.nomGrupo AS Grupo, 2 AS TipoRegistro, Me("Legenda" & i).Caption = nmCampo Next Space(4) & tbl_Produto.nomProduto AS Produto " & _ "FROM tbl_Grupo INNER JOIN (((tbl_Produto INNER JOIN qun_Periodos ON tbl_Produto.codProduto = qun_Periodos.codProduto) LEFT 'monta coluna de acumulado de produto totPrd = Mid(totPrd, 1, Len(totPrd) - 1) JOIN tbl_Consumo ON (qun_Periodos.datPeriodo = tbl_Consumo.datPeriodo) AND (qun_Periodos.codProduto = tbl_Consumo.codProduto)) LEFT JOIN tbl_Preco ON (qun_Periodos.datPeriodo = tbl_Preco.datPeriodo) AND (qun_Periodos.codProduto = tbl_Preco.codProduto)) ON tbl_Grupo.codGrupo = tbl_Produto.codGrupo " & _ "WHERE tbl_Grupo.codGrupo In (" & Me.Parent!strGrupo & ") " & _ "GROUP BY tbl_Grupo.nomGrupo, 2, Space(4) & tbl_Produto.nomProduto " & _ With Me("Campo" & i) .ControlSource = "=IIf((Campo1=Campo3) And (Parent!grpOrient <> 3), Null, Format(" & totPrd & ",'Standard'))" .ColumnHidden = False .ColumnOrder = i + 50 .ColumnWidth = 1500 .TextAlign = 3 'direita FÓRUMACCESS MAIO/JUNHO 2006 - REVISTA Nº 72 35 Access Avançado Access Avançado .FormatConditions.Delete .SetFocus .FormatConditions.Add acExpression, , "Campo1=Campo3" .FormatConditions(0).FontBold = True DoCmd.RunCommand acCmdUnfreezeAllColumns End With End With If Me.grpOrient > 2 Then Me("Legenda" & i).Caption = "Acumulado" With Me.frm_Evolucaot_sub Exit Function .SetFocus DoCmd.RunCommand acCmdUnfreezeAllColumns AlteraRefCz_Err: End With End If Application.Echo True MsgBox "Ocorreu um erro:" & vbCrLf & _ End If Err.Number & " - " & Err.Description, vbCritical, "Atenção!" End Sub End Function Sub CongelaColuna(qtcol As Byte) No rodapé temos também o botão “Visualizar Relatórios”, que chama o relatório de dados ou de gráfico conforme o item visualizado no formulário e o botão “Congelar Colunas” que realiza a ação de congelar a primeira coluna visualizada na pesquisa de dados a fim de facilitar a rolagem horizontal da tela. Private Sub cmdCongelar_AfterUpdate() 'congela a quantidade de colunas informada para _ facilitar rolagem horizontal por vários períodos On Error Resume Next Select Case qtcol Case Is <= 0: Dim ncol As Integer 'quantidade de colunas a congelar Exit Sub Case 1: ncol = 1 DoCmd.RunCommand acCmdUnfreezeAllColumns SendKeys "{TAB}", True If Me.frm_Evolucaod_sub.SourceObject <> "frm_Evolucao_dados" Then Me.cmdCongelar = 0 SendKeys "{HOME}", True SendKeys "^{ }", True Exit Sub DoCmd.RunCommand acCmdFreezeColumn End If SendKeys "{RIGHT}", True Case Else If Me.cmdCongelar = True Then DoCmd.RunCommand acCmdUnfreezeAllColumns SendKeys "{TAB}", True With Me.frm_Evolucaod_sub .SetFocus SendKeys "{HOME}", True SendKeys "^{ }", True CongelaColuna (ncol) SendKeys "+{RIGHT}", True End With DoCmd.RunCommand acCmdFreezeColumn With Me.frm_Evolucaot_sub If Me.grpOrient <= 2 Then SendKeys "{RIGHT 2}", True End Select .Visible = False Else End Sub .SetFocus CongelaColuna (ncol) End If End With Else With Me.frm_Evolucaod_sub 36 MAIO/JUNHO 2006 - REVISTA Nº 72 SOBRE RELATÓRIOS E SEUS CONTROLES A estrutura do relatório segue o mesmo princípio dos formulários de dados. Ele é todo desvinculado, e no evento Open ocorre toda a carga e formatação de campos. Perceba que também aplicamos os recursos de Formatação Condicional para ressalvar as linhas com os dados de Grupos de Produtos. WWW.FORUMACCESS.COM.BR Access Avançado nmCampo = .Fields(i + 2).Name Me("txtMes" & i).Caption = nmCampo With Me("valMes" & i) .ControlSource = "=IIf((Grupo=Produto) And (Forms!frm_Evolucao!grpOrient <> 3), Null, Format([" & nmCampo & "],'Standard'))" .FormatConditions.Delete .FormatConditions.Add acExpression, , "Grupo=Produto" .FormatConditions(0).FontBold = True End With totPrd = totPrd & "CDbl(Nz([" & nmCampo & "],0))+" Next End With 'monta Acumulado Figura 5 – Construindo o relatório Private Sub Report_Open(Cancel As Integer) totPrd = Mid(totPrd, 1, Len(totPrd) - 1) Me.txtAcum.Caption = "Acumulado" With Me.valAcum Dim rstD As Recordset, rstT As Recordset Dim i As Integer Dim lim 'contador As Integer 'limite de períodos pra exibição Dim nmCampo As String Dim totPrd As String 'variável para nome de campo 'string para acumulado do produto .ControlSource = "=IIf((Grupo=Produto) And (Forms!frm_Evolucao!grpOrient <> 3), Null, Format(" & totPrd & ",'Standard'))" .FormatConditions.Delete .FormatConditions.Add acExpression, , "Grupo=Produto" .FormatConditions(0).FontBold = True End With If Forms!frm_Evolucao!strPeriodo = "" Then Cancel = True 'monta colunas de totais Exit Sub If Forms!frm_Evolucao!grpOrient = 3 Then End If totPrd = "" With rstT Me.RecordSource = Forms!frm_Evolucao!frm_Evolucaod_sub.Form.RecordSource Set rstD = Forms!frm_Evolucao!frm_Evolucaod_sub.Form.RecordsetClone lim = .Fields.Count - 3 If lim > 12 Then lim = 12 For i = 1 To lim nmCampo = .Fields(i + 2).Value If Forms!frm_Evolucao!grpOrient = 3 Then Me.ReportFooter.Visible = True Me("totMes" & i).ControlSource = "=Format(" & nmCampo & ",'Standard')" Set rstT = Forms!frm_Evolucao!frm_Evolucaot_sub.Form.RecordsetClone Else Me.ReportFooter.Visible = False totPrd = totPrd & "CDbl(Nz(" & nmCampo & ",0))+" Next End With End If 'monta Acumulado With Me.Produto .FormatConditions.Delete .FormatConditions.Add acExpression, , "Grupo=Produto" totPrd = Mid(totPrd, 1, Len(totPrd) - 1) Me.totAcum.ControlSource = "=Format(" & totPrd & ",'Standard')" End If .FormatConditions(0).FontBold = True End With 'monta colunas de dados With rstD lim = .Fields.Count - 3 If lim > 12 Then lim = 12 For i = 1 To lim End Sub Se algum dos gráficos estiver sendo visualizado, o sistema chamará o relatório do gráfico correspondente, porém, para eles, a definição dos dados para o gráfico não pode ser feita da mesma forma (via evento Open), por isso a rotina do botão “Visualizar Relatórios” do formulário tem mais ações além do OpenReport que imaginamos. Veja em seguida: FÓRUMACCESS MAIO/JUNHO 2006 - REVISTA Nº 72 37 Access Avançado Access Avançado Private Sub cmdVisualizar_Click() .Close acReport, "rpt_Evolucao_grafPizza" .SetWarnings True Dim i As Integer 'contador para campos Dim matPeriodos As Variant Dim strPeriodos As String .OpenReport "rpt_Evolucao_grafPizza", acViewPreview 'matriz dos períodos End With 'string dos períodos Application.Echo True Select Case Me.frm_Evolucaod_sub.SourceObject End Select Case "frm_Evolucao_dados" End Sub DoCmd.OpenReport "rpt_Evolucao", acViewPreview EXPORTANDO OS DADOS Case "frm_Evolucao_grafBarra" 'abro a estrutura do relatório e altero a consulta do gráfico antes de visualizar os dados Application.Echo False DoCmd.OpenReport "rpt_Evolucao_grafBarra", acViewDesign matPeriodos = Split(Forms!frm_Evolucao!strPeriodo, ",") For i = 0 To UBound(matPeriodos) strPeriodos = strPeriodos & "[" & matPeriodos(i) & "]," Next strPeriodos = Left(strPeriodos, Len(strPeriodos) - 1) Reports!rpt_Evolucao_grafBarra!grfProdutos.RowSource = "SELECT Através da propriedade “Barra de menus” dos relatórios, chamamos um menu personalizado construído através de macros de menu. Geralmente utilizo esse recurso para disponibilizar diversas opções para qualquer relatório de um sistema. Aqui temos comandos como: Configurar Impressora, a impressão propriamente dita, envio do relatório para o Excel e o Word (através dos métodos TransferText e Transfer SpreadSheet), visualização do relatório com 1 ou 2 páginas e o comando mais interessante desse menu chamado “Interface Excel”. Trim(qrc_Evolucao_dados.Produto) AS Produto, " & strPeriodos & _ "FROM qrc_Evolucao_dados;" With DoCmd .SetWarnings False .Close acReport, "rpt_Evolucao_grafBarra" .SetWarnings True .OpenReport "rpt_Evolucao_grafBarra", acViewPreview End With Application.Echo True Case "frm_Evolucao_grafPizza" 'abro a estrutura do relatório e altero a consulta do gráfico antes de visualizar os dados Application.Echo False DoCmd.OpenReport "rpt_Evolucao_grafPizza", acViewDesign matPeriodos = Split(Forms!frm_Evolucao!strPeriodo, ",") For i = 0 To UBound(matPeriodos) strPeriodos = strPeriodos & "[" & matPeriodos(i) & "]+" Next strPeriodos = Left(strPeriodos, Len(strPeriodos) - 1) Figura 6 - Configurando a macro para o relatório Reports!rpt_Evolucao_grafPizza!grfProdutos.RowSource = "SELECT Trim(qrc_Evolucao_dados.Produto) AS Produto, " & strPeriodos & " AS Período " & _ "FROM qrc_Evolucao_dados;" With DoCmd .SetWarnings False 38 MAIO/JUNHO 2006 - REVISTA Nº 72 Esse comando chama uma rotina “rstExcel”, onde, através do método CopyFromRecordset do Excel, passo o RecordSource do Relatório aberto para o Excel e realizo diversas formatações automatizadas via VBA. Mas para utilizar esses recursos é necessário adicionar a referência para os objetos do Excel, como mostro na tela seguinte: WWW.FORUMACCESS.COM.BR Access Avançado .Cells(1, 1).Resize(1, rst.Fields.Count) = vaTmp .Range("A2").Select .Range(.Cells(2, 1), .Cells(intMaxRow, intMaxCol)).CopyFromRecordset rst 'formatação conforme conteúdo do campo For i = 1 To rst.Fields.Count - 1 Select Case rst.Fields(i).Type Case dbDate: .Columns(i + 1).NumberFormat = "dd/mm/ yyyy" Case dbByte: .Columns(i + 1).NumberFormat = "General" Case dbDouble: .Columns(i + 1).NumberFormat = "#,##0.00" End Select Next 'exclusão de colunas e campos zerados .Columns("A:B").Delete .Range("A1").Select Figura 7 - Adicionando a referência para os objetos do Excel .Range(.Selection, .Selection.End(xlToRight)).Select A utilização do método CopyFromRecordset é um exemplo das opções disponíveis para tratamento de dados no Excel e pode ser vista na função listada a seguir: .Range(.Selection, .Selection.End(xlDown)).Select .DisplayAlerts = False .Selection.Replace What:="0", Replacement:="", LookAt:=xlWhole, _ SearchOrder:=xlByRows, MatchCase:=False, Function rstExcel() SearchFormat:=False, ReplaceFormat:=False .DisplayAlerts = True 'transfere o recordset do relatório ativo para o Excel _ utilizando o método CopyFromRecordset 'formatação da planilha .Range("B1").Select Dim rst As Recordset .Range(.Selection, .Selection.End(xlToRight)).Select Dim intMaxCol As Integer .Selection.NumberFormat = "mm/yyyy" Dim intMaxRow As Integer .Rows(1).Font.Bold = True Dim appExcel As Excel.Application .Range("A2").Select Dim i As Integer .ActiveWindow.FreezePanes = True .Cells.Select Set rst = CurrentDb.OpenRecordset(Reports(0).RecordSource, dbOpenSnapshot) .Cells.EntireColumn.AutoFit .Range("A2").Select End With intMaxCol = rst.Fields.Count End If If rst.RecordCount > 0 Then rst.MoveLast: rst.MoveFirst End Function intMaxRow = rst.RecordCount + 1 Set appExcel = New Excel.Application GRÁFICOS With appExcel Como dito antes, os botões de gráficos do sistema permitem chamar os subformulários preparados para essa estrutura. Os dados também são atualizados pela seleção de parâmetros do formulário, acessando a função “AlteraRefCz” do subformulário de gráfico. Com poucos ajustes, a rotina segue as mesmas estruturas de geração de consultas e atualização de dados. A formatação do gráfico foi feita diretamente no objeto. .Visible = True ReDim vaTmp(1 To rst.Fields.Count) For i = 1 To rst.Fields.Count vaTmp(i) = rst.Fields(i - 1).Name Next .Workbooks.Add FÓRUMACCESS MAIO/JUNHO 2006 - REVISTA Nº 72 39 Access Avançado Access Avançado Figura 8 - Gráfico de barras Figura 9 - Gráficos do tipo pizza CONCLUSÃO O aplicativo original, que deu a idéia para essa matéria, está em uso desde 2002 com os usuários muito satisfeitos com as possibilidades geradas e os resultados desejados superados. As consultas e gráficos disponíveis já auxiliaram diversas decisões da diretoria e o acompanhamento evolutivo dos dados permitiu alertar os usuários sobre quaisquer oscilações da utilização de recursos da área de TI. Com uma estrutura simples, rápida e eficiente, o aplicativo gerencia atualmente uma base de dados de aproximadamente 60.000 registros por mês. Espero que essa matéria ilumine o caminho de diversos pro- 40 MAIO/JUNHO 2006 - REVISTA Nº 72 jetos semelhantes para serem aprimorados com as técnicas aqui apresentadas. Fiquem à vontade para mandar idéias, sugestões e críticas. Bom estudo a todos! * Paulo Sergio Sarraino ([email protected]) é analista é sócio da Sarraino Systems. Possui graduação de Tecnologia em Processamento de Dados pela FASP (1997) e pós-graduação em Análise de Sistemas c/ Ênfase em Gerência de Projetos pela FIAP (2000). Atua como consultor em Análise e Desenvolvimento de Sistemas em ambiente MS Office e Internet (especialista em Access, VBA e ASP) há 10 anos. WWW.FORUMACCESS.COM.BR