) na janela Escolher a fonte de dados (consulte a Figura 4.2). As informações que você forneceu são armazenadas em um arquivo, conhecido informalmente como um DSN. (A abreviação DSN significa data source name.) O Excel o exibe, juntamente com outras fontes de dados disponíveis, na caixa de diálogo Escolher fonte de dados. A Figura 4.8 mostra o conteúdo de um arquivo DSN típico. Figura 4.8 Os arquivos DSN podem ser lidos por qualquer aplicação que possa abrir um arquivo de texto, incluindo o Bloco de Notas.
Se você analisar novamente a Figura 4.2, observe o botão Procurar na caixa de diálogo Escolher a fonte de dados. Ele permite a você navegar para outros locais onde talvez você tenha armazenado arquivos DSN. No Office 2003, eles são armazenados por padrão em C:\Arquivos de Programas\Arquivos Comuns\ODBC\Data Sources.
A T O N
Talvez você tenha se perguntado sobre as outras duas guias na caixa de diálogo Escolher a fonte de dados, Consultas e Cubos OLAP. Os arquivos de consulta são semelhantes aos DSNs, exceto que além de informações como caminho, nome e tipo de banco de dados, eles também incluem SQL que define tabelas, campos, critérios de filtragem de uma consulta e assim por diante. Eles podem ser mais convenientes mas menos flexíveis que os DSNs. As consultas normalmente têm a extensão de arquivo .dqy. A Figura 4.9 mostra um arquivo DQY típico. Os cubos OLAP tendem a ser estruturas de dados multidimensionais muito grandes. (OLAP significa Online Analytic Processing.) Pelo fato de serem tão grandes, alguns valores são pré-calculados a fim de economizar tempo de recuperação de dados.
Figura 4.9 Observe que a primeira parte do arquivo de consulta se parece com um DSN, enquanto a segunda é a SQL padrão.
88
Gerenciando dados com o Microsoft Excel
Suponha que alguém lhe enviou um arquivo DSN via e-mail. Talvez você o tenha salvado em seu desktop e nesse caso ele não será exibido na caixa de diálogo Escolher a fonte de dados. Para obtê-lo, simplesmente clique no botão Procurar e navegue até o seu desktop. Isso é diferente daquilo que você vê ao clicar em Escolher a fonte de dados no botão Opções. Então a caixa de diálogo mostrada na Figura 4.10 é exibida. Figura 4.10 Esse botão Procurar tem um efeito diferente do botão Procurar na caixa de diálogo Escolher a fonte de dados.
A caixa de diálogo Opções de fonte de dados permite a você fazer alterações mais duradouras na lista de fontes de dados disponível. Se você clicar no botão Procurar mostrado na Figura 4.10, poderá utilizar a caixa de diálogo Selecionar diretório para navegar até uma pasta onde você armazena ou pretende armazenar outros arquivos DSN e de consulta (veja a Figura 4.11). Figura 4.11 Os arquivos com extensões qy , como .dqy e .oqy, normalmente contêm instruções de SQL; isto é, eles são consultas.
Após você ter navegado para uma pasta, clicar em OK vai adicioná-la à lista pesquisada pelo Microsoft Query. Qualquer arquivo de fonte de dados nessa pasta será exibido na lista de fontes de dados disponível na caixa de diálogo Escolher fonte de dados. Em resumo • Clique no botão Procurar na caixa de diálogo Escolher a fonte de dados para navegar até um arquivo de fonte de dados que você quer utilizar para essa consulta. Esse arquivo não será exibido em seguida na caixa de diálogo Escolher a fonte de dados. • Clique no botão Opções na caixa de diálogo Escolher a fonte de dados e, em seguida, no botão Procurar na caixa de diálogo Opções de fonte de dados para navegar até uma pasta que você quer utilizar agora e futuramente. Os arquivos de fonte de dados nessa pasta serão exibidos em seguida na caixa de diálogo Escolher a fonte de dados.
A T O N
Naturalmente, se em um outro momento você quiser usar uma fonte de dados diferente, terá de configurá-la como fez com essa. Se quiser usar um banco de dados da Oracle, por exemplo, terá de criar um DSN com um driver diferente; ou, se quiser usar um banco de dados do Access diferente, precisará de um DSN que o identifique.
Capítulo 4 – Importando dados: uma visão geral
89
Isso completa o primeiro dos dois passos gerais envolvidos na criação de uma nova fonte de dados: identificando o tipo e o local dos dados. Voltemos agora para a corrida eleitoral pela prefeitura, onde, com a fonte de dados identificada, é hora de começar a criar uma nova consulta.
ESTUDO DE CASO
Criando a consulta Você acabou de criar uma nova fonte de dados (ou, de modo equivalente, talvez tenha utilizado uma fonte existente escolhendo Dados, Importar dados externos, Nova consulta ao banco de dados). Você agora verá a caixa de diálogo Escolher a fonte de dados mostrada anteriormente na Figura 4.2. Com a caixa de diálogo Escolher a fonte de dados ativa, na caixa de listagem Bancos de dados clique no nome da fonte de dados em que você está interessado e clique em OK. A janela do Microsoft Query é exibida, como mostrado na Figura 4.12.
Figura 4.12 Utilize o botão Opções para escolher entre mostrar tabelas ou exibições (o termo view , ou visualização , é às vezes usado como um sinônimo de consulta ).
Embora essa seja a primeira vez nessa seqüência que a janela do Microsoft Query é exibida, a consulta está comandando o show nos bastidores, orientando você na criação de uma nova fonte de dados. A caixa de diálogo Adicionar tabelas também aparece automaticamente quando a janela Consulta é aberta; tudo o que você tem a fazer nesse ponto é adicionar uma ou mais tabelas ou consultas existentes do banco de dados. O próximo capítulo aborda detalhadamente várias maneiras de utilizar o Microsoft Query para automaticamente mover dados de bancos de dados para o Excel. Se você não estiver familiarizado com bancos de dados relacionais verdadeiros, por ora tenha em mente estes aspectos: • Uma tabela se parece bastante com uma planilha ou lista do Excel. Ela tem registros, e cada registro ocupa uma linha diferente. Ela tem campos, e cada campo ocupa uma coluna diferente. O exemplo atual mostra como mover dados de uma tabela de banco de dados para uma planilha do Excel.
90
Gerenciando dados com o Microsoft Excel
• Você pode utilizar esse método para mover para o Excel dados de mais de uma tabela de uma vez — até mesmo de mais de um banco de dados. • Uma consulta é uma série de instruções que atua de uma certa maneirade em uma tabela. Uma consulta pode utilizar dados de uma tabela para fazer cálculos (por exemplo,“Se o campo Sexo for 1, exibir ‘Masculino’ e, caso contrário, ‘Feminino’”).Costuma ser eficiente mover dados para a planilha diretamente da consulta e apenas indiretamente da tabela utilizada pela consulta. O exemplo presente continua movendo dados de eleitores diretamente de uma única tabela para uma planilha. 1. Na janela Adicionar tabelas mostrada na Figura 4.12, selecione a tabela chamadaAfiliacao e então clique emAdicionar. Ao fazer isso,a tabela é exibida na janela do Microsoft Query.Se houver dados, você pode continuar adicionando mais tabelas e consultas. Quando você tiver acabado de adicionar, clique emFechar.A janela do Microsoft Query então é exibida como mostrado na Figura 4.13.
Figura 4.13 Se você não vir o painel Critérios, escolha Critérios no menu Exibir.
A janela Consulta tem três painéis: • O painel Tabelas — isto é, o local em que as tabelas utilizadas por sua consulta são exibidas.Como você pode ver na Figura 4.13, os campos individuais da tabela também são mostrados. • O painel Critérios — aqui você pode especificar quais registros devem ser retornados da tabela de banco de dados. Suponha que você queira retornar registros de eleitores cujo valor no campo Partido sej a Republicano ou Independente. Você poderia arrastar o campo Partido de sua tabela para a primeira linha do painel de critérios, rotulado Campo de critérios. Na mesma coluna, você digitaria “Republicano” (entre aspas) na linha Valor. Novamente, na mesma coluna, você digitaria “Independente” na linha Ou. • O painel Dados — isto é, o local em que você especifica quais campos quer retornar.Você não precisa (embora possa, se quiser) retornar todos os campos de uma tabela do banco de dados. No painel Tabelas, basta clicar em um campo que você queira e arrastá-lo para uma coluna no painel Dados (veja a Figura 4.14).
Capítulo 4 – Importando dados: uma visão geral
91
Figura 4.14 Dar um clique duplo em um campo da tabela faz com que ele seja copiado para a primeira coluna disponível no painel Dados.
Painel Tabela
Painel Critérios
Painel Dados
2. Dê um clique duplo em um campo na tabela para copiá-lo para o painel Dados, ou clique em um campo e arraste-o para uma coluna específica no painel Dados. Quando você tiver colocado a quantidade desejada de campos no painel Dados, escolha Arquivo, Retornar dados ao Microsoft Office Excel (ou simplesmente clique no botão Retornardados). Ao fazer isso,a caixa de diálogo Importar dados mostrada na Figura 4.15 é exibida.
Figura 4.15 A aparência exata dessa janela depende da versão do Excel que você está utilizando.
3. Clique em OK para retornar os dados à planilha ativa, começando na célula que estava ativa quando você iniciou o processo. Naturalmente que, se houver dados que você quer evitar que sejam sobrescritos, poderá fornecer um endereço diferente de célula ou mesmo retornar os dados para uma nova planilha.
A C I D
O passo 2 observou que você pode dar um clique duplo em um campo ou clicar e arrastá-lo para o painel Dados. Clicar duas vezes no campo faz com que ele seja colocado na coluna mais à direita disponível do painel Dados. Se você arrastar um campo, poderá colocá-lo na coluna que quiser no painel Dados. Se você arrastar o asterisco na parte superior da lista de campos para o painel Dados, obterá todos os campos.
A Figura 4.16 mostra a aparência da planilha depois que os dados foram retornados da fonte. A campanha do seu amigo agora pode se concentrar em anúncios publicitários e no esforço de obter votos nos subgrupos com mais probabilidade de votar na próxima eleição.
92
Gerenciando dados com o Microsoft Excel
Figura 4.16 Observe que os dados entram na planilha na forma de uma lista, completa com nomes de campo.
Há várias propriedades úteis sobre o intervalo que contém os dados retornados — chame-o de intervalo de dados externos . Entre outras propriedades, você pode • Fazer o intervalo se atualizar automaticamente sempre que você abrir a pasta de trabalho. • Especificar como quer gerenciar um aumento ou uma diminuição do número de registros. • Copiar fórmulas que dependem do intervalo de dados externos para acomodar registros adicionais.
A T O N
➪
O Excel identifica o intervalo de dados externos automaticamente. O nome depende em parte da versão que você está utilizando. O Excel 97 o chama de DadosExternos1 e se você tiver outros intervalos de dados ext ernos na mesma planilha, ele o chamará de DadosExternos2, DadosExternos3 e assim por diante. Versões subseqüentes identificam os intervalos de dados de acordo com o nome da fonte de dados que você selecionou na caixa de diálogo Escolher a fonte de dados; por exemplo,Consulta_de_empregado_DB . Independentemente da versão, os nomes dos intervalos devem estar em nível de planilha.
Para obter uma análise sobre nomes em nível de planilha e de pasta, consulte o Capítulo 3, “Listas, nomes e filtros do Excel”,p. 51.
Para ver quais opções estão disponíveis, clique com o botão direito do mouse em qualquer célula no intervalo de dados externos e escolha Propriedades (ou, dependendo de sua versão, Propriedades de intervalo de dados) no menu de atalho. ➪
Informações adicionais sobre as opções disponíveis podem ser encontradas em “Utilizando o Microsoft Query”, p. 106.
Utilizando o assistente de consulta À medida que acumula cada vez mais experiência com o Microsoft Query, descobrirá que (com moderação) ele é uma maneira útil de definir como você quer recuperar dados de um banco de dados: quais registros, quais campos, e em que ordem.
Capítulo 4 – Importando dados: uma visão geral
93
Até que você adquira um pouco de confiança, porém, talvez queira contar com um auxílio do Microsoft Query, chamado Assistente de consulta. Dê uma outra olhada na Figura 4.2: há uma caixa de seleção na janela Escolher a fonte de dados, rotulada Usar o assistente de consultas para criar/editar consultas. Se você marcar essa caixa de seleção, o Excel apresentará uma série de janelas — de modo bem parecido como o Assistente de gráfico — que permitirá a você definir sua consulta, passo a passo. A Figura 4.17 mostra o primeiro passo do Assistente de consulta, depois de você ter terminado de definir a fonte de dados. Figura 4.17 Ao utilizar o Assistente de consulta, será exibida essa janela em vez da janela do Microsoft Query.
A janela Escolher colunas do Assistente de consulta combina a janela Adicionar tabelas (consulte a Figura 4.12) com o ato de arrastar campos para o painel Dados da janela Consulta. Na Figura 4.17, o usuário expandiu a tabela de Afiliação clicando no sinal de adição que está bem à esquerda do nome da tabela. A lista então se expande para mostrar os nomes dos campos na tabela, e o sinal de adição junto ao nome da tabela é alterado para um sinal de subtração. Para continuar, basta clicar em um nome de campo e então no botão do sinal maior que (>). O campo é movido para a lista Colunas em sua consulta. Se você mudar de idéia, selecione um nome de campo nessa lista e clique no botão com o sinal menor que (<) (veja a Figura 4.18).
Figura 4.18 Clique no sinal duplo menor que (<<) para remover todos os campos previamente selecionados.
94
Gerenciando dados com o Microsoft Excel
Quando você tiver terminado de selecionar os campos, clique no botão Next para ir para o próximo passo do assistente, mostrado na Figura 4.19. Figura 4.19 O Assistente de consulta refere-se a campos como colunas, e a registros como linhas.
Nesse passo, você identifica quais campos, se houver, você quer utilizar para restringir os registros que são retornados para a planilha. Para fazer isso, siga estes passos: 1.
Selecione um campo na lista Coluna a filtrar.
2.
Clique na primeira seta do lado esquerdo e escolha um operador apropriado: igual, é menor do que, começa com e assim por diante.
3.
Clique no primeiro drop-down do lado direito e escolha um valor de critério apropriado (veja a Figura 4.20).
A T O N
Você também pode digitar um valor de critério na caixa em vez de selecionar um de um drop-down. Se fizer isso, certifique-se de digitar um valor existente no campo. Se a consulta não puder corresponder ao valor que você digitar, ela não retornará nenhum registro. Esses critérios não fazem distinção de letras maiúsculas e minúsculas: “verde” é tratado da mesma maneira que “Verde”.
Figura 4.20 Para adicionar mais critérios, você precisa primeiro selecionar E ou Ou entre os conjuntos de critérios — o E é a escolha padrão.
Capítulo 4 – Importando dados: uma visão geral
95
4. Se você quiser adicionar mais operadores e critérios, utilize a segunda e terceira duplas de
drop-down. Se você estabelecer três critérios, a caixa de diálogo exibirá uma barra de rolagem vertical e um outro conjunto de critérios. Você não está limitado a três conjuntos de critérios inicialmente visíveis. Quando você tiver acabado, clique em Avançar (ou Next ). O passo 4 mostra que o Assistente de consulta é mais fácil de utilizar e menos poderoso que a própria Consulta em si. Esse passo permite a você escolher entre os operadores disponíveis, em vez de contar com você para saber o que são eles e como especificá-los. Ele também exibe os valores que podem ser utilizados como critérios, em vez de contar com você para fornecê-los. Por outro lado, você pode filtrar somente um campo. Você não poderia, por exemplo, especificar que quer retornar Republicanos Masculinos; isso iria exigir o uso de dois campos: Sexo e Partido. O Microsoft Query, por outro lado, permite a você filtrar quantos campos quiser. Quando você clicar em Avançar no passo 4, será exibida a janela Ordem de classificação (veja a Figura 4.21). Selecione um ou mais campos e especifique se quer uma classificação crescente ou decrescente para cada um deles. O primeiro campo que você especifica torna-se a chave de classificação primária; o segundo campo torna-se a chave de classificação secundária. Isto é, se você selecionar primeiro Sexo e então Partido, e especificar uma classificação crescente para cada, os registros talvez venham para o Excel nessa ordem (dependendo dos valores reais para afiliação de partido): Feminino Democrata, Feminino Independente, Feminino Republicano, Feminino Verde, Masculino Democrata, Masculino Independente, Masculino Republicano, Masculino Verde. Figura 4.21 O usuário selecionou Idade na lista suspensa e clicou em Decrescente. O padrão é uma classificação crescente.
Quando você clicar em Avançar, o passo final do Assistente de consulta será exibido como mostrado na Figura 4.22. Se clicar em Salvar consulta, você salvará a consulta em um arquivo de dados com formato de texto. Isso pode ser útil, mas também pode tornar-se um problema. Suponha primeiro que você clique no botão Retornar Dados ao Microsoft Excel e então clique em Concluir (ou Finish). Se você fizer isso, a definição da consulta é salva na pasta de trabalho ativa (em um nome oculto, um ao qual nem você nem qualquer pessoa tem acesso direto; isso é muito diferente de um arquivo DSN legível). Subseqüentemente, você pode atualizar os dados no intervalo de dados externos reexecutando a consulta.
96
Gerenciando dados com o Microsoft Excel
Figura 4.22 Visualizar a consulta no Microsoft Query é uma boa maneira de ver como construir a mesma consulta sem utilizar o Assistente de consulta.
A C I D
Você pode atualizar um intervalo de dados externos clicando com o botão direito do mouse em qualquer célula do intervalo e escolher Atualizar dados externos no menu de atalho (em vez disso, sua versão poderia exibir Atualizar dados).
Se você quiser colocar a mesma consulta em outra pasta de trabalho, terá de copiar o intervalo de dados externos e colá-lo nessa pasta de trabalho, ou terá de recriar a consulta com a outra pasta de trabalho ativa.
A T O N
Talvez pareça muito fácil, mas para copiar o intervalo de dados externos e a definição de consulta para um outro local, tudo o que você precisa fazer é copiar e colar o intervalo de dados externos inteiro. A definição de consulta, bem como o nome conforme definido, acompanham os dados.
Por outro lado, suponha que você salve a consulta em vez de retornar os dados diretamente para a pasta de trabalho. Então, com uma outra pasta de trabalho ativa, você poderia escolher Dados, Importar dados externos e clicar em Importar dados. Você veria sua consulta salva como uma das fontes de dados disponíveis. Isso torna conveniente restabelecer a consulta em uma nova pasta de trabalho sempre que você quiser, sem copiar e colá-la, e sem recriá-la. O problema é que a consulta é armazenada em formato de texto e qualquer pessoa talvez consiga usá-la. (A consulta é salva como um arquivo DQY, do tipo mencionado anteriormente neste capítulo e mostrado na Figura 4.9.) As consultas freqüentemente precisam ter senhas salvas com elas, então se estiver pensando em salvar uma consulta que obtém dados de uma fonte protegida por senha, talvez você queira pensar duas vezes. Você pode escolher Exibir dados ou Editar consulta no Microsoft Query antes de clicar em Concluir (ou Finish). Essa escolha é útil se você estiver aprendendo a utilizar o Microsoft Query ou se simplesmente achar conveniente começar utilizando o Assistente de consulta e, então, fazer o ajuste fino com o Microsoft Query. Na maioria dos casos, porém, você escolherá Retornar dados ao Microsoft Office Excel. Quando você clicar em Concluir (ou Finish), será exibida a caixa de diálogo Importar dados mostrada na Figura 4.15. Clique em OK para concluir o processo. A Figura 4.23 mostra como sua planilha é exibida quando você faz isso.
Capítulo 4 – Importando dados: uma visão geral
97
Figura 4.23 Observe que a ordem dos registros corresponde à classificação escolhida na Figura 4.21.
Essa é uma situação fantástica para nomes de intervalo dinâmicos, que foram apresentados no Capítulo 3, “Listas, nomes e filtros do Excel”. Quando seu intervalo de dados externos for atualizado com novos dados, a definição dele será alterada automaticamente. Suponha que o intervalo que contém os dados externos seja identificado como Query_From_Next_Election e que ocupe o intervalo A1:D100. Você adiciona cinco novos registros ao banco de dados — registros possíveis de serem retornados pela consulta. Na próxima vez que o intervalo de dados for atualizado, ele incluirá esses cinco novos registros e estenderá cinco linhas adicionais na planilha, ocupando o intervalo A1:D105. Se você estabelecer dois nomes de intervalo dinâmicos que utilizam Query_From_Next_Election como uma base, você poderá facilmente organizar outras partes de sua pasta de trabalho para serem atualizadas juntamente com o intervalo de dados externos. Um gráfico XY (de dispersão) é um bom exemplo: talvez você tenha um intervalo dinâmico que atue como os valores do eixo X do gráfico e um que atue como os valores do eixo Y do gráfico. Por exemplo, talvez você defina o nome do intervalo IdadeGrafico como =DESLOCAMENTO(Planilha1!Query_from_Next_Election,1,2, _ LINHAS(Planilha1!Query_from_Next_Election),1)
e o nome do intervalo ProbalidadeGrafico como =DESLOCAMENTO(IdadeGrafico,0,1)
98
Gerenciando dados com o Microsoft Excel
Aplicado ao intervalo de dados externos mostrado na Figura 4.23, o intervalo dinâmico chamado IdadeGrafico é deslocado em uma linha (portanto, ele inicia na linha 2), em duas colunas (portanto, inicia na coluna C) e tem a mesma quantidade de linhas que o intervalo de dados externos e uma coluna de largura. Quando o intervalo de dados externos obtém mais registros, ele tem mais linhas, assim como o intervalo IdadeGrafico. O intervalo dinâmico chamado ProbalidadeGrafico é baseado no IdadeGrafico, mas está deslocado em zero linha e em uma coluna do IdadeGrafico. Como sua definição não especifica o número de linhas e colunas em seu intervalo, ele tem por padrão o mesmo número de linhas e colunas que seu intervalo-base. O gráfico mostrado na Figura 4.24 tem uma única série de dados, definida como =SERIE(,Planilha1!IdadeGrafico,Planilha1!ProbalidadeGrafico,1)
Quando o intervalo de dados externos é atualizado, novos registros entram no intervalo de dados externos. Ele ocupa mais linhas, e os intervalos dinâmicos que dependem dele também crescem. Como a série de dados do gráfico refere-se aos nomes de intervalo dinâmicos, ela captura e exibe os registros recentemente adicionados.
Figura 4.24 O gráfico é atualizado automaticamente se for baseado em nomes de intervalo dinâmicos.
Importando dados para tabela dinâmica Se você trabalhou muito com tabelas dinâmicas do Excel, certamente está familiarizado com uma lista como a fonte de dados de uma tabela dinâmica. E poderia utilizar os dados como mostrado na Figura 4.23 como uma fonte de tabela dinâmica, sem mais manipulação.
Capítulo 4 – Importando dados: uma visão geral
99
Consulte a Figura 4.15, que mostra a janela Importar dados, que é exibida quando você acaba de definir uma consulta com o Microsoft Query ou com o Assis tente de consulta. Você irá notar que naquela janela há uma opção para criar um relatório de tabela dinâmica (a expressão exata depende da versão do Excel que você está utilizando). Se você selecionar essa opção, verá o passo final do Assistente de tabela dinâmica em vez do intervalo de dados externos com seus registros e campos. Não há nada especial sobre esse recurso. Você chega ao mesmo lugar seguindo estes passos:
1. Inicie o Assistente de tabela dinâmica escolhendo Relatório de tabela e gráfico dinâmicos no menu Dados. 2. Selecione Fonte de dados externos no primeiro passo do assistente. 3. Clique em Importar dados no segundo passo do assistente. Você então verá a janela Escolher a fonte de dados, assim como na Figura 4.2. Agora pode selecionar uma fonte de dados existente ou criar uma. No final das contas, você terminará estruturando a consulta com o Microsoft Query (ou, se você escolher, com o Assistente de consulta) e quando retornar os dados para o Excel, você vai retornar para o passo final do Assistente de tabela dinâmica. O processo e o resultado final são os mesmos, independentemente de você iniciar com o Assistente de tabela dinâmica ou com o comando Importar dados externos.
Preenchendo tabelas dinâmicas Uma tabela dinâmica armazena seus dados em um local especial chamado cache. O cache torna possível a uma tabela dinâmica fazer recálculos complexos muito rapidamente. Mas o cache tem desvantagens. Talvez você tenha notado que se uma tabela dinâmica estiver baseada em dados de uma lista do Excel, e se os dados nessa lista forem alterados, a tabela dinâmica não se altera em resposta. Para que isso aconteça, você tem de solicitar-lhe isso — escolhendo Dados, Atualizar dados ou clicar no botão Atualizar na barra de ferramentas Tabela dinâmica, ou pelo menos um dos dois outros métodos. O mesmo vale se você baseia uma tabela dinâmica naquilo que o Excel chama de dados externos . Suponha que você utilize o Assistente de tabela dinâmica ou o comando Importar dados para criar uma tabela dinâmica que resume registros localizados em um banco de dados. Subseqüentemente, os dados do banco de dados são alterados. Assim como a tabela dinâmica foi baseada em uma lista do Excel, você tem de atualizar a tabela dinâmica. Atualizar a tabela dinâmica faz com que o cache seja atualizado, e quando é atualizado, a tabela dinâmica em si é atualizada.
A C I D
Se quiser, você pode suprimir o cache. Uma das opções de tabela dinâmica é Salvar dados com layout da tabela. Desmarcar essa opção significa que a tabela dinâmica não tem nenhum cache, algo de que a funcionalidade da tabela dinâmica basicamente depende. Você pode economizar um pouco de espaço e tempo suprimindo o cache. Se você alguma vez precisar de um cache — para girar a tabela, por exemplo — basta escolher Dados, Atualizar dados.
100
Gerenciando dados com o Microsoft Excel
Atualizando o cache Não há solução realmente satisfatória para o problema descrito na seção anterior. A partir do Excel 2003, o cache simplesmente não responde automaticamente às alterações em sua fonte de dados. O melhor que você pode fazer é configurar uma ou mais opções de tabela dinâmica que façam com que o cache seja atualizado em resposta a um evento. Os ev entos que podem atualizar o cache incluem abertura da pasta de trabalho que contém a tabela dinâmica e o decorrer de um determinado período de tempo. Clique com o botão direito do mouse em uma célula da tabela dinâmica e escolha Opções de tabela. A caixa de diálogo mostrada na Figura 4.25 será exibida. Use a caixa de diálogo Opções de tabela dinâmica para fazer um ou os dois procedimentos: • Marque a caixa de seleção Atualizar ao abrir e salve a pasta de trabalho. Quando você abrir a pasta de trabalho na próxima vez, a tabela dinâmica automaticamente atualizará seu cache a partir da fonte de dados. • Se você quiser uma atualização mais freqüente, marque a caixa de seleção Atualizar a cada e utilize o seletor para especificar o número de minutos de espera entre as atualizações automáticas. O problema, naturalmente, é que requer tempo e recursos do sistema para realizar a atualização. Se você tiver uma grande quantidade de dados subjacentes à tabela dinâmica, atualizá-la a cada (digamos) 5 minutos pode ser um desperdício. Em um ambiente em rede, onde você está encadeando centenas de milhares de bytes de dados pela rede durante uma atualização, considere a freqüência das atualizações muito cuidadosamente.
Figura 4.25 Uma boa idéia é dar um nome descritivo para a tabela e não contar com o nome padrão TabelaDinâmica1.
Capítulo 4 – Importando dados: uma visão geral
A C I D
101
Se os dados da sua tabela dinâmica estiverem em uma fonte de dados externos, você pode criar a tabela dinâmica sem jamais colocar uma lista na pasta de trabalho. Em seguida, porém, você talvez pense que que r ver os registros e campos individualmente, assim como se você tivesse primeiro importado os dados para um intervalo de dados externos e então criado uma tabela dinâmica que utiliza esse intervalo. Para criar a lista, dê um clique duplo na célula de Soma total da tabela dinâmica. Isso faz com que uma nova planilha seja inserida e os dados do cache sejam colocados nela em forma de lista.
Escolhendo entre Atualização ao abrir e o evento Workbook_Open A seção anterior descreveu como uma tabela dinâmica atualiza automaticamente seus dados: marque a caixa de seleção Atualizar ao abrir nas opções da tabela dinâmica. Você também pode fazer com que um intervalo de dados externos se atualize automaticamente: clique com o botão direito do mouse em qualquer célula do intervalo de dados, escolha Propriedades do intervalo de dados no menu de atalho e marque Atualizar dados na caixa de seleção Abrir arquivo. Isso é bem simples e direto, mas as coisas começam a ficar complicadas quando você tem mais de uma coisa para fazer quando uma pasta de trabalho é aberta. Por exemplo, abrir a pasta de trabalho talvez faça com que uma sub-rotina do VBA seja executada como parte do evento Ao abrir da pasta de trabalho. Nesse caso, pode ser importante saber se uma tabela dinâmica ou um intervalo de dados externos são atualizados antes ou depois da sub-rotina a ser executada. Uma vez gastei oito inestimáveis horas para recriar centenas de tabelas e gráficos dinâmicos que foram acumulados em um período de cinco meses, todos eles errados, porque eu pensei que a atualização ocorresse antes do evento Open. Prometo a você que é importante ter em mente que o evento Open antecede uma atualização automática ao abrir. Ou você talvez tenha várias tabelas dinâmicas em uma pasta de trabalho, cada uma devendo ser atualizada quando a pasta de trabalho for aberta. Isso pode apresentar um aborrecimento. Se uma estrutura da tabela dinâmica for alterada como resultado de uma atualização automática, o Excel avisa você via uma caixa de mensagem que diz Pivot table was changed during Refresh Data operation . (Se essa mensagem será ou não exibida dependerá da versão do Excel em uso.) Uma mensagem de alerta não é um problema, mas se você tiver, digamos, 10 ou 15 tabelas dinâmicas que são alteradas quando são atualizadas e você estiver recebendo um alerta para cada uma delas, tendo de clicar em OK para ir para a próxima tabela dinâmica — isso é irritante. Então, considere controlar as atualizações da tabela dinâmica com o Visual Basic for Applications. Para fins de atualização de tabelas dinâmicas, este capítulo fornece apenas uma visão geral. ➪
O Capítulo 7,“Revisão dos princípios básicos do VBA”, aborda mais detalhadamente o VBA.
Conforme mencionado anteriormente, pastas de trabalho têm eventos Open (entre vários outros). A idéia por trás de um evento, nesse contexto, é que o Excel detecta quando o evento ocorre e executa o código em resposta ao evento. Esse código é chamado handler de evento: ele controla o que acontece quando o evento ocorre. Então, você pode escrever o código VBA que é executado quando ocorre o evento Open da pasta de trabalho, e esse código pode atualizar suas tabelas dinâmicas.
102
Gerenciando dados com o Microsoft Excel
A lógica é que dessa maneira você pode exercer mais controle sobre quando as tabelas dinâmicas são atualizadas. E não casualmente, você pode suprimir esses alertas irritantes de que a tabela dinâmica foi alterada. Se você estiver utilizando o Excel 2003, pode haver ainda uma outra razão. O Excel 97 não avisa que as atualizações automáticas poderiam estar baseadas em consultas prejudiciais que poderiam, por exemplo, gravar dados em uma fonte externa. A partir do Excel 2000, Service Release 1, você é alertado e pode escolher continuar com a atualização, desativar a atualização ou ativar a atualização automática de consulta de todas as pastas de trabalho e sem ver o aviso novamente. O Excel 2003 não fornece a terceira opção, que suprime avisos subseqüentes. A única maneira de fazer isso é editar o registro do sistema ou atualizar as tabelas dinâmicas automaticamente com o próprio código VBA. Como já observado, fazer as atualizações com código VBA coloca você em uma posição de exercer mais controle sobre a sincronização das atualizações. Para dar um jeito de atualizar todas as tabelas dinâmicas de uma pasta de trabalho usando o evento Open, execute esses passos: 1. Abra a 2.
pasta de trabalho que contém as tabelas dinâmicas.
Clique com o botão direito do mouse no ícone do Excel que aparece diretamente à esquerda do menu Arquivo, para exibir um menu de atalho e escolha Exibir código.
A C I D
Você também pode pressionar Alt+F11 para abrir o Visual Basic Editor. Uma vez aí, clique com o botão direito do mouse no ícone Esta pasta de trabalho no Explorer de Projeto e, então, escolha Exibir código para abrir a janela do código da pasta de trabalho.
Você verá uma janela semelhante àquela mostrada na Figura 4.26. Para fins de construção de um handler de evento Open, não importa se o Explorer de projeto ou a janela Propriedades estão visíveis, mas, se você quiser, poderá localizá-los no menu Exibir. Figura 4.26 O código que controla um evento está associado a um objeto do Excel, então ele não está localizado em uma pasta Módulos.
3.
Observe na Figura 4.26 que o painel de código ocupa a parte direita da janela. Ele tem duas listas suspensas em sua parte superior. A da esquerda, em que (Geral) está selecionada, é a lista suspensa Microsoft Excel Objetos. A da direita, em que (Declaração) está selecionada, é a lista suspensa Procedimento. Clique na lista suspensa Microsoft Excel Objetos e escolha Pasta de trabalho na lista.
Capítulo 4 – Importando dados: uma visão geral
103
Duas instruções são exibidas automaticamente na janela de código: uma instrução Private Sub, identificando o procedimento (por padrão, Workbook_Open) e uma instrução End Sub, marcando o fim do procedimento. A lista suspensa Procedimento também seleciona automaticamente Open (veja a Figura 4.27). Figura 4.27 Há 28 procedimentos disponíveis como eventos de pasta de trabalho na lista suspensa de procedimentos, incluindo Activate e NewSheet.
4. Digite o código que você quer executar entre as instruções Private Sub e End Sub (veja a Figura 4.28). 5. Escolha Arquivo, Fechar e Voltar para Microsoft Excel e depois salve a pasta de trabalho. ➪
Para conhecer a Option Explicit, veja “Estabelecendo sub-rotinas”, p. 161.
Eis o código novamente, com alguns comentários para explicá-lo: Private Sub Workbook_Open()
Estabeleça uma sub-rotina que será executada quando a pasta de trabalho que contém a subrotina for aberta. Figura 4.28 A Option Explicit requer que você declare as variáveis antes de utilizá-las. É boa prática de programação fazer isso.
Dim wks As Worksheet, pt As PivotTable
Declare duas variáveis-objetos: wks e pt. Declarar as variáveis é meramente informar ao Visual Basic de que elas existem, que têm esses nomes e os tipos de objetos que elas representam. Declarar (um outro termo é dimensionar , que é a origem da palavra-chave Dim) wks como Planilha é declarar
104
Gerenciando dados com o Microsoft Excel
que a variável wks pode representar planilhas. Assim como em álgebra, a variável X pode representar um valor como 1, 23 ou 846; a variável wks pode representar Planilha1, Planilha2 ou qualquer planilha que tenha qualquer nome. De maneira semelhante, declarar a variável pt como Tabela dinâmica é declarar que ele pode representar qualquer tabela dinâmica. For Each wks In ThisWorkbook.Worksheets
Essa instrução inicia um loop. As instruções subseqüentes serão executadas uma vez para cada instância da variável wks. Wks representará cada planilha na pasta de trabalho — uma planilha diferente para cada vez que o próprio loop é executado. For Each pt In wks.PivotTables
Um outro loop é iniciado. Esse é executado dentro do loop externo e será executado uma vez para cada tabela dinâmica na planilha atualmente representada por wks. Suponha que Planilha1 tenha duas tabelas dinâmicas e Planilha2 tenha três tabelas dinâmicas. Quando wks representa Planilha, pt representará a primeira e, então, a segunda das duas tabelas dinâmicas em Planilha1. Então, quando wks representa Planilha2, pt representará a primeira, em seguida, a segunda e, por último, a terceira tabela dinâmica dessa planilha. pt.RefreshTable
Com wks representando uma planilha em particular e pt representando uma tabela dinâmica em particular nessa planilha, o Visual Basic sabe precisamente que tabela dinâmica atualizar. O comando RefreshTable faz isso e sem que o usuário tenha de responder a nenhum aviso. Next pt
Vá para a próxima tabela dinâmica na planilha atualmente representada por wks. Next wks
Vá para próxima planilha na pasta de trabalho. End Sub
Conclua a sub-rotina. Alguns comandos que você indica ao VBA para executar não avisam quando acontece algo que você talvez precisa saber. O exemplo mostrado aqui, RefreshTable, é um deles. Outros avisam você. Alterar a orientação de um campo de tabela dinâmica é um deles: o Excel adverte que fazer isso sobrescreverá os dados existentes. A C I D
Se você quiser suprimir os avisos temporariamente, utilize esse comando: Application.DisplayAlerts = False
Trate essa instrução com cuidado.Antes de utilizá-la, certifique-se de que não quer que seu código avise você sobre um possível problema. E inverta o efeito dela assim que possível com Application.DisplayAlerts = True
Capítulo 4 – Importando dados: uma visão geral
105
Essa sub-rotina é concisa e exaustiva: apenas em oito instruções ela atualiza todas as tabelas dinâmicas em todas as planilhas da pasta de trabalho. Mas ela não permite controle muito personalizado. Você precisa microgerenciar o código para fazer isso. Por exemplo, suponha que você quer atualizar somente duas tabelas dinâmicas específicas quando uma pasta de trabalho particular é aberta. O código poderia se parecer com isto: Private Sub Workbook_Open() Dim wks As Worksheet, pt As PivotTable ThisWorkbook.Worksheets(“PartyStats”) _ .PivotTables(“Age By Party”).RefreshTable ThisWorkbook.Worksheets(“StateStats”) _ .PivotTables(“Party By State”).RefreshTable End Sub
Esse código atualiza somente duas tabelas dinâmicas: Idade por Partido e Partido por Estado. Suponha que ali houvesse uma outra tabela dinâmica, Visitas por Data, que mostra o número de visitas em um site da Web durante cada mês. Talvez você não queira atualizar essa tabela dinâmica toda vez que a pasta de trabalho é aberta. Um usuário ocasional poderia ter a impressão errada se o mês inteiro de setembro tivesse 1.600 visitas e o mês inteiro de outubro tivesse 1.750 visitas, e se ele abrisse a pasta de trabalho no meio do mês atual, em 15 de novembro, a tabela dinâmica (e talvez um gráfico associado) talvez exibisse somente a metade das visitas do mês, isto é, até essa data. Provavelmente, ao contrário de Idade por Partido e Partido por Estado, a interpretação da tabela dinâmica de Visitas por Data depende de quando o usuário consultar durante o mês. Portanto, você poderia colocar no código VBA para atualizar a tabela dinâmica Visitas por Data somente no último dia de cada mês.
Olhando para frente Neste capítulo, você viu como estabelecer fontes de dados externos com arquivos DSN. O arquivo DSN contém informações sobre o caminho, nome e tipo da fonte de dados externos. Você viu como atualizar intervalos de dados e tabelas dinâmicas automaticamente, quando a pasta de trabalho é aberta, e com o código VBA quando você quer exercer mais controle sobre o processo de atualização. E viu como utilizar o Microsoft Query para guiar você pelo processo de apontar consultas na fonte de dados. Essa foi necessariamente uma breve visão geral, mas o Capítulo 5, “Utilizando o Microsoft Query”, orienta você ao long o do processo de maneira consideravelmente mais detalhada.
5 Utilizando o Microsoft Query Entendendo a “Consulta” O capítulo anterior comparou o uso do Assistente de consulta ao uso da janela do Microsoft Query. Você viu que escolher o Assistente de consulta em vez do Microsoft Query é escolher uma ferramenta menos poderosa e mais amigável, em vez de uma ferramenta moderadamente exigente que oferece mais funcionalidade. Há uma escolha parecida envolvendo o Microsoft Query e um gerenciador de bancos de dados. O Microsoft Query oferece a você algumas maneiras de estruturar uma consulta — a seleção de campos e registros e ordens de classificação, por exemplo — mas não oferece a rica variedade de ferramentas que você tem quando utiliza um banco de dados diretamente. Ainda, o Microsoft Query é o único meio disponível para importar dados para uma planilha do Excel automaticamente, sem programação. E uma das melhores maneiras de mover dados de um banco de dados para uma planilha é utilizar as ferramentas do banco de dados para estruturar os dados de uma consulta e utilizar o Microsoft Query para organizar a transferência de dados para a planilha. Uma dificuldade em aprender sobre consultas é a palavra consulta em si. Ela é utilizada, um tanto casualmente, para dizer qualquer coisa: de dados retornados por uma consulta a um conjunto de instruções que define como controlar um conjunto de dados, à aplicação que ajuda a criar a consulta. Para complicar, as consultas fazem mais do que simplesmente retornar dados de uma fonte de dados: elas também podem adicionar ou remover dados nas tabelas, editar dados e, até mesmo, criar novas tabelas. Este livro utiliza o termo consulta para representar um conjunto de instruções. Por exemplo, esta é uma consulta simples: SELECT Tiles.TileID, Tiles.SpaceID, Tiles.Floor, Tiles.SmokeZone FROM Tiles;
Ela está escrita na Structured Query Language — ou SQL para abreviar. Essa consulta seleciona o nome de campo TileID, SpaceID, Floor e SmokeZone a partir de algo chamado Tiles, que poderia ser uma tabela ou outra consulta. (Muitas vezes você se verá criando uma consulta baseada em outra consulta.) A maioria das consultas que este livro discute é consulta Seleção; isto é, consultas que importam dados de um banco de dados e os levam para uma outra aplicação. Aqui a aplicação de interesse é o Excel, mas os princípios se aplicam independentemente de qual seja a aplicação receptora. Se uma consulta Seleção alterar os dados de alguma maneira (por exemplo, se Sexo igual a 1 mostrar “Masculino” e se Sexo igual a 2 mostrar “Feminino”) essa alteração acontece depois que a
Capítulo 5 – Utilizando o Microsoft Query
107
consulta importou os dados. Então, a menos que especificado de outro modo, você pode supor que uma consulta discutida neste livro é uma consulta Seleção. Uma consulta Exclusão (isto é, que remove registros de uma tabela), uma consulta Acréscimo (isto é, que adiciona registros) ou uma consulta Atualização (isto é, que modifica valores de um registro) serão identificadas por tipo.
A T O N
A SQL é uma linguagem padrão. É muito provável que uma instrução SQL que funciona em um sistema de gerenciamento de bancos de dados funcione da mesma maneira quando utilizada por um outro diferente. Esse evasivo muito provável é devido ao fato de que há variações na SQL. A Transact-SQL, por exemplo, difere da SQL sob importantes aspectos. Mas são praticamente idênticas na sintaxe básica de consulta.
É muito raro ter de escrever uma consulta com SQL. As aplicações mais populares que utilizam a SQL oferecem ao usuário uma interface gráfica para ajudar a projetar a consulta. A aplicação então interpreta as informações gráficas para escrever a consulta. A Figura 5.1 mostra um exemplo. O painel de tabela contém a tabela com seus campos, o painel de dados mostra os registros e seus valores de campo, e a janela de SQL mostra a instrução Structured Query Language. Figura 5.1 Essa é a maneira que o Microsoft Query representa visualmente a linguagem de consulta estruturada.
É possível visualizar a SQL de uma consulta que você construiu ou que está construindo, clicando no botão da barra de ferramentas SQL do Microsoft Query.
A C I D
Poucas aplicações que o ajudam a escrever a SQL, como o Microsoft Query e o Access, têm um recurso de busca e troca de texto. Suponha que você precisasse alterar a referência de tabela na consulta de exemplo mostrada anteriormente de Tiles para Tiles2004. Você poderia adicionar uma tabela chamada Tiles2004 ao painel de tabela e mudar cada referência de campo na consulta de Tiles para Tiles2004. Muitas vezes é mais rápido exibir a janela SQL,copiar seu texto,colá-lo em uma outra aplicação como o Bloco de notas ou oWord, substituirTiles por Tiles2004 e, em seguida, copiar e colar o resultado de volta na janela SQL.
108
Gerenciando dados com o Microsoft Excel
O sistema de gerenciamento de bancos de dados, seja ele o Access, o Oracle, o SQL Server ou algum outro, vê a instrução SQL, depois a interpreta e retorna os dados da mesma maneira.
Consultando múltiplas tabelas Um motivo fundamental para construir consultas é fazer a join de mais de uma tabela. Há várias razões pelas quais você talvez queira fazer isso. As duas próximas seções descrevem duas razões comuns que foram discutidas brevemente, e unicamente no contexto do Excel, no Capítulo 1, “Utilizando incorretamente o Excel como uma ferramenta de gerenciamento de banco de dados”.
ESTUDO DE CASO Você dirige o departamento de recursos da Ballou Realty. A Ballou aluga espaço em seus vários edifícios para outras empresas com necessidade de área comercial. Seus funcionários construíram vários bancos de dados para ajudar a manter as informações sobre os escritórios em si, equipamentos dos edifícios, como aparelhos de ar-condicionado e PBXs, e componentes dos edifícios como janelas e portas. Um de seus bancos de dados contém registros que descrevem as portas nos edifícios comerciais da Ballou: portas dos escritórios, portas dos armários, portas que separam corredores, portas que dão saída para a rua e assim por diante. Por várias razões, incluindo questões de reparo e garantia, você acha que é necessário saber o nome do técnico de manutenção que inspecionou pela última vez cada porta. Uma maneira de organizar as coisas é ter um campo, talvez chamado TechName, em sua tabela Doors. Então, quando um técnico da Ballou inspecionar uma porta, um dos itens que é registrado é o nome do técnico. Mas você quer ter o cuidado de não ter de digitar o nome do técnico em cada registro. Se fizer isso, um erro simples de ortografia cria um novo técnico: talvez “Smith”tenha feito o serviço, mas se em vez disso alguém digitar “Smit”, qualquer resumo de dados envolvendo técnicos estará errado. Ele irá mostrar que uma porta não foi inspecionada por Smith, mas pelo inexistente Smit. Antes de você começar a trabalhar na Ballou, os funcionários do departamento de recursos mantinham os dados utilizando apenas o Excel. Eles evitavam o problema de ortografia incorreta configurando uma lista de validação, ao escolher Dados, Validação e ativando uma lista (veja a Figura 5.2). Figura 5.2 A lista de validação é, além disso, uma outra boa oportunidade de utilizar um nome de intervalo dinâmico.
Você implementou uma política departamental para armazenar informações sobre instalações em um banco de dados verdadeiro. Evitar erros de digitação utilizando a validação de dados do Excel tornou-se irrelevante. Para ajudar a garantir a integridade dos dados em um banco de dados, você cria uma tabela com (nesse exemplo) os nomes dos técnicos que trabalham para Ballou, e organiza sua tabela Doors principal para exibir os nomes de técnicos disponíveis em uma lista suspensa. Selecionar um nome dessa lista suspensa evita erros de digitação.
Capítulo 5 – Utilizando o Microsoft Query
109
Você chama a tabela de Technicianse armazena os nomes dos técnicos em um campo chamadoTechName. É útil à tabela Technicians ter um campo com ID de registro único, talvez TechID, e a sua tabela Doors, um campo com o mesmo nome. A Figura 5.3 mostra como isso pode ser configurado no Microsoft Access. Figura 5.3 Cada tabela tem um campo TechID. Isso prepara o caminho para um vínculo entre as duas tabelas.
A tabela Technicians poderia ter um registro cujo valor para o campo TechID fosse 1, e o valor de TechName, Fred Tafoya . Se Fred Tafoya foi o último técnico da Ballou a inspecionar uma porta em particular,o registro dessa porta teria o valor 1 no campo TechID na tabela Doors. Dada essa configuração, você retorna registros para o Excel via Microsoft Query seguindo estes passos: 1. Com uma planilha do Excel ativa, escolha Dados, Importar dados externos. 2. Crie uma Nova fonte de dados ou utilize uma fonte existente. A janela do Microsoft Query aparece, junto com a caixa Adicionar
tabelas. ➪
A criação de uma nova fonte de dados é descrita em “Obtendo dados externos na pasta de trabalho”, p. 80. 3. Clique na tabela Doors na caixa de listagem Tabela para selecioná-la. Clique no botão Adicionar para colocar a tabela Doors
no painel Tabelas. 4. Repita o passo 3 para a tabela Technicians. 5. Clique no botão Fechar para descartar a caixa Adicionar tabelas.
O painel de tabelas agora é exibido como na Figura 5.4.
110
Gerenciando dados com o Microsoft Excel
Figura 5.4 Para retornar todos os campos da tabela, dê um clique duplo no asterisco na parte superior de uma lista de campos ou simplesmente arraste-a para o painel de dados.
Uma linha, chamada linha de join, aparece entre as duas tabelas, conectando o campo TechID na tabela Doors ao campo TechID na tabela Technicians. A T O N
Sob algumas circunstâncias, a linha de join não é exibida automaticamente. Se não for, simplesmente clique em um campo TechID da tabela, arraste-o para o campo TechID da outra tabela e libere o botão do mouse.
Para retornar o ID da porta e o nome do técnico que a inspecionou por último, arraste o campo DoorID da tabela Door para o painel de dados. Em seguida, arraste o campo TechName da tabela Technicians para o painel de dados. O resultado é mostrado na Figura 5.5.
Figura 5.5 Você não precisa mover nenhum campo TechID para o painel de dados para retornar o campo TechName.
Painel da Tabela
Painel de Critérios
Painel de Dados
Capítulo 5 – Utilizando o Microsoft Query
111
Quando você escolhe Arquivo, Retornar dados ao Microsoft Excel, a caixa de diálogo Impor tar dados é exibida. Clique em OK para aceitar sua colocação dos registros na planilha, como mostrado na Figura 5.6. Você agora pode utilizar o Excel para revisar as informações sobre a manutenção em progresso das portas dos edifícios comerciais da Ballou, usando as ferramentas de análise de dados do Excel, como tabelas dinâmicas e gráficos.
Figura 5.6 Os resultados de sua consulta: registros de dois campos em tabelas diferentes, unidos por um campo comum não exibido.
Fazendo joins de registro-pai e registro-filho Os edifícios analisados na seção anterior são provavelmente sujeitos a códigos municipais e outros regulamentos que exigem a inspeção regular de portas. Esses regulamentos naturalmente requerem que você corrija qualquer problema que for encontrado. Portas externas precisam funcionar corretamente em situações de emergência. As portas que separam corredores costumam ser portas cortafogo e devem retardar a evolução de um incêndio. As portas dos armários devem estar quase sempre trancadas, especialmente se os armários contiverem materiais perigosos. Por sua vez, isso significa que é preciso inspecionar cada uma dessas portas regularmente e fazer um registro de sua condição, incluindo qualquer manutenção realizada. Mais ainda, quando um ocupante do edifício reclama que, digamos, uma porta do banheiro não fecha, essa reclamação precisa ser registrada. Assim como o reparo posterior da porta. As reclamações não seguem agendas organizadas como ocorre com a manutenção regular.
112
Gerenciando dados com o Microsoft Excel
ESTUDO DE CASO
Escolhendo o registro-pai Dada essa situação — uma bem comum, a propósito — como você pretende armazenar as informações no banco de dados Door que você configurou para a Ballou Realty? Uma porta constitui um registro? Em caso positivo, como você lida com o fato de que uma porta recebe cuidados de manutenção um número indeterminado de vezes por ano? Quantos campos você deve alocar para registrar esses serviços de manutenção? Lembre-se de que toda vez que uma porta sofre manutenção, você precisa armazenar as informações sobre o técnico, a data em que o serviço foi feito, que ação foi tomada, se está coberta pela garantia e assim por diante. Seu registro de portas vai precisar de uma grande quantidade de campos. A Figura 5.7 mostra como os funcionários do departamento de recursos talvez tenham utilizado uma estrutura de arquivo simples para armazenar as informações. Figura 5.7 Um projetista de banco de dados que não entende estruturas relacionais (ou prefere não utilizá-las) talvez empregue esse projeto.
A T O N
O termo arquivo simples significa um conjunto de dados que não é relacional. Uma lista do Excel é um arquivo simples: registros diferentes ocupam linhas diferentes e campos diferentes ocupam colunas diferentes. Ele é bidimensional ou simples.
Às vezes o layout mostrado na Figura 5.7 faz bastante sentido. Se você sabe, por exemplo, que uma porta receberá manutenção não mais que quatro vezes durante o tempo que estiver em uso,você talvez opte pela praticidade de um arquivo simples. Muitos projetos errados na teoria, são corretos na prática.
Capítulo 5 – Utilizando o Microsoft Query
113
Com mais freqüência, porém, essa classificação de layout cria problemas para você. Afinal de contas, cada instância de manutenção realmente é um registro diferente e tratá-lo como um campo diferente desmembra a realidade da situação. Quando chega a época de analisar todos os dados sobre manutenção,você precisará convertê-los em registros individuais. Suponha que você queira calcular o número de vezes que as portas são consertadas e o número de vezes que são trocadas. Se você tiver cada instância de manutenção armazenada como um registro separado, é fácil:você simplesmente importa, digamos,o campo MaintenanceAction para o Excel. Em seguida, utiliza uma tabela dinâmica (ou uma fórmula de matriz) para contar o número de registros com “Conserto” e o número de registros com “Troca”. Mas se os registros de manutenção são armazenados em campos separados em vez de em registros separados, você tem um problema. Ele é solucionável, sim, mas ainda é um problema. Você tem de importar, para cada registro, a primeira, segunda, terceira,…enésima instância de manutenção, recuperando MaintenanceAction1, MaintenanceAction2, MaintenanceAction3,…, MaintenanceActionN. Em seguida, você tem de converter todos esses valores no formato de lista antes de usar uma tabela dinâmica para resolver o problema. Quando não for possível prever quantos registros de manutenção você precisará permitir, será necessária uma solução diferente. Em vez disso, talvez, seu registro devesse representar cada instância de manutenção de uma porta. Dessa maneira, você acaba com um número indeterminado de registros, não campos. Mas, então,você precisa repetir todas as informações sobre a porta em si. Você precisa saber em que porta foi feita a manutenção e se há bastante informação relacionada a isso.Você precisará de um campo que identifique exclusivamente a porta, um que exiba sua data de instalação, um outro para o nome do fabricante, a data de término de garantia, se é uma porta corta-fogo, se a porta é fechada a chave e assim por diante. É uma perda de tempo e desnecessário repetir todas essas informações estáticas em cada registro de detalhes criado para descrever a manutenção periódica.
Utilizando joins para criar estruturas relacionais A solução para esse problema, como para a maioria dos problemas semelhantes, é estruturar o banco de dados de modo que você tenha uma tabela que contém as informações que não sofrem alterações e um outro que contém as informações que sofrem alterações. Nesse caso, você teria uma tabela com informações estáticas sobre a porta (seu ID,sua data de instalação e assim por diante) e outra tabela que contém informações sobre a manutenção de porta (a data em que foi feita a manutenção,quem fez o serviço e assim por diante). A tabela Doors armazena as informações de uma porta em particular que não mudarão ou não apresentam essa probabilidade: seu ID exclusivo, seu fabricante, sua localização e assim por diante. Elas são chamadas registros-pai . A tabela DoorMaintenance armazena as informações que você espera alterar de registro a registro: a ação tomada (inspeção ou reparo,por exemplo), o nome de um técnico,a data em que a ação foi tomada, o resultado da ação e assim por diante. Elas são chamadas registros-filho. Cada registro-filho pertence a um registro-pai em particular. O que é crucial nessa configuração é que cada uma das duas tabelas tem um campo que permite vinculá-las, ou fazer a join delas, de tal maneira que se você se concentrar em um registro-pai em particular você automaticamente obtém somente os registros-filho pertencentes ao pai. Nesse estudo de caso,você quer certificar-se de que ao solicitar informações sobre a porta de entrada no lado norte do primeiro andar do edifício,os únicos registros de inspeção e reparos que são exibidos são aqueles que pertencem àquela porta. Esse relacionamento é mostrado graficamente no painel de tabelas, independentemente de você estar utilizando o Microsoft Query ou um gerenciador de bancos de dados como o Access (veja a Figura 5.8).
114
Gerenciando dados com o Microsoft Excel
Figura 5.8 A linha entre as duas tabelas é chamada de join.
Observe na Figura 5.8 que a tabela chamada Doors armazena informações relativamente estáticas sobre uma porta: sua classificação, se é uma porta corta-fogo ou não, seu andar e assim por diante. A tabela chamada DoorDetails armazena as informações que são alteradas — nesse caso, os dados são alterados com o tempo: a data em que uma porta foi inspecionada, a data em que foi consertada, qual era o problema e assim por diante. Em particular, observe na Figura 5.8 que as tabelas Doors e DoorDetails incluem um campo chamado DoorID. É esse campo compartilhado que estabelece um relacionamento entre as duas tabelas, e isso estabelece uma estrutura relacional . Isso não é apenas mais um arquivo simples. A T O N
É bom, mas não necessário, que as duas instâncias do campo tenham o mesmo nome. Na tabela Doors, ele poderia ter sido chamado de DoorID e na tabela DoorDetails, de DoorIdentifier.
Entendendo inner joins Quando duas ou mais tabelas são unidas, como estão na Figura 5.8, sua consulta pode retornar registros de uma tabela e registros relacionados da outra tabela. Os registros são relacionados se tiverem o mesmo valor nos campos que estão nas extremidades da join. A Figura 5.9 mostra registros retornados do Microsoft Access para o Microsoft Excel por essa consulta. Observe que o valor de DoorID da tabela de registros-pai (registros das portas) é sempre o mesmo do valor de DoorID da tabela de registros-filho (registros de manutenção das portas). Observe também na Figura 5.9 que a porta com DoorID 1A0A003 aparece seis vezes. Os dados nas colunas A e B vêm da tabela Doors, que contém somente uma instância dessa porta em particular.Essa porta aparece seis vezes em DoorDetails: uma para cada vez que um técnico da Ballou fez um serviço na porta. Relacionando as duas tabelas, a consulta é capaz de exibir informações estáticas, como andar e local, junto com as informações que são alteradas, como a data em que a porta foi inspecionada. A estrutura relacional permite a você evitar os dois problemas discutidos na seção anterior: colocar um número grande de campos em um registro quando eles poderiam ser ou não utilizados, e repetir desnecessariamente informações estáticas nos muitos registros de detalhes.
Capítulo 5 – Utilizando o Microsoft Query
115
Figura 5.9 Não é necessário retornar os campos da join da consulta. Eles são mostrados aqui somente para mais clareza.
Na Figura 5.8, a linha da join que conecta os campos DoorID das duas tabelas é o tipo padrão: uma inner join. Uma inner join retorna um registro somente se o mesmo valor existir nos dois campos de relacionamento. Por exemplo,a Figura 5.8 mostra um registro com o valor1A0A321 no campo DoorID da tabela Doors, e também no campo DoorID da tabela DoorDetails. Dado o tipo de join, a consulta não retornaria esse registro se qualquer uma das tabelas não tivesse nenhum registro com esse valor. Vamos colocar dessa maneira: uma inner join não retornará um registro a menos que os dois campos de relacionamento tenham o mesmo valor. Há alguns aspectos que devem ser lembrados sobre os campos que são unidos: • Os campos devem ter o mesmo tipo de dados; um não pode ser numérico e o outro de texto, por exemplo. • Se os campos têm o mesmo nome e tipo de dados e um deles é uma chave primária da tabela, o Microsoft Query e o Microsoft Access conseguem fazer a join das tabelas automaticamente. • Se a join for do tipo padrão (uma inner join), a consulta retorna somente os registros com valores idênticos nos campos de relacionamento.
A T O N
➪
Uma chave primária é um campo em uma tabela que identifica exclusivamente cada registro. Por exemplo, se um CIC em particular for atribuído a exatamente uma pessoa, o campo CIC poderia ser uma chave primária da tabela. No estudo de caso Doors utilizado neste capítulo, DoorID é a chave primária da tabela Doors: ela identifica exclusivamente uma porta. Ela não pode ser a chave primária da tabela DoorDetails, porque cada porta pode aparecer mais de uma vez nessa tabela; todos os valores de uma chave primária devem ser exclusivos. As chaves primárias têm aplicabilidade ampla no projeto de banco de dados.
Para obter informações adicionais sobre chaves primárias e outras chaves, veja“Estabelecendo chaves”,p. 228.
116
Gerenciando dados com o Microsoft Excel
Entendendo outer joins Há dois outros tipos de join: uma left outer join e uma right outer join. Esses termos não são intuiti vamente significativos e a única razão para serem mencionados aqui é para que você os reconheça se alguma vez os vir em uma instrução SQL. Na Figura 5.8, suponha que a tabela Doors contém uma porta cujo DoorID é 1A0A321A e que a tabela DoorDetails não tem nenhum registro com essa DoorID (talvez porque nenhum serviço foi feito nessa porta nesse ano). A consulta não retornará nenhum registro com DoorID 1A0A321A de DoorDetails, porque não há nenhum nessa tabela. E o registro que existe na tabela Doors com DoorID 1A0A321A não será retornado: a inner join requer que exista uma correspondência para retornar qualquer registro que seja. A Figura 5.10 mostra o que acontece se, no Microsoft Query, você escolher Relações no menu Tabela (ou se der um clique duplo na linha de relacionamento). Figura 5.10 A primeira opção especifica o padrão, uma inner join. A segunda e terceira opções especificam outer joins.
Suponha que você selecione a segunda opção, Todos os valores de ‘Doors’ e Somente registros de ‘DoorDetails’ onde Doors.DoorID = Doordetails.DoorID e, depois, clique em Adicionar e, em seguida, em Fechar. A Figura 5.11 mostra o que acontece na janela do Microsoft Query. Há duas diferenças principais entre as Figuras 5.8 e 5.11. Uma são os registros adicionais no painel de dados da Figura 5.11. Esses registros adicionais têm valores no campo DoorID na tabela Doors, mas nenhum para o campo DoorID na tabela DoorDetails. E isso é o que essa join solicita: todos os valores de Doors, e somente os registros de DoorDetails com valores correspondentes em DoorID. No lugar de um valor DoorID e DateInspected de DoorDetails, a consulta retorna valores nulos — marcadores de lugar, de certo modo. Compare esse resultado com aquele mostrado na Figura 5.8. Lá o registro com o valor DoorID, por exemplo, 1A0A321A não foi mostrado, porque não havia nenhum registro correspondente em DoorDetails. Na Figura 5.11, porém, a join solicita todos os registros da tabela Doors, independentemente de terem ou não registros correspondentes em DoorDetails .
Capítulo 5 – Utilizando o Microsoft Query
117
Figura 5.11 Observe a ponta da seta no fim da linha de relacionamento: ela aponta para a tabela que talvez não corresponda a um valor de relacionamento.
A C I D
Esse tipo de join oferece uma maneira conveniente de localizar registros em uma tabela que não têm registros correspondentes em uma outra tabela. Na Figura 5.11, você poderia fornecer o critério Is Null para o campo DoorID em DoorDetails. Executar a consulta retornaria todos os registros (e somente aqueles registros) com valores de DoorID na tabela Doors que não tiverem valores correspondentes no campo DoorID de DoorDetails.
A outra diferença principal entre as Figuras 5.8 e 5.11 é a ponta da seta no fim da linha de relacionamento na Figura 5.11. No Microsoft Query e no Microsoft Access, quando você vê a cabeça de seta na linha de relacionamento de uma consulta, você sabe que uma outer join foi especificada. Você sabe também que a tabela apontada pela seta é a tabela que retornará valores nulos para aqueles registros que não têm valores correspondentes no campo de relacionamento. A diferença entre uma left outer join e uma right outer join é insignificante: é meramente uma questão de qual tabela é mencionada antes ou depois do relacionamento. Essas duas joins são equivalentes: FROM Doors LEFT OUTER JOIN DoorsDetails ON Doors.DoorID = DoorsDetails.DoorID; FROM DoorsDetails RIGHT OUTER JOIN Doors ON DoorsDetails.DoorID = Doors.DoorID;
Em uma left join, a tabela identificada à esquerda da JOIN retorna todos os seus registros; em uma right join, a tabela identificada à direita da JOIN retorna todos os seus registros. (A palavra OUTER é opcional na SQL.)
Utilizando consultas de bancos de dados De uma maneira pelo menos, depois que você estabeleceu uma outer join utilizando o Microsoft Query, você atingiu o limite dessa aplicação. Você não poderá ter mais de duas tabelas no painel de tabelas do Microsoft Query se tiver utilizado uma outer join (veja a Figura 5.12).
118
Gerenciando dados com o Microsoft Excel
Figura 5.12 Para estabelecer uma outer join com mais de duas tabelas, você precisa de um gerenciador de consultas mais sofisticado.
Portanto, se estiver em uma situação mesmo que ligeiramente mais complicada, você precisará envolver um sistema de gerenciamento de banco de dados diretamente. Essas aplicações, como o Access, o SQL Server e o Oracle, não impõem limites tão restritivos à criação de consultas.
Criando uma consulta no Access Pelo fato de o Access geralmente acompanhar o Excel nas edições do Office, ele se torna uma plataforma conveniente para discutir a criação e o gerenciamento de consultas em um banco de dados. Mas os conceitos abordados aqui vão muito mais além do Access e são empregados em todos os sistemas de banco de dados relacional.
A T O N
Os capítulos a seguir mostram como utilizar o Excel para criar consultas que são executadas pelo banco de dados. Este livro, afinal de contas, é intitulado Gerenciando dados com o Microsoft Excel , e não Gerenciando dados com o Microsoft Access. Mas é útil saber como construir a consulta utilizando o banco de dados antes de construí-la, um passo a menos, a partir da pasta de trabalho.
Suponha que o conjunto de dados que este capítulo discutiu até agora esteja localizado em um banco de dados do Access. Para estender a consulta sobre portas de um edifício além dos recursos do Microsoft Query, você talvez utilize o Access para construir uma consulta mais complicada. Então, para obter os dados para um intervalo de dados externos ou uma tabela dinâmica do Excel, você utiliza o Microsoft Query para tratar a consulta do Access exatamente como se ela fosse uma tabela.
Capítulo 5 – Utilizando o Microsoft Query
119
Você começaria abrindo o banco de dados no Access. Depois de abrir o banco de dados, você verá a janela principal do Access, como mostrado na Figura 5.13. Figura 5.13 A colocação dos objetos (tabelas, consultas, formulários e assim por diante) depende da versão do Access que você está utilizando.
Você pode ver as tabelas chamadas Doors, DoorLocations e DoorDetails na janela Banco de dados. Para construir uma consulta usando essas tabelas, comece clicando no botão (ou guia, no Access 97) Consultas. Em seguida, clique em Novo para estabelecer uma nova consulta. A janela Nova consulta mostrada na Figura 5.14 é exibida. Figura 5.14 Os assistentes são ocasionalmente úteis, mas você construirá a maioria de suas consultas no modo de exibição Design.
Certifique-se de que o modo de exibição Design está selecionado na caixa de listagem e clique em OK. A caixa Mostrar tabela é exibida: ela é semelhante à caixa Adicionar tabelas que você viu anteriormente na Figura 4.12 do Capítulo 4, “Importando dados: uma visão geral”, que é parte do Microsoft Query. A caixa Mostrar tabela permanece aberta até que você clique no bot ão Fechar. Isso permite a você continuar adicionando tabelas e consultas até terminar.
A C I D
Se você tiver apenas algumas tabelas e consultas no banco de dados, utilize a guia Ambas para mostrar as tabelas e as consultas. Se você tiver uma grande quantidade delas, que precisa utilizar a barra de rolagem para localizar todas, é mais fácil fazer a seleção utilizando primeiro a guia Tabelas e, em seguida, a guia Consultas.
Após ter selecionado as tabelas e consultas que você quer para sua nova consulta, clique em Fechar. A janela Consulta do Access aparece como mostrado na Figura 5.15.
120
Gerenciando dados com o Microsoft Excel
Figura 5.15 As tabelas são exibidas no painel de tabelas, da esquerda para a direita, na ordem em que você as seleciona na caixa Mostrar tabela.
Há algumas diferenças importantes entre a janela de consulta do Access e a janela do Microsoft Query (compare com, por exemplo, a Figura 5.11). • A janela Consulta do Access não tem nenhum painel de dados. Para ver o que sua consulta retorna, você precisa clicar no botão Executar, ou clicar no botão Exibir e escolher o modo de exibição Folha de dados na lista. Não há diferença entre os botões Executar e Exibir para consultas Seleção. Com outros tipos de consultas — Exclusão, Atualização ou Acréscimo, por exemplo — há uma razão para utilizar o botão Exibir em vez do botão Executar. O botão Exibir pode apresentar uma prévia daquilo que a consulta fará e o botão Executar, na realidade, executa a ação. • Você pode adicionar um campo à Grade de projeto e escolher não mostrá-lo nos resultados da consulta. Suponha, por e xemplo, que você que ira classificar registros em algum campo, mas não quer retornar esse campo na consulta. Coloque o campo na Grade de Design e escolha Crescente ou Decrescente na linha Classificação da grade. Por último, para evitar que a consulta exiba esse campo, desmarque a caixa de seleção do campo na linha Mostrar da grade. • O Access não limita você a duas tabelas em uma consulta quando você está utilizando uma outer join. A Figura 5.16 mostra como talvez você estruture uma consulta envol vendo as tabelas chamadas Doors , DoorLocations e DoorDetails . A consulta retorna todos os registros de Doors que têm correspondência nos registros em DoorLocations , quer eles tenham correspondência ou não nos registros em DoorDetails , e utiliza a tabela DoorLocations para mostrar o local de uma porta. Você não conseguiria fazer isso utilizando apenas o Microsoft Query. • Os dados podem ser resumidos na consulta. Por exemplo, você talvez simplesmente queira saber o número de vezes que uma porta foi inspecionada. Com o campo DoorID na grade de consulta duas vezes — uma vez a partir da tabela Doors e uma vez a partir da tabela DoorDetails — você clicaria no botão Totals. Na nova linha Totals, escolha Agrupar por para a tabela Doors e Contar para a tabela DoorDetails. O projeto resultante é mostrado na Figura 5.17. Ao clicar no botão Executar, você obtém o resultado mostrado na Figura 5.18. Essa lista de recursos não chega nem perto de ser completa: há muitas maneiras de ajustar uma consulta Seleção no Access que não estão disponíveis no Microsoft Query.
Capítulo 5 – Utilizando o Microsoft Query
Figura 5.16 No Access, a janela Consulta especifica de qual tabela um campo é proveniente.
Botão Totais
Figura 5.17 Além da Contar, outra estatística de resumo disponível inclui média, soma e desvio padrão (exibida como StDev).
Figura 5.18 Observe que DoorID 1A0A041C não tem nenhum registro na tabela DoorDetails. Portanto essa consulta utiliza uma outer join (veja a Figura 5.17).
121
122
Gerenciando dados com o Microsoft Excel
Depois que você construiu a consulta, salve-a escolhendo Salvar no menu Arquivo ou simplesmente feche-a. Você será perguntado se deseja salvar suas alterações e terá uma chance de dar à consulta um nome mais útil que um nome padrão como Consulta1, Consulta2 e assim por diante.
Utilizando o Microsoft Query para retornar os resultados de uma consulta ao banco de dados Depois que a consulta foi salva, você obtém seus resultados em uma planilha ou em uma tabela dinâmica do Excel da maneira normal via Microsoft Query, embora nesse caso você trabalhe com a consulta em vez de tabelas. Suponha que você crie uma consulta mostrada na Figura 5.16 e a chame de Consulta Doors. Você poderia retornar seus dados para o Excel utilizando estes passos: 1. Com uma planilha do Excel ativa, escolha Dados, Importar dados externos. Se você já definiu o banco de dados como uma fonte de dados, selecione-o na janela Escolher a fonte de dados (lembre-se de que adicionar uma nova consulta a uma fonte de dados não altera em nada a fonte de dados em si). Caso contrário, você precisará criá-lo por meio do item Nova fonte de dados. 2. Utilizando a janela Adicionar tabelas, adicione a Consulta Doors ao painel de tabelas do Microsoft Query. Se você não vir a Consulta Portas na lista Tabelas, clique no botão Opções, marque a caixa de seleção Modos de exibição (e desmarque a caixa de seleção Tabelas se quiser), e clique em OK (veja a Figura 5.19). Figura 5.19 Um modo de exibição é muito semelhante a uma consulta Seleção. No Access, os dois termos são quase sinônimos.
3. Arraste cada campo que você quer retornar para o painel de dados. A janela do Microsoft Query agora é exibida como mostrado na Figura 5.20. 4. Clique no botão Retornar dados, ou escolha Arquivo, Retornar dados ao Microsoft Office Excel.
A T O N
Como observado anteriormente, os modos de exibição são muito semelhantes às consultas Seleção. As tabelas de sistema são utilizadas pelo banco de dados para monitorar as informações sobre tabelas, consultas e outras estruturas que você definiu no próprio banco de dados. Sinônimos são a interseção de uma tabela e o usuário dessa tabela; é improvável que você precise visualizá-los, a menos que esteja consultando uma fonte de dados Oracle.
Capítulo 5 – Utilizando o Microsoft Query
Figura 5.20 Quando campos de duas ou mais tabelas têm o mesmo nome, eles são classificados pelo nome da tabela (por exemplo, Doors.DoorID ).
A Figura 5.21 mostra os dados retornados da Consulta Doors à planilha.
Figura 5.21 Observe as células vazias em alguns registros da DoorDetails: a outer join permite que a consulta retorne essesregistros.
123
124
Gerenciando dados com o Microsoft Excel
Controlando o intervalo de dados Após você ter retornado dados para um intervalo de dados externos em uma planilha do Excel, o intervalo terá algumas propriedades adicionais com as quais você talvez queira trabalhar.
A T O N
As aplicações do Microsoft Office utilizam o termo propriedades para referir-se a aspectos de objetos. Como apenas um grande números de exemplos, um intervalo de células tem uma propriedade Bordas que especifica bordas (superior,inferior,esquerda, direita,diagonal ou nenhuma) do intervalo. Até se familiarizar com a noção de propriedades talvez você ache útil pensar em uma propriedade como uma opção.
Clique no botão direito do mouse em qualquer célula de um intervalo de dados externos e escolha Propriedades do intervalo de dados no menu de atalho. A caixa de diálogo mostrada na Figura 5.22 será exibida.
Figura 5.22 Se você não vir as Propriedades do intervalo de dados no menu de atalho, você não clicou dentro de um intervalo de dados externos.
Do ponto de vista de gerenciamento dos dados externos, as propriedades mais importantes mostradas na Figura 5.22 são • Salvar definição de consulta . Não desmarque essa caixa a menos que você esteja certo daquilo que está fazendo. Se você desmarcá-la, não será possível atualizar os dados da consulta ou mesmo editá-la. Se desmarcar a caixa de seleção e, em seguida, salvar a pasta de trabalho, você terá perdido a consulta para sempre. • Salvar senha. Essa propriedade é importante principalmente em um ambiente em rede. (Se você estiver em um ambiente independente, a sua senha está protegendo os seus dados de quem? Você mesmo?) Você pode salvar a senha no arquivo DSN que define o conjunto de dados — seu caminho, seu tipo e assim por diante — mas esse arquivo está armazenado no formato ASCII, então qualquer pessoa suficientemente curiosa pode obter a senha. Em comparação, a definição de consulta que foi salva na pasta de trabalho está em um nome oculto, algo que é muito mais difícil de descobrir.
Capítulo 5 – Utilizando o Microsoft Query
125
• Ativar atualização em segundo plano. Marcar essa caixa de seleção significa que as atualizações podem ocorrer sem interromper seu trabalho normal no Excel. Você pode estar inserindo fórmulas ou girando tabelas enquanto o Excel atualiza o intervalo de dados externos. Desmarcar a caixa de seleção significa que você não pode prosseguir até que a consulta tenha terminado de ser executada. Essa propriedade provavelmente terá utilidade para você somente se a fonte de dados em si for atualizada freqüentemente e for grande o suficiente para fazer com que as consultas levem muito tempo para serem executadas. • Atualizar dados em ‘Abrir arquivo’ . Quando a pasta de trabalho for aberta, execute a consulta de modo que os dados mais recentes estejam disponíveis na fonte. O único motivo de preocupação é a presença de um handler de event o que é executado quando a pasta de trabalho é aberta. Então talvez você precise saber qual ocorre primeiro (o handler do evento Open é executado primeiro). ➪
Para localizar as informações adicionais sobre os dados atualizados, veja “Importando dados para tabela dinâmica”, p. 98.
Para as próximas quatro propriedades listadas na caixa de diálogo Propriedades do intervalo de dados externos, suponha que o intervalo de dados externos ocupa A1:B5 na planilha, como mostrado na Figura 5.23. Figura 5.23 O intervalo original de dados externos está sombreado.
Na Figura 5.23, os limites do i ntervalo original são indicados pelos números de linhas da coluna C e as letras da coluna na linha 6. O intervalo de dados está para ser atualizado. Cada uma das próximas seis figuras mostra efeito nos dados na Figura 5.23, de acordo com a propriedade selecionada, e se a consulta retorna mais ou menos registros do que aqueles mostrados na Figura 5.23. • Insira células para novos dados, exclua as células não utilizadas . Começando com a linha 6, as células nas colunas A e B serão empurradas para baixo conforme dois novos registros forem inseridos. As colunas C a IV não são afetadas. As células contend o “Column A” e “Column B” são empurradas para baixo para acomodar as células inseridas, mas nada acontece às células na coluna C (veja a Figura 5.24). Se a consulta de dados perde registros, as células abaixo da linha 5 são puxadas para cima conforme as células dos registros perdidos são excluídas. Novamente, somente as colunas A e B são afetadas. As células contendo “Column A” e “Column B” são puxadas para cima conforme as células são excluídas e nada aconteceu às células na coluna C (veja a Figura 5.25).
126
Gerenciando dados com o Microsoft Excel
Figura 5.24 O intervalo de dados externos também foi configurado como Preservar formatação de célula.
Figura 5.25 A consulta perdeu seus quatro registros originais e adicionou dois novos registros.
• Insira linhas inteiras para novos dados, limpe as células não utilizadas . Começando com a linha 5, todas as linhas são empurradas para baixo em 2 posições — e portanto as células C5:IV6 ficarão vazias. Observe na Figura 5.26 que as células na coluna C contendo a “Row 5”, “Row 6”, “Row 7”, “Column A” e “Column B” são empurradas para baixo pela inserção de duas linhas (embora essas linhas não sejam inseridas onde você talvez espere). Se a atualização retorna somente dois registros em vez dos quatro originais, as células A4:B5 são limpas. A Figura 5.27 mostra que as células contendo a “Column A” e a “Column B” permanecem no lugar porque as células não utilizadas da consulta não são excluídas. Observe também que somente o conteúdo das células A4:B5 foi limpo, não seu formato. Como nem as células nem as linhas são excluídas, todos os outros dados fora do intervalo de dados externos permanecem onde estavam. Figura 5.26 Observe que a inserção da linha indicada na coluna C não corresponde à inserção do registro indicada em A6:B7.
Figura 5.27 As células não utilizadas no intervalo de dados externos são limpas, não excluídas, mas o intervalo é redefinido para A1:B3.
Capítulo 5 – Utilizando o Microsoft Query
127
• Sobrescreva as células existentes com novos dados, limpe as não utilizadas. Nenhuma célula ou linha é inserida para acomodar novos registros. Se você tiver dados abaixo do intervalo de dados externos (e isso é normalmente uma falha no projeto da planilha), eles serão sobrescritos e você não será avisado. A Figura 5.28 mostra que os valores “Column A” e “Column B” foram sobrescritos por novos registros, mas nada aconteceu com os dados na coluna C, pois nada foi inserido. As células esvaziadas pela consulta por haver menos registros são limpas, dessa maneira nenhuma célula ou linha é excluída e os outros dados permanecem no lugar. Figura 5.28 Cuidado ao colocar as informações abaixo de um intervalo de dados externos; elas podem ser sobrescritas como mostrado aqui.
• Preencha fórmulas em colunas adjacentes aos dados . Essa pode ser uma maneira útil de evitar calcular campos na própria consulta, utilizando a sintaxe da fórmula do gerenciador de bancos de dados e funções predefinidas. Apenas crie uma fórmula na planilha em uma coluna imediatamente à esquerda ou à direita do intervalo de dados externos e marque essa caixa de seleção. Quando novos registros chegarem conforme você atualiza os dados, o Excel automaticamente copiará as fórmulas adjacentes existentes na linha final ocupada pelo intervalo de dados externos.
Gerenciando campos booleanos e caixas de seleção Um problema envolvido na importação de dados de um banco de dados para planilha são os campos booleanos. Booleanos também são chamados de campos Verdadeiro/Falso , e o Access freqüentemente refere-se a eles como campos Sim/Não. Os campos booleanos assumem um dentre apenas dois valores possíveis: Verdadeiro e Falso. As dificuldades surgem quando você retorna os valores desses campos para a planilha, pois os bancos de dados não necessariamente os armazenam como VERDADEIRO ou FALSO, os valores que você normalmente vê em uma planilha do Excel. Você precisa considerar isso, seja no banco de dados ou no Excel. E as caixas de seleção, que normalmente resultam em um valor Verdadeiro (marcado) ou um valor Falso (desmarcado) pode ter um terceiro valor, Nulo (sombreado).
Retornando valores booleanos à planilha Suponha que seu banco de dados contenha uma tabela chamada Patient_Restraints, e que o campo Medical_Reason é definido como booleano. A Figura 5.29 mostra como esse campo talvez seja exibido com a tabela no modo de exibição Folha de dados.
128
Gerenciando dados com o Microsoft Excel
Figura 5.29 O Access exibe campos booleanos como caixas de seleção na visualização Folha de dados.
A Figura 5.30 mostra como o campo Medical_Reason é exibido em uma planilha do Excel, dependendo de como você o coloca lá: • Se você seleciona o campo na folha de dados do Access, copie-o e, em seguida, vá para o Excel e cole-o numa planilha, você verá os valores TRUE e FALSE da maneira mostrada nas células A1:A11 da Figura 5.30. • Se você importa dados para uma planilha utilizando Importar dados externos, os valores TRUE são exibidos como 1 e os valores FALSE são exibidos como 0. Isso é mostrado nas células C1:C11 da Figura 5.30.
Figura 5.30 Colar um campo do Access em uma planilha fornece o nome de campo como um cabeçalho e configura o preenchimento do cabeçalho como cinza.
Se você quiser lidar com algo diferente de 1 e 0 em um intervalo de dados externos, considere atualização dos dados utilizando uma procedure do VBA como esse handler de evento Open:
Capítulo 5 – Utilizando o Microsoft Query
129
Private Sub Workbook_Open() Application.Goto Reference:="Query_from_MS_Access_Database" Selection.QueryTable.Refresh BackgroundQuery:=False Application.Goto Reference:="Query_from_MS_Access_Database" With Selection .Replace What:="0", Replacement:="FALSE", LookAt:=xlWhole .Replace What:="1", Replacement:="TRUE", LookAt:=xlWhole End With End Sub
Esse código primeiro seleciona o intervalo existente de consulta e, então, atualiza seus dados. Em seguida, ele seleciona novamente o intervalo de consulta no caso de o número de registros ter sido alterado devido à atualização. Por último, ele substitui 0 por FALSE e 1 por TRUE. Ele substitui um 0 ou 1 somente se for o valor inteiro da célula, para evitar, por exemplo, substituir o valor da célula de um cabeçalho Medical_Reason1 por Medical_ReasonTRUE.
Gerenciando dados de caixas de seleção Quando as caixas de seleção de um formulário do Access estão associadas a campos booleanos, nenhuma dificuldade surge. Mas, às vezes, você precisa fornecer não só os valores VERDADEIRO e FALSO, mas também um valor Nulo. Esse valor Nulo talvez signifique algo como Não aplicável. Suponha que na tabela Patient_Restraints, o campo Medical_Reason precisa assumir três valores: VERDADEIRO quando um paciente foi internado por razões médicas, FALSO quando houve alguma outra razão e Nulo quando uma internação não foi utilizada. Se o campo Medical_Reason estiver definido como booleano, não será possível distinguir entre FALSO e Nulo; nos dois casos, um campo do registro não está marcado. Mas se você definir o campo Medical_Reason como um Inteiro, ele poderá assumir muito mais valores. E isso abre a possibilidade de associá-lo a uma caixa de seleção em um formulário de dados que no Access tem uma propriedade TripleState . Com a propriedade TripleState ativa, a caixa de seleção do formulário pode estar marcada (VERDADEIRO), desmarcada (FALSO) ou sombreada (algum outro significado, em geral, Não aplicável). A Figura 5.31 mostra como esse formulário de dados talvez seja exibido na tela.
Figura 5.31 A caixa de seleção sombreada indica um valor Nulo.
Se você configurar, ou não, a caixa de seleção do formulário para assumir um dos três valores, se o campo subjacente for numérico então TRUE é armazenado como –1 (em vez de 1), enquanto FALSE ainda é armazenado como 0. Se a caixa de seleção do formulário estiver configurada para TripleState, o próprio campo armazena um Nulo como o valor quando a caixa de seleção estiver sombreada.
130
Gerenciando dados com o Microsoft Excel
A Figura 5.32 mostra como intervalos de dados externos são exibidos quando um campo é definido como numérico e seus valores são determinados por uma caixa de seleção em um formulário de dados. As células A1A11 mostram 10 valores. Os cinco 1s representam que a caixa de seleção do formulário foi marcada para esses registros. Os cinco 0s representam que a caixa de seleção do formulário não foi marcada. As células D1:D11 também mostram 10 valores. O –1 quer dizer que a caixa de seleção foi marcada e o 0, significa que não foi marcada. As células em branco nas linhas 8 a 11 significam que a caixa de seleção permaneceu sombreada. Figura 5.32 Para ajudar a distinguir entre um valor Nulo e nenhum registro, selecione a propriedade Incluir números de linhas do intervalo.
Se você substituir 0 por FALSE e 1 por TRUE no intervalo de dados externos, deve evitar substituir os números de linhas mostrados na coluna C. Então, altere o código mostrado anteriormente para substituir fora da coluna de números de linhas: Sub Workbook_Open() Dim ExtRange As Range Dim NCols As Integer, NRows As Integer Set ExtRange = Worksheets("Sheet1") _ .Names("Query_from_MS_Access_Database").RefersToRange NCols = ExtRange.Columns.Count - 1 NRows = ExtRange.Rows.Count Application.Goto Reference:="Query_from_MS_Access_Database" Selection.QueryTable.Refresh BackgroundQuery:=False ExtRange.Offset(0, 1).Resize(NRows, NCols).Select With Selection .Replace What:="0", Replacement:="FALSE", LookAt:=xlWhole .Replace What:="-1", Replacement:="TRUE", LookAt:=xlWhole .Replace What:="", Replacement:="#N/A", LookAt:=xlWhole End With End Sub
Esse código conta o número de colunas e linhas no intervalo de dados externos e, então, seleciona o intervalo de células que é deslocado desse intervalo em uma coluna, com o mesmo número de linhas e uma coluna a menos. No intervalo selecionado, ele substitui 1 e 0 como antes e, além disso, substitui as células em branco pelo valor de erro #N/A.
Capítulo 5 – Utilizando o Microsoft Query
131
Olhando para frente Este capítulo estendeu a discussão, iniciada no Capítulo 4, sobre a utilização do Microsoft Query para importar dados de uma fonte de dados externos para uma planilha do Excel. O resultado é um foco no comando Nova consulta ao banco de dados. Mas há outras opções disponíveis para você no menu Dados principal, incluindo gerenciamento de seus dados utilizando tabelas dinâmicas, agrupamento em campos contínuos e utilização de consultas da Web. Você encontrará as informações sobre essas opções no Capítulo 6, “Importando dados: considerações adicionais”.
6 Importando dados: considerações adicionais Entendendo tabelas dinâmicas As tabelas dinâmicas são o meio de resumir dados mais poderosos do Excel. Se você estiver interessado em somar despesas mensalmente, ou descobrir a renda média das pessoas em várias funções, ou em obter uma contagem do número de itens que você vendeu por linha de produto, uma tabela dinâmica é normalmente a maneira de fazê-lo no Excel. Por causa de seus recursos de resumo, as tabelas dinâmicas são ferramentas não apenas para a análise de dados, mas também para o gerenciamento de dados. Se você ainda não tiver utilizado tabelas dinâmicas de maneira ampla, você achará esta seção uma introdução útil. Se você for um usuário experiente, talvez prefira pular para a próxima seção, “Preparando dados para tabelas dinâmicas”.
Criando uma tabela dinâmica A Figura 6.1 mostra um exemplo simples de uma tabela dinâmica e seus dados subjacentes. Não seria difícil construir cinco fórmulas de matriz que retornassem o total de unidades vendidas por região; por exemplo =SOMA(SE(B2:B39="South",D2:D39,0))
Mas por que se preocupar? A tabela dinâmica dá a você esses totais de maneira muito rápida e fácil. Eis os passos a serem seguidos, dado que você tem uma lista do Excel como aquela mostrada nas colunas A a D da Figura 6.1. Certifique-se de que você está utilizando uma lista como descrito no Capítulo 2, “Recursos de gerenciamento de dados do Excel”, com cabeçalhos de coluna que identificam os campos.
1. Escolha Dados, Relatório de tabela e gráfico dinâmicos. O passo 1 do Assistente de tabela dinâmica mostrado na Figura 6.2 será exibido. 2. Nessa instância, você selecionaria Banco de dados ou lista do Microsoft Office Excel e Tabela dinâmica como um tipo de relatório. (Um banco de dados do Excel é um termo arcaico para uma lista.) Escolha Avançar .
Capítulo 6 – Importando dados: Considerações adicionais
133
Figura 6.1 A maioria das tabelas dinâmicas é baseada em uma lista do Excel ou em uma fonte de dados externos.
Figura 6.2 Basear uma tabela dinâmica em uma outra significa que elas utilizam o mesmo cache de dados — uma utilização eficiente de memória.
3. No passo 2 do Assistente de Tabela Dinâmica, mostrado na Figura 6.3, arraste pelo intervalo
de dados na planilha de modo que seu endereço, A1:D39 na Figura 6.1, seja exibido na caixa Intervalo. Clique em Avançar .
134
Gerenciando dados com o Microsoft Excel
Figura 6.3 Se os dados subjacentes estiverem em uma pasta de trabalho diferente, você pode clicar no botão Procurar para localizá-los.
4.
O passo 3 do Assistente de Tabela Dinâmica é exibido (veja a Figura 6.4). Selecione o local para a tabela dinâmica e clique em Concluir .
Figura 6.4 Uma tabela dinâmica por padrão inicia na linha 3 para deixar espaço para um campo de Página.
Um diagrama para a tabela dinâmica é exibido na planilha, junto com a Lista de campos da tabela dinâmica, como mostrado na Figura 6.5. Para reproduzir a tabela dinâmica mostrada na Figura 6.1, você arrastaria o campo Region da Lista de campos para a área de campos de linha, e arrastaria o campo Units Sold para a área Itens de dados. Assim que você coloca um campo na área Itens de dados, a tabela dinâmica substitui o diagrama na planilha. Figura 6.5 Se preferir, você pode utilizar o drop-down Adicionar a na Lista de campos de tabela dinâmica em vez de arrastar e soltar campos na planilha.
A T O N
No Excel 97, o Assistente de Tabela Dinâmica é um pouco diferente daquele mostrado nas figuras 6.2 a 6.5. Em particular, há um passo de layout no qual você pode projetar a tabela dinâmica. Nas versões subseqüentes, você cria o layout diretamente na planilha como acaba de ser descrito. Você pode passar para o passo de layout clicando no botão Layout no terceiro passo do assistente.
Capítulo 6 – Importando dados: Considerações adicionais
135
Reconfigurando uma tabela dinâmica Por que tabela dinâmica ( pivot table)? Considerando tudo que uma tabela dinâmica é capaz, a razão para o termo é relativamente insignificante. Na Figura 6.1, se você clicar no botão rotulado Region, arrastá-lo para a célula G3 e liberar o botão do mouse, a tabela pivota, ou gira — isto é, a Region do campo Linha torna-se Region do campo Coluna. Isso é engraçado, mas em quase 10 anos de utilização de tabelas dinâmicas para analisar e gerenciar dados, eu raramente tive motivo para girar uma tabela, exceto para explicar o termo às pessoas. As tabelas dinâmicas são capazes de muito, muito mais que apenas pivotar. A Figura 6.1 mostra a Soma de unidades vendidas. Além da Soma, há muitos resumos que você pode escolher, incluindo Contagem, Média, Máximo e Mínimo. Por padrão o resumo do campo de Dados numéricos é Soma, e o resumo do campo Dados de texto é Contagem. Para selecionar uma estatística de resumo diferente, clique com o botão direito do mouse em qualquer célula no campo Dados, escolha Configurações de campo no menu de atalho, e localize o resumo que você quer na caixa de listagem Resumir por. Você também pode ter um campo Linha e um campo Coluna em uma tabela dinâmica. Isso ajuda você a avaliar o efeito em conjunto dos dois campos. A Figura 6.6 dá um exemplo.
Figura 6.6 Para adicionar o Product como o campo Coluna, arraste-o da Lista de campos para área Coluna no diagrama mostrado na Figura 6.5.
Além disso, uma tabela dinâmica pode acomodar mais de um campo Linha e um campo de Coluna. A Figura 6.7 mostra a tabela dinâmica da Figura 6.6, mas com um campo Linha externo e um interno e nenhum campo Coluna. Para obter dois campos Linha, por exemplo, basta arrastá-los para a área Linha do diagrama. Ou, para revisar o layout depois que a tabela dinâmica foi criada, basta arrastar o botão Region diretamente para a direita do botão Product.
136
Gerenciando dados com o Microsoft Excel
Figura 6.7 Para obter subtotais da Region, clique com o botão direito do mouse em Region, escolha Configurações de campo, escolha Resumir por e selecione um subtotal como Soma.
Além dos campos Dados, Linha e Coluna, as tabelas dinâmicas também podem ter campos de Página. Os campos de Página não funcionam da mesma maneira que os campos Linha e Coluna. O objetivo deles é permitir a você selecionar um subconjunto de dados subjacentes e fazer com que a tabela dinâmica exiba somente esse subconjunto. A Figura 6.8 mostra os dados da Figura 6.1 utilizando Product como um campo Página e exibindo somente as informações do produto Coffee. Figura 6.8 Sua tabela dinâmica incluirá os registros de cada item em seu campo de Página se você selecionar Tudo no seu dropdown.
Se quiser, você pode utilizar mais de um campo de Página. Se você fizer isso, eles se comportarão como se estivessem conectados por Es. Por exemplo, a tabela dinâmica na Figura 6.9 mostra apenas as Unidades Vendidas (Units Sold) do produto Coffee na região Northeast.
Capítulo 6 – Importando dados: Considerações adicionais
137
Figura 6.9 Somente os locais da região Northeast são mostrados quando você escolhe esse item em um campo de Página.
Você pode configurar certas opções em tabelas dinâmicas. Para fazer isso, clique com o botão direito do mouse em uma célula na tabela dinâmica e escolha Opções de tabela no menu de atalho. A caixa de diálogo mostrada na Figura 6.10 aparece. Figura 6.10 Dar à tabela dinâmica um nome descritivo é útil principalmente se mais tarde você quiser se basear nela para criar outra tabela dinâmica.
As mais importantes dessas opções são as seguintes.
Totais gerais para colunas, Totais gerais para linhas Consulte a Figura 6.6, que mostra uma tabela dinâmica com um campo Linha e um campo Coluna. Ela mostra Total Geral na linha 12 e na coluna I. Se você desmarcar a caixa de seleção Totais gerais para colunas, a tabela dinâmica não mostra os totais na linha 12. Se desmarcar a caixa de seleção Totais gerais para linhas, a tabela dinâmica não mostra os totais na coluna I.
AutoFormatação de tabela O Excel oferece 22 formatos de tabela dinâmica por meio do botão Formatar relatóri o na barra de ferramentas Tabela dinâmica. Se, tendo aplicado um dos formatos, você desejar remover o formato da tabela dinâmica, desmarque a caixa de seleção AutoFormatação de tabela.
138
A T O N
Gerenciando dados com o Microsoft Excel
O formato que estiver rotulado Nenhum não é o mesmo que o formato padrão aplicado quando você desmarca a caixa de seleção AutoFormatação de tabela.
Mesclar rótulos A Figura 6.7 mostra uma tabela dinâmica com um campo externo Linha, Product, e um campo interno Linha, Region. Se você marcar a caixa de seleção Mesclar rótulos, as células F5:F10 e as células F12:F17 serão mescladas (como se você tivesse escolhido Formato, Células, Alinhamento e marcado a caixa de seleção Mesclar células). Isso também centraliza os rótulos das células mescladas nos sentidos horizontal e vertical.
Preservar formatação Quando você está trabalhando com o campo Dados de uma tabela dinâmica, normalmente é melhor formatá-lo clicando com o botão direito do mouse em uma de suas células, escolhendo Configurações de Campo no menu de atalho e, então, clicando no botão Número. Dessa maneira, o formato de número que você selecionar será preservado se você pivotar ou atualizar a tabela. Mas se você formatar as células do campo Dados diretamente (ou as células em um campo Linha ou Coluna) selecionando-as e, em seguida, escolhendo Células no menu Formatar, é possí vel perder a formatação (em particular, formatos de data). Marcar a caixa de seleção Preservar formatação ajuda a salvar a formatação da célula direta.
Repetir rótulos de item em cada página impressa Se você tivesse uma tabela dinâmica extensa, ela poderia distribuir-se por mais de uma página impressa. E se você tiver somente um campo Linha, o rótulo que está em efeito na parte superior da segunda, ou subseqüente, página impressa aparece na parte superior dessa página. Mas talvez você tenha mais de um campo Linha ou Coluna, como na Figura 6.7. Nesse caso, o rótulo do campo externo Linha ou Coluna não é repetido na parte superior de cada página se essa caixa de seleção não estiver marcada.
Layout de página Escolha Vertical para ter múltiplos campos Página empilhados verticalmente, como na Figura 6.9, ou escolha De um lado a outro para exibi-los lado a lado.
Campos por coluna ou campos por linha Dependendo de sua escolha de Layout de página, os campos Página são empilhados em colunas ou colocados lado a lado nas linhas. Suponha que você escolha Vertical. Essa opção permite a você especificar quantos campos Página são exibidos em uma coluna antes de campos Página adicionais serem colocados em uma coluna adjacente. Se você escolher De um lado a outro, poderá especificar quantos campos de página serão exibidos em uma determinada linha antes de campos adicionais serem colocados na próxima linha.
Para valores de erro, mostrar Se houver valores de erro como #DIV/0! ou #REF!na fonte de dados da tabela dinâmica, eles são exibidos na própria tabela dinâmica. Talvez você queira que um valor diferente, como Error, seja
Capítulo 6 – Importando dados: Considerações adicionais
139
exibido na tabela dinâmica. Nesse caso, marque essa caixa de seleção e digite o valor que você quer que seja mostrado na caixa de edição associada. Para células vazias, mostrar
Essa caixa de seleção e sua caixa de edição associada agem da mesma maneira que seus componentes em Para valores de erro, mostrar, mas para valores ausentes em vez de valores de erro. Salvar dados com layout de tabela
Uma tabela dinâmica pode armazenar seus dados subjacentes em um cache. É esse cache que torna a tabela dinâmica tão eficiente em recalcular resultados quando você girá-la ou alterá-la de alguma outra maneira. Se você desmarcar essa caixa de seleção, a tabela dinâmica não salvará um cache. Ao reabrir sua pasta de trabalho, você terá de atualizar os dados da tabela dinâmica antes de alterar a estrutura da tabela. Você libera algum espaço de armazenamento omitindo o cache. Ativar análise de dados
Se você salvou o cache junto com a tabela dinâmica, poderá chegar aos dados subjacentes de qualquer célula no campo de Dados. Bastar dar um clique duplo nessa célula, que o Excel insere uma nova planilha com uma lista. A lista contém todos os campos de todos os registros que pertencem à célula em que você clicou duas vezes. Se desmarcar essa caixa de seleção, você não conseguirá fazer isso e talvez queira evitar que outros usuários de sua tabela dinâmica vejam registros de detalhes. Se quiser fazer a análise de detalhes, você também deve marcar a caixa de seleção Salvar dados com layout de tabela. Atualizar ao abrir
Essa é uma opção muito útil se os dados que formam a base da tabela dinâmica forem alterados ocasionalmente. Se a caixa de seleção estiver marcada, a tabela dinâmica automaticamente se atualiza a partir da fonte de dados quando você abre a pasta de trabalho.
A C I D
Muitas das pastas de trabalho que eu preparo para meus clientes têm até 20 tabelas dinâmicas baseadas em fontes de dados externos como bancos de dados do Access. Pode levar um longo e frustrante tempo até que todas as tabelas dinâmicas nessas pastas de trabalho sejam atualizadas. Nesses casos, eu prefiro desmarcar essa opção e utilizar o evento Open da pasta de trabalho para executar o código VBA que pergunta ao usuário se ele quer atualizar as tabelas dinâmicas. Em caso afirmativo, o evento executa mais código VBA que atualiza todas as tabelas dinâmicas na pasta de trabalho.
As seguintes opções estão disponíveis somente se a tabela dinâmica estiver baseada em uma fonte de dados externos. Atualizar a cada x minutos
Você pode fazer com que a tabela dinâmica se atualize a partir da fonte de dados marcando a caixa de seleção e configurando o número de minutos com o seletor.
140
Gerenciando dados com o Microsoft Excel
Salvar senha
Se a fonte de dados externos for protegida por senha, você pode fazer com que o Excel salve a senha de modo que você (e outros usuários da pasta de trabalho) não tenha de fornecê-la quando a tabela dinâmica for atualizada mais tarde. É mais seguro salvar a senha da fonte de dados desse modo do que salvá-la com um arquivo DSN, que pode ser lido com algo tão universal e básico quanto o Bloco de notas. Consulta em segundo plano
Se sua fonte de dados externos suporta consultas assíncronas, talvez você queira utilizar essa opção. Se a caixa de seleção estiver marcada, você pode continuar a fazer outro trabalho no Excel enquanto a consulta atualiza a tabela dinâmica. Se a caixa de seleção estiver desmarcada, você terá de esperar até que a atualização seja concluída antes de poder fazer qualquer outra coisa na pasta de trabalho. Essa opção é útil principalmente em ambientes de rede muito lentos. Otimizar memória
Se você marcar essa caixa de seleção, o Excel executa alguns passos preliminares antes de atualizar uma tabela dinâmica. Em particular, ele determina quantos itens exclusivos há nos campos de linha e coluna da tabela dinâmica. Fazendo isso, o Excel é capaz de gerenciar suas alocações de memória de maneira mais eficiente. Talvez você observe uma ligeira redução no desempenho enquanto o Excel faz essas verificações.
Preparando dados para tabelas dinâmicas Há algumas considerações especiais que devem ser lembradas quando você especifica os registros que quer utilizar em tabelas dinâmicas. Essas questões não se aplicam a intervalos de dados externos, a menos que você, então, utilize o intervalo de dados externos como a fonte de uma tabela dinâmica. A idéia é evitar inserir registros na tabela dinâmica se eles contiverem valores nulos em um campo que você quer utilizar para agrupar registros.
Agrupando em campos de data e hora Freqüentemente acontece de você querer analisar dados de uma tabela dinâmica utilizando uma data ou hora do dia como uma linha, coluna ou campo de página. Por exemplo, talvez você queira saber o número médio de acidentes de trânsito por hora do dia e durante cada dia da semana. Quando é importante saber a hora e/ou data que algo ocorre, é comum registrar a hora exata do dia da ocorrência e, freqüentemente, sua data específica. Mas quando você quer analisar os dados, é raro preocupar-se com o minuto exato da hora em que o evento ocorreu. Pelo menos no plano da estatística descritiva, você raramente presta atenção ao fato de que dois acidentes de trânsito ocorreram às 16h37 enquanto um ocorreu às 16h38. Assim, você gostaria de agrupar seus dados de acordo com categorias mais amplas que minutos — grupos de meia hora ou uma hora, talvez. Outras análises, como monitorar os rendimentos de uma empresa, em geral contam com categorias até mesmo mais amplas, como meses e trimestres.
Capítulo 6 – Importando dados: Considerações adicionais
141
As tabelas dinâmicas do Excel têm um recurso muito útil que pode ajudá-lo nesse caso. Se você estabelecer um campo de hora ou data na área de linha ou de coluna de uma tabela dinâmica, poderá em seguida criar grupos baseados nesse campo, grupos definidos por horas, ou dias, ou meses e assim por diante. A Figura 6.11 mostra um exemplo. Figura 6.11 Uma tabela dinâmica baseada em valores de data ou hora raramente fornece alguma informação útil antes de você agrupar suas datas ou horários.
Na Figura 6.11, o horário em que os acidentes de trânsito foram registrados é mostrado em um intervalo de planilha começando em A1, adjacente a uma tabela dinâmica baseada nesse inter valo de dados. Os recursos de resumo de tabelas dinâmicas ainda não exercem nenhum papel — a tabela somente reproduz as informações do intervalo de dados subjacentes. Agora suponha que você quisesse ver o número de acidentes por hora. Para fazer isso, siga estes passos:
1. Clique em qualquer célula no campo de linha da tabela dinâmica e escolha Dados, Organizar Estrutura de Tópicos, Agrupar (dependendo da sua versão, você também pode clicar com o botão direito do mouse em uma célula no campo de linha e escolher Agrupar e mostrar detalhes, Agrupar ). A caixa de diálogo Agrupamento, mostrada na Figura 6.12, é exibida. Figura 6.12 Os valores inicial e final são baseados nos valores menores e maiores na fonte de dados.
142
Gerenciando dados com o Microsoft Excel
2.
Como o campo Linha que você selecionou é um campo de data/hora, a caixa de diálogo oferece por padrão agrupar o campo em meses. Clique no item Meses para desmarcá-lo e, em seguida, clique no item Horas para marcá-lo, conforme mostrado na Figura 6.12.
3.
Clique em OK para agrupar o campo de linha, conforme mostrado na Figura 6.13.
Figura 6.13 O formato de um campo agrupado em uma tabela dinâmica é baseado no formato da fonte de dados adjacentes, mas não necessariamente idêntico a ele.
Agora um padrão começa a surgir — um que estava obscurecido por todos os detalhes no campo de linha antes de você agrupá-los. Fica evidente que a maioria dos acidentes de trânsito ocorre durante os horários de pico. Exatamente como você suspeitava.
A T O N
Se estiver utilizando o Excel 97, você poderá agrupar dessa maneira somente se fizer o campo que você quer agrupar em um campo Linha — você não pode faze r isso com um campo Coluna ou Página no Exce l 97. Depois de criar os grupos, você poderá girar a tabela para transformar o campo Linha num campo Coluna ou de Página. Nas versões subseqüentes, é possível começar agrupando em um campo Coluna, mas (a partir do Excel 2003) não em um campo Página. Contudo, a lógica das dimensões da planilha indica que é pratico iniciar com um campo Linha, porque você pode acomodar muito mais valo res não agrupados dessa maneira.
Agrupando em outros campos numéricos A Figura 6.14 mostra uma situação semelhante. Você tem dados de rendimento por data de um lançamento cinematográfico de um filme que está sendo exibido nos cinemas há algumas semanas. Você não se importa com o dia específico (nem hora do dia) em que o rendimento foi alcançado, mas está interessado no valor em dólares que foi obtido semanalmente.
Capítulo 6 – Importando dados: Considerações adicionais
143
Figura 6.14 Neste caso você irá querer agrupar por data em vez de por hora.
Comece criando uma tabela dinâmica que utiliza Revenue Date como um campo de Linha e Revenue Amount como um campo de Dados. Então, selecione o campo Revenue Date, escolha Dados, Organizar Estrutura de Tópicos, Agrupar, desmarque o Mês clicando nele na caixa de listagem Por, e selecione Dias (consulte a Figura 6.12). O seletor Número de dias torna-se disponível e você o utiliza para especificar sete dias. Quando você clica em OK, a tabela dinâmica é reconfigurada para ser exibida como na Figura 6.15.
Figura 6.15 As opções de agrupamento das tabelas dinâmicas não incluem Semana — para agrupar por semana, você tem de especificar sete dias.
Esse recurso é fantástico em tabelas dinâmicas — tornando-as tão valiosas na análise de eventos dependentes do tempo. Se você utiliza o Excel 97 e baseia gráficos em tabelas dinâmicas, ou utiliza uma versão superior para criar gráficos dinâmicos, o recurso de agrupamento por hora e data é uma excelente maneira de fornecer análises visuais.
144
A C I D
Gerenciando dados com o Microsoft Excel
Você não fica restrito a apenas um nível de agrupamento. Seria possível, por exemplo,escolher Meses e Anos — e você provavelmente deve fazer isso se estiver analisando dados como rendimentos corporativos que se estendem por mais de um ano.
Evitando valores nulos Mas há um problema. As tabelas dinâmicas não conseguem agrupar em campos que contêm valores nulos. Por exemplo, suponha que os conjuntos de dados subjacentes mostrados nas Figuras 6.11 a 6.15 tinham uma célula vazia em, digamos, A207. A tabela dinâmica resultante seria exibida como aquela na Figura 6.16. Figura 6.16 Observe o valor em branco em C12:D12.
Logo que você tenta agrupar no campo com o valor em branco, o Excel exibe a mensagem de advertência Não é possível agrupar esta seleção , e você não irá visualizar a caixa de diálogo Agrupamento. O Excel não diz por que não pode agrupar essa seleção, mas a razão é que normalmente há um valor nulo em algum lugar no campo que você está tentando agrupar. Você pode ocultar esse valor em branco com as Configurações de campo, ou clicando nele com o botão direito do mouse e selecionando Ocultar no menu de atalho. Isso não ajuda — se você tentar agrupar o campo novamente, você ainda continuará a receber a mensagem de advertência em vez da caixa de diálogo Agrupamento. Você pode remover o valor nulo da lista de planilha e, então, atualizar os dados da tabela dinâmica, e, novamente, isso não terá nenhum efeito. Com um campo de data ou hora, depois de permitir a entrada de um valor nulo na tabela dinâmica, você não será capaz de agrupar por esse campo. A solução é remover esse registro da lista de planilha antes de criar a tabela dinâmica. Ou, se você baseou a tabela dinâmica em uma fonte de dados externos, poderá utilizar o painel Critérios do Microsoft Query para evitar que qualquer valor nulo entre na tabela em primeiro lugar (veja a Figura 6.17).
Capítulo 6 – Importando dados: Considerações adicionais
145
Figura 6.17 O critério que especifica É não Nulo evita que qualquer registro com um valor nulo nesse campo seja retornado à pasta de trabalho.
Portanto, se precisar agrupar valores de data ou hora em um campo da tabela dinâmica, é uma boa idéia você configurar sua fonte de dados de modo que ela não forneça nenhum valor nulo nesse campo. ➪
O procedimento de basear uma tabela dinâmica em uma fonte de dados externos é analisado em detalhes em “Importando dados para tabela dinâmica”, p. 98.
Evitando valores nulos em outros campos agrupados Há ocasiões em que você talvez queira agrupar valores diferentes de datas e horas. Suponha que você quisesse examinar as idades dos clientes, fregueses ou pacientes — em geral, as pessoas com as quais você trabalha. A faixa etária deles é exibida na Figura 6.18. Figura 6.18 O Excel não reconhece os valores na coluna A como datas ou horas.
146
Gerenciando dados com o Microsoft Excel
Quando você clica em uma das células na tabela dinâmica e escolhe Dados, Organizar Estrutura de Tópicos, Agrupar, a caixa de diálogo Agrupamento mostrada na Figura 6.19 é exibida. O Excel pode reconhecer e dividir um campo de data ou hora em categorias padrão como ano, mês, dia e minuto. Em comparação, o Excel tem que contar com o usuário para fornecer intervalos para uma escala arbitrária, como na Figura 6.19. Você sabe que os números representam anos, mas o Excel não. Figura 6.19 Você pode amontoar muitos registros em um grupo na parte superior ou inferior do intervalo alterando os valores Iniciar em ou Finalizar em.
Esse tipo de agrupamento numérico traz consigo o mesmo problema que acompanha datas e horas: um valor nulo na fonte de dados faz com o Excel indique que não pode agrupar esse campo quando você solicita. Então, é inteligente utilizar o mesmo tipo de critério, É não nulo , conforme mostrado na Figura 6.17. No entanto, há uma diferença. Com um campo como aquele mostrado na Figura 6.18, você não precisa começar a partir do zero se você permitir que um valor nulo entre na tabela dinâmica. Simplesmente remova-o da fonte de dados da tabela — seja ele uma lista de planilha ou uma fonte de dados externos — e atualize os dados da tabela dinâmica. Agora, você conseguirá agrupar nesse campo.
Utilizando critérios com o Microsoft Query A seção anterior mostrou como você pode utilizar os critérios no Microsoft Query para resolver problemas causados por valores nulos em tabelas dinâmicas. O Microsoft Query fornece critérios mais flexíveis, chamados de parâmetros , de maneira bem parecida com o Microsoft Access. Quando você indica ao Microsoft Query, ou a um gerenciador de bancos de dados como o Microsoft Access ou o SQL Server, que você quer retornar apenas determinados registros, você o faz por meio de um critério. Talvez você só queira ver registros de nascimento de crianças com peso superior a 2.000 gramas ao nascer. Então, você forneceria esse peso como um critério (veja a Figura 6.20).
Capítulo 6 – Importando dados: Considerações adicionais
147
Figura 6.20 Você pode fornecer mais de um campo de Critérios. Eles atuarão como se estivessem unidos por Es.
Como mostrado na Figura 6.20, a pessoa que está criando a consulta inseriu o valor >2000 no painel Critérios. Quando ela escolhe Arquivo, Retornar dados ao Microsoft Office Excel, somente esses registros com um peso de nascimento superior a 2.000 gramas são retornados. Se o usuário baseou um intervalo de dados externos ou uma tabela dinâmica nessa consulta, toda vez que o intervalo de dados ou a tabela for atualizada ela receberá somente os registros com um valor acima de 2.000 no campo de peso de nascimento. Mas se o usuário colocar um tipo especial de critério, um parâmetro, no painel Critérios, ele poderá alterar o valor do critério toda vez que a consulta for executada. Uma consulta parametrizada é mostrada na Figura 6.21. Figura 6.21 Observe o uso do operador de comparação > antes do parâmetro. Os operadores são freqüentemente úteis, mas não são necessários em consultas parametrizadas.
Incluindo o critério em colchetes, o usuário o estabeleceu como um parâmetro. O Microsoft Query e o Microsoft Access reconhecem os colchetes como sinal de um parâmetro. Quando a consulta é executada, o usuário é solicitado a fornecer um valor que será utilizado como um critério. Executar a consulta mostrada na Figura 6.21 faz com que a caixa de diálogo mostrada na Figura 6.22 seja exibida antes.
148
Gerenciando dados com o Microsoft Excel
Figura 6.22 A caixa de diálogo que solicita um valor é exibida novamente se você atualiza um intervalo de dados de planilha que obtém seus dados dessa consulta.
Por que isso é útil? Porque todas as vezes que a consulta é executada, ela solicita ao usuário um valor para utilizar como um critério. Isso significa que o usuário pode fornecer um valor diferente todas as vezes. Nesse exemplo, o usuário poderia especificar 2000 como um critério uma vez e a consulta retornaria somente os registros com um peso de nascimento acima de 2.000; da próxima vez, o usuário poderia especificar 2500, e a consulta retornaria somente os registros com um peso de nascimento acima de 2.500. Você pode fornecer um intervalo de critérios utilizando a palavra-chave Entre. Suponha que você queira retornar somente os registros com um valor de peso de nascimento maior ou igual a um valor e menor ou igual a um outro valor. Você pode utilizar o parâmetro Entre [Lower Weight] E [Higher Weight] . Quando a consulta é executada, ela solicita ao usuário um valor para Lower Weight e, em seguida, um para Higher Weight . Depois que o segundo critério é fornecido, a consulta retornará somente os registros que satisfazem os dois critérios. O uso de Entre…E… também funciona bem com dados de data e hora — por exemplo, Entre [Starting Date] E [Ending Date] . ➪
Você encontrará as informações sobre utilização de parâmetros com consultas por meio do VBA, em “Trazendo dados de volta de uma consulta parametrizada”, p. 282.
Consultando sites da Web Os sites da Web podem ser uma fonte rica de informações para suas pastas de trabalho — principalmente sites financeiros e os que oferecem produtos para venda. Construindo uma consulta para esse tipo de site, você pode atualizar os resultados sempre que quiser sem utilizar seu navegador.
ESTUDO DE CASO
Obtendo dados financeiros Suponha que você queira ficar de olho nos níveis e no volume dos índices dos principais mercados financeiros. Fazer isso pode ser uma boa maneira de dizer se os mercados estão começando a oscilar, para cima ou para baixo.
1. Comece selecionando uma célula de planilha que não seja parte de um intervalo de dados externos existente. Se a célula ativa for parte desse intervalo, os itens de menu Importar dados, Nova consulta da Web e Nova consulta ao banco de dados são desativados. 2. Escolha Dados, Importar dados externos, Nova consulta à Web . A caixa de diálogo mostrada na Figura 6.23 é exibida.
Capítulo 6 – Importando dados: Considerações adicionais
149
Figura 6.23 A página real que aparece depende do local da home page do seu navegador.
3. Na caixa Endereço da caixa de diálogo, digite o URL da página que você quer utilizar, e clique no botão Ir para avançar para a página. Depois que a página foi carregada, você verá caixas amarelas com setas pretas espalhadas pela página. Cada uma indica a presença de uma tabela que pode ser consultada. 4. Esses ícones quadrados por padrão estão desligados: isto é, suas tabelas associadas não estão selecionadas. Para selecionar uma tabela, clique em seu ícone. O ícone fica verde e a seta é substituída por um tique, ou marca de seleção. Para tornar a visualização daquilo que você está selecionando mais fácil, uma tabela é delineada quando você move o ponteiro do seu mouse sobre o ícone associado a ela. 5. Quando você tiver selecionado a tabela que desejar, clique no botão Importar no canto inferior direito da caixa de diálogo. A caixa de diálogo é fechada e a caixa de diálogo Importar dados é exibida. Você pode configurar propriedades de intervalo de dados nesse momento ou fazê-lo mais tarde clicando com o botão direito do mouse em uma célula do intervalo de dados e escolhendo Propriedades no menu de atalho. ➪
A caixa de diálogo Importar é analisada em “Controlando o intervalo de dados”, p. 124.
6. Clique em OK e os dados serão retornados a um intervalo de dados externos em sua pasta de trabalho. As imagens gráficas, quando houver, na tabela que você selecionou, não são retornadas (veja a Figura 6.24) .
150
Gerenciando dados com o Microsoft Excel
Figura 6.24 Essa consulta não utilizou nenhum formato de dados.
Agora, você pode atualizar as informações em seu intervalo de dados sempre que quiser. Simplesmente clique com o botão direito do mouse em uma célula no intervalo de dados e escolha Atualizar dados no menu de atalho. A C I D
É uma boa idéia utilizar o botão Propriedades na caixa de diálogo Importar dados para verificar o nome que será atribuído ao intervalo de dados. Alguns sites retornam nomes significativos. Outros retornam apenas o URL, o qual não costuma ser uma boa escolha para um nome de intervalo.
Utilizando as opções de consulta da Web Na Figura 6.23, se você clicar no botão Opções no canto superior direito da caixa de diálogo, a caixa de diálogo mostrada na Figura 6.25 será exibida. Você pode escolher Nenhuma para não obter nenhuma formatação. Se escolher Somente formatação em rich text, você obterá as fontes utilizadas na tabela: negrito, cores, itálico e assim por diante. Se escolher Formatação HTML completa, você obterá as fontes sofisticadas e se a tabela contiver qualquer hyperlink, ele se tornará parte do intervalo de dados externos. Essa pode ser uma maneira útil de obter mais informações sobre um elemento em uma tabela. Figura 6.25 Você pode redefinir essas opções clicando com o botão direito do mouse no intervalo de dados e escolhendo Editar consulta no menu de atalho.
Capítulo 6 – Importando dados: Considerações adicionais
151
As páginas da Web freqüentemente utilizam blocos para formatar seções da página. Se você selecionar a opção Importar blocos para as colunas, essas seções são retornadas à planilha em colunas que utilizam os delimitadores na página da Web. Se você desmarcar essa opção, as duas opções restantes nesta seção da caixa de diálogo são desativadas. Escolher Considerar delimitadores consecutivos como um só significa que os delimitadores consecutivos não se tornarão uma célula em branco. Se você selecionar essa opção, a opção Usar as mesmas configurações de importação para a seção inteira é ativada. Se você escolher não usar as mesmas configurações de importação, suas escolhas serão aplicadas somente ao primeiro bloco pré-formatado encontrado pela consulta. A consulta tentará selecionar as melhores escolhas para qualquer bloco pré-formatado restante. Talvez você encontre uma página da Web que contém números que se parecem com datas, mas que, na realidade, são algo mais. Por exemplo, uma página com números de produtos talvez utilize um padrão como 11-5-98. O Excel em geral reconheceria isso como uma data e o converteria em representação padrão de número serial de data. Você pode evitar que isso aconteça escolhendo a opção Desativar reconhecimento de data. Por último, algumas páginas da Web têm redirecionamento automático para outros sites. Ao atualizar a consulta mais tarde, você talvez seja redirecionado e pode ser que seus dados não sejam atualizados adequadamente. Para evitar que isso aconteça, escolha a opção Desativar redirecionamentos de consulta à Web.
Olhando para frente As consultas são ferramentas poderosas para uso na importação de dados para uma pasta de trabalho do Excel, e se tornam mais poderosas ainda quando você aplica parâmetros a elas. As técnicas discutidas no Capítulo 5, “Utilizando o Microsoft Query”, e neste capítulo são boas para trabalho interativo — quando você for fazer algo somente uma vez, ou quando estiver no início do processo de desenvolvimento de algo que você espera crescer. Essas técnicas ainda são utilizadas, e com bastante freqüência, em situações mais complexas. A complexidade maior de uma solução freqüentemente depende do uso de codificação e, nas aplicações do Office, isso normalmente começa com o VBA. Ela começa desse ponto até o uso de bibliotecas de objetos que permite ao VBA criar e executar consultas do tipo discutido nos Capítulos 5 e 6. Para chegar nesse ponto, você precisa conhecer algo sobre o VBA. No próximo capítulo, este livro se afasta brevemente de seu tema principal de gerenciamento de dados para familiarizá-lo com, ou talvez lembrá-lo de, alguns aspectos fundamentais do VBA necessários para tornar automático um relacionamento da pasta de trabalho com as fontes de dados externos.
Gerenciando bancos de dados de dentro do Excel
V I E T R A P
7
Revisão dos princípios básicos do VBA .......................................................................................................................... 15 4
8
Abrindo bancos de dados ............................................................................................................................................. 185
9
Gerenciando objetos de banco de dados ...................................................................................................................... 218
10
Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects ......................................................... 24 7
11
Obtendo dados do Access para o Excel com o ADO e o DAO........................................................................................... 27 8
12
Controlando um banco de dados a partir do Excel com o ADO e DAO............................................................................ 31 2
7 Revisão dos princípios básicos do VBA Utilizando o VBA para gerenciar dados Supostamente, este livro deveria tratar de gerenciamento de dados utilizando o Excel, então, o que um capítulo sobre o Visual Basic for Applications (VBA) está fazendo aqui? Como acontece, o restante deste livro é inteiramente dedicado ao VBA. Os seis primeiros capítulos estavam envolvidos com o uso de capacidades predefinidas de gerenciamento de dados do Excel. Por exemplo • As funções de planilha que ajudam a gerenciar dados. • As ferramentas que ajudam a filtrar e classificar dados. • As maneiras de preencher tabelas dinâmicas e intervalos de dados. • Os métodos de importação de dados de fontes como bancos de dados, arquivos de texto e a Web. Essas são técnicas poderosas e têm ampla aplicabilidade. Para situações padrão, elas são muito úteis. Você terá de fazer alguns ajustes aqui e ali para obter os resultados exatamente da maneira desejada, mas em muitos casos o uso dessas técnicas é tudo o que é necessário. Essas situações padrão incluem gerenciar dados que já estão em uma pasta de trabalho do Excel e trazer dados de outras fontes em uma pasta de trabalho onde você tira proveito de funções de planilha e das tabelas dinâmicas do Excel e suas capacidades gráficas.
Adicionando objetos ao VBA Até aqui, porém, este livro não discutiu mover dados de outra direção: em particular, movê-los do Excel para um banco de dados. Para isso você realmente precisa do VBA. Você também precisa de um conjunto de ferramentas como as fornecidas por ActiveX Data Objects (ADO) ou de seu predecessor, Data Access Objects (DAO). Esses conjuntos de ferramentas são também ambos bem adaptados e necessários para outras tarefas como manipulação de um banco de dados diretamente da plataforma Excel. Você verá muito mais em ADO e DAO nos capítulos 8 a 12, mas eis uma breve visão geral. Tanto ADO quanto DAO são bibliotecas de objeto — isto é, coleções de objetos que você pode utilizar no código VBA. Você utiliza ADO ou DAO, não ambos simultaneamente. Os objetos que
Capítulo 7 – Revisão dos princípios básicos do VBA
155
estão nas bibliotecas são objetos de banco de dados. Eles definem para o VBA os objetos que você encontra em bancos de dados: tabelas, consultas, registros, campos e assim por diante. Utilizando uma dessas bibliotecas, ADO ou DAO, disponível para seu código VBA, você torna possível manipular os dados armazenados em um banco de dados assim como utilizar o VBA para manipular os dados armazenados em uma pasta de trabalho do Excel. ADO e DAO diferem entre si de várias maneiras e essas diferenças também são discutidas em capítulos posteriores. Por enquanto é suficiente saber que ADO é mais novo do que DAO, que você pode utilizá-lo com um intervalo mais amplo de bancos de dados do que DAO e que muitos desenvolvedores o consideram mais fácil de utilizar do que DAO. Como verá, tanto ADO quanto DAO são bem projetados para mover dados do Excel para um banco de dados, mas vão bem além desse papel. São também valiosos para mover dados de um banco de dados para o Excel, em situações em que as técnicas discutidas nos capítulos precedentes não fazem o que você precisa. E você vai precisar de um ou de outro quando quiser modificar registros de banco de dados enquanto estiver trabalhando no Excel. Se estiver utilizando ADO ou DAO, você precisa utilizá-lo no contexto do VBA. Nenhum se comporta como um suplemento como o Solver, que é instalado como uma opção e então pode ser chamado a partir da planilha. ADO e DAO são ambos coleções de objetos, métodos e propriedades que estendem o alcance do VBA. É importante ter em mente que você precisa utilizar o VBA para tirar proveito do modelo de objeto de banco de dados como fornecido pelo ADO ou DAO. O VBA é uma parte necessária dessa interface entre Excel e bancos de dados, e você precisa de um conhecimento funcional pelo menos dos princípios básicos do VBA para gerenciar a interface. Isso é o que este capítulo está fazendo aqui.
Colocando o VBA em perspectiva histórica Em 1995, a Microsoft deu um passo que aumentou significativamente o poder das aplicações no conjunto Office: ela expôs o modelo de objeto . Isso significava que a Microsoft permitiu que usuários do Excel e outras aplicações do Office escrevessem programas em BASIC que poderiam diretamente manipular alguns objetos da aplicação. Antes do VBA e da exposição do modelo de objeto, um usuário do Excel que quisesse automatizar certas tarefas estava severamente limitado pela linguagem de programação disponí vel. Havia uma linguagem de macros misteriosa que você poderia utilizar no Excel, mas para utilizá-la não era nada como a codificação com linguagens de programação populares da época como C, BASIC e Fortran. Você colocava seu código de macro em uma planilha muito a uma pasta de trabalho e colocava uma instrução ou função em uma célula e seus argumentos em uma célula adjacente. Mas o VBA e o modelo de objeto tornaram as coisas muito mais simples e diretas. Repentinamente seu código parecia normal: outros programadores podiam compreendê-lo e mantê-lo. Seu código poderia manipular diretamente objetos do Excel como planilhas, intervalos, nomes e gráficos. Ele podia trazer métodos para suportar objetos — por exemplo, seu código poderia invocar um método Clear do intervalo para limpar esse intervalo de seu conteúdo. Ele podia manipular propriedades, como configurar a propriedade Color da fonte como Vermelho.
156
Gerenciando dados com o Microsoft Excel
Isso é o que significava expor o modelo de objeto. Finalmente você tinha acesso à hierarquia de objetos: a aplicação Excel por cima, à qual pertenciam as pastas de trabalho, às quais pertenciam as planilhas de gráfico e planilhas, às quais pertenciam os eixos, série de dados, células, intervalos, colunas, linhas e assim por diante. Cada um desses é um objeto.
Utilizando o modelo de objeto Os objetos têm métodos . Um intervalo de planilha, por exemplo, tem métodos como AutoFilter, Clear, Copy, Delete, Select e Sort (há muitos outros). Invocando um método Copy do intervalo, posso colocar seu conteúdo na área de transferência, pronto para colar em outra parte. Os objetos têm propriedades . Um intervalo de planilha, por exemplo, tem propriedades como Borders, Columns, Formula, Offset e RowHeight (há muitas outras). Configurando uma propriedade Redimensionar do intervalo, posso trabalhar com um intervalo mais alto ou mais baixo, mais largo ou mais estreito. Você pode obter todos os objetos do Excel e seus métodos e propriedades associados, utilizando o VBA no Excel. O Visual Basic Editor (VBE) do Excel automaticamente os torna disponíveis para seu código porque o modelo de objeto foi exposto. Os bancos de dados também têm objetos, bem como métodos e propriedades que pertencem aos objetos. Os objetos dos bancos de dados incluem tabelas, consultas, parâmetros e assim por diante. Mas eles não pertencem ao modelo de objeto do Excel: não há só um modelo de objeto principal que contém, por exemplo, planilhas do Excel, tabelas do Access, slides do PowerPoint e parágrafos do Word. Há um modelo de objeto diferente para cada aplicação.
Utilizando modelos de objeto de outras aplicações Esses outros modelos de objeto não estão automaticamente disponíveis para o VBA no Excel. Se iniciar o VBE a partir do, digamos, Access, você descobrirá que todos os objetos de banco de dados do Access estão automaticamente disponíveis para você. Mas eles não estão disponíveis se você iniciar o VBE a partir do Excel. Para utilizar um objeto de banco de dados em seu código VBA do Excel, você precisa estabelecer uma referência a um modelo de objeto compatível com o banco de dados. Estabelecendo a referência, você permite que o VBA saiba onde procurar as informações sobre os objetos que utiliza. Há vários desses modelos de objeto e cada um é armazenado em uma biblioteca — em particular, em uma biblioteca de vínculo dinâmico ou DLL ( dynamic link library). Você estabelece a referência por meio de um simples comando de menu. No VBE, escolha Ferramentas, Referências e marque a caixa de seleção para a biblioteca que você quer (veja a Figura 7.1).
A T O N
A versão do DAO ou ADO disponível depende da versão do Offic e (e portanto do sistema operacional) instalada no computador. Essas versões têm o mesmo propósito: dar a seu código acesso a objetos de banco de dados. Da perspectiva do usuário, DAO e ADO são idênticos de alguma forma, mas muito diferentes em aspectos importantes. Na época em que este livro estava sendo escrito, a Microsoft estava focalizando esforços de desenvolvimento no ADO e considerava o DAO como uma tecnologia de legado. Este livro destaca as diferenças entre os dois onde seu código VBA seria afetado. Em outra parte, você pode considerar que a discussão sobre o ADO também se aplica ao DAO.
Capítulo 7 – Revisão dos princípios básicos do VBA
157
Figura 7.1 Quando tiver uma escolha de versões, normalmente é melhor escolher aquela com o número de versão mais alto.
Depois de estabelecer a referência, você achará que seu código VBA reconhece os objetos de banco de dados.
A C I D
Ao compilar ou executar seu código VBA, se obtiver a mensagem de erro de compilador Tipo definido pelo usuário não definido, quase certamente significa que você digitou errado o nome de um tipo de variável ou que você ainda não estabeleceu uma referência à biblioteca do tipo.
Por exemplo, com uma referência de biblioteca estabelecida adequadamente, seu código VBA pode fazer cada um dos seguintes itens, todos sem jamais abrir a janela do banco de dados: • Retornar registros e campos de uma tabela ou consulta diretamente para a planilha. • Filtrar os registros retornados por uma consulta fornecendo um valor de parâmetro à consulta antes de executá-la. • Adicionar registros a ou excluir registros de uma tabela. • Editar registros. • Criar e executar uma consulta e fazer com que seja salva no banco de dados ou desapareça logo que terminar com ela A lista anterior é só um pequeno exemplo das tarefas que você pode realizar ao tornar o modelo de objeto de banco de dados disponível para seu código VBA do Excel.
Gerenciando o banco de dados a partir do Excel Por que você poderia querer manipular um banco de dados de dentro do Excel? Como se revela, há uma variedade de razões, nem todas patentes.
158
Gerenciando dados com o Microsoft Excel
Acomodando o usuário
Se outras pessoas além de você utilizam sistemas que você desenvolve, é preciso levar em conta as interfaces com usuário confortáveis. A maioria dos usuários de computador sente-se à vontade em um contexto de planilha. Eles estão acostumados à noção de linhas e colunas e como elas se interseccionam para formar células. Suas experiências com planilhas talvez se voltem completamente para Lotus 1-2-3, enquanto os gerenciadores de bancos de dados, tal como dBASE, vieram mais tarde (e nunca foram tão populares quanto as aplicações de planilha). A maioria dos usuários aprecia a flexibilidade da planilha — por exemplo, esta pode fazer uma linha significar qualquer coisa e uma coluna significar qualquer coisa — e freqüentemente são dissuadidos pela ardilosidade do banco de dados onde uma linha deve representar um registro e uma coluna, um campo. A qualquer momento você pode ajudar um usuário a sentir-se mais confortável utilizando seu produto, você está à frente do jogo. Tirando proveito dos recursos do Excel
O Excel oferece capacidades que simplesmente não estão disponíveis em bancos de dados, ou que não estão tão bem implementadas como no Excel. Os gráficos, por exemplo, estão implementados no Access, mas têm de ser incorporados em formulários ou relatórios. As tabelas dinâmicas estão disponíveis de maneira semelhante, mas somente em folhas de dados e formulários. Mais sério é o conjunto limitado de funções. Embora o Access ofereça uma lista razoavelmente boa de funções, uma vez que é um gerenciador de bancos de dados, a lista está longe de ser tão extensa quanto a do Excel. Uma função tão básica quanto Média não está disponível no Access — você mesmo tem de criá-la utilizando VBA em Access. Mas se tornar registros de banco de dados e campos disponíveis para seu código VBA do Excel, você pode utilizar funções de planilha do Excel para analisar os dados.
A C I D
Quer ou não você alguma vez utilize o VBA do Excel em conjunção direta com um banco de dados, lembre-se de que funções de planilha do Excel estão disponíveis no VBA. Por exemplo, para obter o valor médio de uma matriz, você talvez poderia utilizar algo como : MyMedian = Application.WorksheetFunction.Median(MyArray)
Mantendo a flexibilidade
Anteriormente, neste capítulo, foi mencionada a natureza generalizada do layout de planilha do Excel: que linhas e colunas não são relegadas a qualquer papel específico como são na visualização de folha de dados de um banco de dados. Esse fato tem aplicabilidade que vai além do conforto de usuário. Às vezes você encontra uma situação que indica uma matriz ou uma tabela ou um intervalo de células; o termo importa menos que o fato de você precisar utilizar linhas que interseccionam colunas. E a situação é tal que você não pode considerar as linhas como registros e as colunas como campos, como exigido pelo banco de dados.
Capítulo 7 – Revisão dos princípios básicos do VBA
159
Um exemplo é um calendário de recursos. Para seus usuários empregarem o calendário efetivamente, você gostaria que eles fossem capazes de reservar um recurso durante um período particular de tempo em um dia em particular. A estrutura do Excel é ideal para isso. Você pode organizar as coisas da seguinte maneira: • Atribua cada linha da planilha para representar um recurso específico (uma sala de reunião, por exemplo ou um projetor compatível com estação de trabalho ou uma tela de alta definição). • Atribua cada coluna da planilha para representar um incremento de meia hora, de modo que a coluna C possa representar 0600 a 0630, a coluna D, 0630 a 0700, e assim por diante. • Atribua cada planilha para representar um dia diferente. Com esse tipo de estrutura, um usuário rapidamente pode ver que a Sala K já foi reservada para uma reunião que começa às 10:30 em 14 de outubro, de modo que sua reunião terá de utilizar a Sala O. Veja a Figura 7.2. Esse é o tipo de coisa para o qual as planilhas do Excel são tão apropriadas. O olho ocidental gosta de ver a hora progredir da esquerda para a direita, não para cima e para baixo (e definitivamente não da direita para a esquerda), então slots de hora são melhor colocados de modo que ocupem colunas diferentes. Para a conveniência do usuário, faz sentido colocar recursos disponíveis adjacentes a outros, de modo que se a Sala K estiver indisponível em um horário em particular, é fácil de ver se alguma outra sala está disponível. Se colunas diferentes estiverem em utilização para representar slots diferentes de hora, isso deixa linhas diferentes para representar recursos diferentes. Mas é difícil ver como você gerenciaria isso em um banco de dados. Você precisaria primeiro definir um campo como um slot de hora. Assim, cada registro teria talvez 48 campos que representam se ele está em utilização durante uma meia hora em particular. Cada registro teria de representar um recurso em particular em um dia em particular. E cada registro precisaria de registros-filhos para armazenar dados como quem fez a reserva, quais contas devem ser cobradas para quaisquer despesas incorridas, quais bebidas devem ser fornecidas, se a reserva é para ocorrer em datas subseqüentes e assim por diante. Figura 7.2 Não há nenhuma camisa de força “linha como registro” e “coluna como campo” nessa estrutura.
160
Gerenciando dados com o Microsoft Excel
Considerando tudo, a interface com o usuário é difícil de tratar com um banco de dados só. Mas é fácil de fazer utilizando uma pasta de trabalho. E fica mais fácil ainda quando você põe um banco de dados em ação no problema, mas de tal maneira que sua utilização seja transparente ao usuário. Por exemplo, você poderia decidir que no banco de dados uma reserva é um registro. Ele tem um campo que identifica a data da reserva, outro para identificar a hora inicial e outro para a hora final, outro para o recurso que está sendo reservado etc. Isso significa que você pode utilizar a pasta de trabalho para exibir as informações e o banco de dados para armazenar as informações. Essa abordagem tira proveito das capacidades de ambas as aplicações: a capacidade da planilha de representar as informações de uma maneira visualmente informativa e intuitiva, e a capacidade do banco de dados de armazenar, localizar e recuperar grandes quantidades de dados muito rapidamente. Observe que embora o cenário como descrito aqui envolva apenas um fluxo de uma via de dados, você não poderia realizá-lo importando dados externos, seja para um intervalo de dados externo ou para uma tabela dinâmica: os layouts que eles utilizam são muito restritivos. (Isto é, a propósito, uma aplicação do mundo real que foi utilizada diariamente por uma corporação de médio porte durante vários anos.)
Revisando a lógica Realmente, o ponto principal levantado na seção anterior — utilizar a força de cada aplicação — é o raciocínio para utilizar o VBA do Excel em conjunto com um modelo de objeto de banco de dados como ADO ou DAO. O Excel foi construído e refinado durante vários anos e várias versões principalmente como uma aplicação que analisa, sintetiza e exibe dados, e é de fato muito bom nessas tarefas. Em resposta a solicitações de usuário, a Microsoft adicionou certas capacidades ao Excel que não faziam parte do design original. Por exemplo, o Excel não foi originalmente planejado como uma aplicação multiusuário com todos os recursos necessários para assegurar a integridade de dados quando vários usuários têm uma determinada pasta de trabalho aberta ao mesmo tempo. Você pode organizar a pasta de trabalho para ser compartilhada, mas esse recurso veio relativamente tarde no desenvolvimento do Excel e não funciona tão bem quanto em aplicações que têm esse recurso embutido em seu projeto básico. De maneira semelhante, bancos de dados como o Access foram desenvolvidos como mecanismos que armazenam grandes quantidades de dados confiavelmente e recuperam registros muito rapidamente, mesmo de conjuntos de dados bem grandes. Eles também têm sofisticados métodos de resolver conflitos quando mais de um usuário por vez estiver tentando modificar um registro em particular. Se você tiver relativamente poucos registros para lidar, estará normalmente em melhor situação utilizando funções e recursos nativos da planilha do Excel para gerenciá-los, como discutido no Capítulo 2, “Recursos de gerenciamento de dados do Excel”, e no Capítulo 3, “Listas, nomes e filtros do Excel”. E se você tiver um conjunto de dados relativamente grande, você pode estar em melhor situação utilizando o gerenciador de bancos de dados isoladamente.
Capítulo 7 – Revisão dos princípios básicos do VBA
161
Mas se você tiver um conjunto de dados grande e precisar de uma interface com o usuário flexível ou um conjunto grande de funções ou uma capacidade gráfica forte, você precisa tanto de um banco de dados quanto de uma pasta de trabalho. E a melhor forma de gerenciar a comunicação de duas vias entre eles é por meio de VBA quando aprimorado com ADO. Com o raciocínio precedente, você deve se dirigir ao próximo capítulo se já for um expert em VBA. Caso contrário, o restante deste capítulo fornece uma introdução rápida aos aspectos do VBA mais freqüentemente utilizados nos contextos de gerenciamento de dados e gerenciamento de banco de dados.
Estabelecendo sub-rotinas Uma sub-rotina é uma coleção de instruções do VBA que, juntas, realizam algo. O termo é um pouco infeliz, implicando que está subordinado a algo. Mas o significado é mais amplo que isso: uma subrotina pode ser o código de primeiro nível e principal em seu projeto ou pode ser um conjunto de código que realiza alguma tarefa menor em favor do código principal.
Fornecendo os elementos básicos da sub-rotina Em VBA, as sub-rotinas são armazenadas em módulos e estabelecer um módulo é onde tudo começa. Para estabelecer um módulo você precisa iniciar o VBE. A partir da janela principal do Excel, uma maneira de chegar ao VBE é escolhendo Macro do menu Ferramentas e depois selecionando Editor do Visual Basic no menu de atalho. À medida que tornar-se mais experiente com o VBE, você se encontrará utilizando as teclas de atalho do teclado (Alt+F11) tão consistentemente, que estará sujeito a se esquecer de que há outra maneira de chegar lá. Ao iniciar o VBE, a janela mostrada na Figura 7.3 aparece. Figura 7.3 O VBE tem uma aparência muito semelhante em outras aplicações do Office como Word e Access.
162
Gerenciando dados com o Microsoft Excel
As duas janelas à esquerda na Figura 7.3 não são necessárias se você estiver estabelecendo uma sub-rotina, mas podem ser úteis. O VBE refere-se à coleção inteira de planilhas, módulos e formulários de usuário em um arquivo Excel como um projeto. Quando tiver vários módulos em um projeto, o Explorer de Projeto é a maneira mais conveniente de alternar entre eles. A janela Propriedades não é tão útil rotineiramente como o Explorer de Projeto e talvez você decida fechá-la só para tirá-la de seu caminho. Gosto de tê-la aí porque eu freqüentemente oculto planilhas em minhas pastas de trabalho. A janela Propriedades é uma maneira conveniente de ocultar e reexibir planilhas, alterando sua propriedade Visible.
A C I D
Você pode configurar a propriedade Visible da planilha como xlSheetVisible: esse é seu estado normal. Ou pode-se configurá-la como xlSheetHidden: isso é o mesmo que escolher Formatar, Planilha, Ocultar. Há pelo menos dois problemas com uma planilha oculta. A menos que a pasta de trabalho esteja protegida, o usuário pode dizer que a planilha existe porque o item de menu Reexibir está ativado e o usuário pode reexibir a planilha. Utilizando a janela Propriedades (ou código VBA) para configurar a propriedade Visible como xlSheetVeryHidden, resolve ambos os problemas. Mesmo se a própria pasta de trabalho não estiver protegida, o i tem de menu Reexibir permanece desativado — e, portanto,o usuário não pode reexibir a partir do menu Formatar nem deduzir que uma planilha oculta existe.
Depois de abrir o VBE, há várias maneiras de estabelecer módulos, dependendo do que você se dispõe a realizar. O método mais simples e direto é escolher Módulo no menu Inseri r. A Figura 7.4 mostra o resultado. Figura 7.4 O objeto ativo é aquele cujas propriedades aparecem na janela Propriedades. Módulos padrão têm somente uma propriedade.
Capítulo 7 – Revisão dos princípios básicos do VBA
A C I D
163
Observe a instrução Option Explicit no início do módulo na Figura 7.4. É boa prática de programação utilizar essa instrução porque ela o força a declarar explicitamente todas as variáveis em seu código (a importância da declaração explícita é discutida na próxima seção). Em caso de você se esquecer de utilizá-la, você pode escolher Opções do menu Ferramentas do VBE, clicar na guia Editor e certificar-se de que a caixa de seleção Requerer declaração de variável está marcada. Então todos os novos módulos automaticamente incluem a instrução Option Explicit .
Agora, tudo que é necessário é digitar uma instrução como a seguinte em algum lugar embai xo da instrução Option Explicit : Sub MoveListToDatabase
A string MoveListToDatabase torna-se o nome da sub-rotina, por ser a primeira string de caracteres seguida da palavra-chave Sub. Ao terminar de digitar o nome da sub-rotina, pressione Enter. Ao fazer isso, duas coisas acontecem imediatamente: • Um par vazio de parênteses automaticamente é fornecido seguido do nome da sub-rotina (a menos que você mesmo tenha digitado os parênteses). • Uma instrução End Sub é fornecida, com uma linha vazia entre a Sub e a End Sub onde você pode começar a introduzir seu código. Ambas já aconteceram na Figura 7.5. Nesse ponto, você estabeleceu uma sub-rotina: forneceu uma instrução de início ( Sub ) e uma instrução de término ( End Sub) e há um par de parênteses no final da instrução Sub (veja a Figura 7.5). Você agora poderia voltar para a janela da pasta de trabalho, escolher Ferramentas, Macro, Macros, selecionar a macro MoveListToDatabase e clicar no botão Executar. A sub-rotina poderia executar, embora não fizesse nada porque você ainda não havia fornecido a ela quaisquer instruções — as instruções que indicam onde localizar dados e o que fazer com eles. Figura 7.5 Os elementos básicos da subrotina estão agora completos.
164
Gerenciando dados com o Microsoft Excel
Declarando variáveis A seção anterior aconselhou-o a utilizar Option Explicit e a utilizar a opção Requerer declaração de variável para fornecer essa instrução automaticamente. O seguinte parágrafo fornece a razão. Nos tempos ruins de antigamente, — aproximadamente entre 1950 e 1980 — a codificação seguia uma abordagem “faça como preferir” (codificação é só outro termo para programação). Quando precisava de uma nova variável, tudo que você tinha a fazer era utilizá-la. Por exemplo, isto teria sido não apenas sintaticamente válido, mas parte da prática estabelecida até mesmo se o programa nunca tivesse mencionado a existência de uma variável NumberOfRows antes dessa instrução: NumberOfRows = Rows(MyMatrix)
Esse tipo de coisa resultava em um código que era muito difícil de entender e documentar, muito menos de solucionar problemas. Especialmente em um programa longo, era inteiramente possível que o codificador se esquecesse de que já tinha utilizado uma variável NumberOfRows para armazenar o número de linhas no intervalo chamado MyMatrix. Então talvez agora criasse outra variável para transportar as mesmas informações, apenas utilizando a nova no processo. Imagine a dificuldade enfrentada pelos programadores meses ou até anos mais tarde, tentando lembrar-se do que tinham em mente com duas variáveis para um único propósito. Pior ainda, o infeliz codificador poderia se lembrar de que já estava utilizando uma variável nomeada NumberOfRows, mas tinha digitado seu nome errado ao utilizá-la mais tarde. Suponha que ele quisesse começar criando um intervalo colocando o valor de NumberOfRows em uma variável nomeada NumberOfColumns: NumberOfColumns = NumberOfRow
Mas veja o que ele fez: ele digitou errado NumberOfRows deixando de fora o final s . Fazendo isso, ele criou uma nova variável — lembre-se, elas só podem ser criadas utilizando-as. E uma nova variável teria o valor especial de Empty, indicando que a variável está como ainda não inicializada. Portanto, a variável NumberOfColumns, em vez de aceitar como seu valor o número de linhas em MyMatrix, aceita o valor 0 de NumberOfRow.
A T O N
Uma variável declarada dessa maneira (assim, na ausência de Option Explicit) utilizando VBA seria de um tipo especial, Variant. As variáveis Variant iniciam a vida com um valor especial: Empty. Variáveis numéricas iniciam como zero e variáveis string iniciam como strings de comprimento zero (“”).
As coisas não vão funcionar como o programador tinha antecipado. Vai ser difícil para ele entender o que está errado porque estará procurando problemas com NumberOfColumns quando a fonte do problema é NumberOfRow. Uma boa maneira de se proteger contra esse tipo de bobagem é requerer declaração de variável. Isso significa que antes de poder utilizar uma variável, você tem de declará-la. No VBA, você faz isso com uma instrução Dim como esta, que declara duas variáveis: Dim NumberOfRows As Integer, NumberOfColumns As Integer
Capítulo 7 – Revisão dos princípios básicos do VBA
165
, como Sub, é um pouco enganosa. É a abreviação de dimensão, que tradicionalmente significava especificar a estrutura de uma matriz de memória. Na sintaxe do VBA, quando você dim uma variável, você declara ao VBE que a variável existe, e em geral, declara também que tipos de valores a variável pode assumir. Neste exemplo, as variáveis NumberOfRows e NumberOfColumns podem assumir somente valores inteiros: nenhum deles pode ser igual a 5,3 ou 5/4/2009 ou “Fred”. Nesse exemplo, então, o efeito de Option Explicit é impedir você de utilizar uma variável chamada NumberOfRow (novamente, observe o s final ausente) quando ainda não tiver declarado essa variável. Assim, se digitar errado o nome da variável, você não terá criado automaticamente uma nova variável. O VBE observará que você está tentando utilizar uma variável não-declarada e o advertirá com uma mensagem de erro. O efeito de declarar suas variáveis em uma ou mais instruções Dim é que você pode olhar para trás para ver quais variáveis você declarou. É muito mais fácil localizá-las em instruções Dim do que varrer um programa inteiro procurando as primeiras instâncias de uma variável. Isso o ajuda a evitar declarar múltiplas variáveis que realizam o mesmo propósito. Dim
A T O N
Também é considerada boa prática de programação declarar suas variáveis no começo de uma sub-rotina ou no começo de um módulo e não diferir quaisquer declarações até mais adiante no código. Mas é válido fazer isso.
Se você não utilizar Option Explicit, o VBE não se queixará quando você subitamente referenciar uma nova variável — quer você queira ou não. Ele o deixará utilizar essa nova variável e, a propósito, dará à variável o tipo mais flexível (e maior consumidor de recursos): Variant. Uma variável cujo tipo é Variant pode assumir quase qualquer tipo de valor: inteiros, números decimais, strings, datas e assim por diante. O tipo Variant é o padrão em uma instrução Dim como esta: Dim NumberOfRows, NumberOfColumns
Se você não especificar o tipo de uma variável, ela assume Variant por padrão. Isso é melhor que não declarar absolutamente nenhuma variável na ausência de Option Explicit, mas é muito melhor declarar o tipo de uma variável. Entre outras razões, você se protege contra atribuir, digamos, “Fred” a uma variável declarada como Integer.
A T O N
É na instrução Dim que você declarará variáveis que representam objetos de banco de dados. Você primeiro deve estabelecer uma referência à biblioteca de objeto, como discutido anteriormente neste capítulo. Mas depois que essa referência tiver sido estabelecida, você pode utilizar instruções como estas: Dim dbMyDatabase As Database Dim tdfDoors As TableDef Dim qdfDoors As QueryDef
166
Gerenciando dados com o Microsoft Excel
A C I D
Para iniciar o nome de uma variável com um indício do que representa é uma questão de estilo pessoal, mas não deve causar danos e às vezes pode ajudar.No exemplo da nota anterior, a variávelqdfDoors é declarada uma definição de consulta (QueryDef). A string de três letras qdf pode ajudar você ou outra pessoa a se lembrar, mais tarde no código, de que qdfDoors representa uma definição de consulta.
Entendendo a notação de ponto A primeira seção deste capítulo mencionou que o modelo de objeto do Excel é uma hierarquia. Por exemplo, um objeto próximo à parte superior da hierarquia é a pasta de trabalho. As pastas de trabalho têm planilhas que as pertencem. Por sua vez, as planilhas têm linhas e colunas e células. (As planilhas também têm muitos outros objetos. Este livro focaliza objetos importantes para o gerenciamento de dados e, o objeto Comment, por exemplo, não é crucial para o gerenciamento de dados.) No curso de mover dados para um banco de dados a partir de uma planilha, ou para uma planilha a partir de um banco de dados, você sempre precisa prestar atenção à coluna, linha ou célula em particular da qual você quer obter os dados ou para a qual você quer escrevê-los. E esse requisito implica que você precisa especificar a planilha em que a coluna, linha ou célula é encontrada. Além disso, se mais de uma pasta de trabalho estiver aberta quando seu código executa, você precisa especificar a pasta de trabalho em que você está interessado. O VBA trata tudo isso por meio de itens e notação de ponto: • Quando estiver lidando com uma coleção de objetos, como uma coleção de planilhas em uma pasta de trabalho, você identifica um objeto em particular como um item. Por exemplo, Worksheets("Sheet1")
referencia a planilha chamada Sheet1. Você também pode utilizar o número de um objeto em vez de seu nome: Worksheets(1)
mas isso pode ser difícil, a menos que esteja absolutamente certo de que sabe que planilha o número 1 referencia; costuma ser Sheet1 mas pode facilmente ser alguma outra planilha. Em geral você especifica um objeto por meio de seu nome. Às vezes você o especifica por seu número, especialmente no contexto de loops (veja “Utilizando loops”, mais adiante neste capítulo). • Você referencia um objeto que pertence a outro objeto conectando-os com pontos. Eis um exemplo: Worksheets("Sheet2").ChartObjects("Chart 1")
Esta instrução utiliza notação de ponto para referenciar o gráfico incorporado chamado Chart 1 na planilha chamada Sheet2: observe o ponto entre o primeiro parêntese fechado e a palavra-chave ChartObjects. Se não especificasse onde procurar Chart 1, em Sheet2, você receberia uma mensagem de erro. Em geral, tudo que vem depois de um ponto pertence ao que vem antes.
Capítulo 7 – Revisão dos princípios básicos do VBA
167
No modelo de objeto, os objetos “têm” outros objetos: uma pasta de trabalho tem planilhas, uma planilha tem células, células têm bordas. Mas objetos também têm propriedades e métodos. Em seu código, você também utiliza a notação de ponto para conectar propriedades e métodos aos objetos que os têm. Por exemplo, você talvez queira ativar Chart 1 em Sheet2. Esta instrução faria isso: Worksheets("Sheet2").ChartObjects("Chart 1").Activate
A instrução diz para ativar algo. Uma grande quantidade de coisas pode ser ativada: pastas de trabalho, planilhas, gráficos e assim por diante. Essa instrução refere-se ao método Activate quando ele aplica ao objeto de gráfico chamado Chart 1 que está localizado na planilha chamada Sheet2. Depois que seu código ativou o gráfico, você talvez queira que ele mude o tipo do gráfico a partir de tudo com que iniciou — talvez um gráfico de colunas — para um gráfico de linha com marcadores. Você poderia utilizar esta instrução para fazer isso: ActiveChart.ChartType = xlLineMarkers
Aqui, você começa referenciando o gráfico ativo (cabe a você certificar-se de que há um gráfico ativo no ponto em que o VBA executa a instrução). Os gráficos têm propriedades e uma dessas propriedades é o tipo do gráfico: coluna, barra, linha, XY (Dispersão) e assim por diante. Portanto, a instrução determina o tipo do gráfico atribuindo um valor em particular, xlLineMarkers, à propriedade ChartType. Considerar métodos e propriedades em termos concretos pode ser útil. Um exemplo comum talvez seja um carro. Você poderia considerar que um carro tem vários métodos e um deles é o método VirarParaEsquerda. Você executaria esse método para fazer o carro virar para a esquerda. Você poderia considerar que um carro tem várias propriedades e uma delas é a propriedade Cor. E poderia configurar sua propriedade Cor como Vermelho, Azul, Marrom — qualquer que seja a cor desejada e que esteja disponível. Se for novo em VBA, a distinção entre um método e uma propriedade talvez pareça um pouco obscura. Não se preocupe com isso: com tempo e experiência, isso se torna mais claro.
A C I D
Uma ótima maneira de se familiarizar com o VBA em geral e notação de ponto em particular é utilizar o programa de gravação de macros. Escolha Ferramentas, Macro, Gravar nova macro e clique em OK. Então faça algo relevante para o Excel: abra uma pasta de trabalho ou exclua uma linha ou digite um valor ou insira um nome. Então clique no botão Parar gravação, alterne para o VBE e examine o código que foi registrado para você. Você verá uma maneira como o VBA automatizaria tudo que você fez manualmente. Veja “Entendendo o código do programa de gravação de macros”, mais adiante neste capítulo.
168
Gerenciando dados com o Microsoft Excel
Formalmente, sua referência a uma coleção de objetos é a uma propriedade. Então, nesta instrução Worksheets("Sheet1").Rows(2) A T O N
a referência a Rows é a uma propriedade da planilha: a coleção das 65.536 linhas nessa planilha e uma coleção não é um objeto. Mas logo que especificar a qual linha está se referindo (aqui, isto é 2), você faz referência a um objeto. Essa é a razão por que, ao examinar o modelo de objeto na documentação de Ajuda do VBA, você vê que Rows é uma propriedade do objeto Worksheet.
Utilizando loops Costuma acontecer no código VBA — particularmente o código planejado para adicionar, editar, copiar, mover ou excluir valores de dados — de você querer referenciar muitos valores ou células ou linhas, um ou uma de cada vez. Você poderia querer adicionar registros recentemente obtidos a um conjunto que já existe ou editar muitos valores concatenando cada um com uma string como @mydomain.com ou adicionar novos registros a uma tabela de banco de dados e atribuir-lhes valores de sua planilha. Projetando loops corretamente, você pode executar ações um número enorme de vezes e obter seus resultados com grande velocidade e exatidão.
Utilizando os loops For-Next Um loop For-Next é uma estrutura fundamental em código VBA. É uma maneira básica de tomar uma ação ou conjunto de ações um número especificado de vezes. Eis um exemplo simples e direto: For RowCounter = 1 To 10 Cells(RowCounter, 1) = RowCounter Next RowCounter
A instrução dentro do loop, entre a instrução For e a instrução Next, atribui um valor a uma célula. Este fragmento Cells(RowCounter, 1)
referencia uma célula na linha RowCounter e na coluna 1. Depois da palavra-chave Cells, o primeiro número em parênteses especifica a linha e o segundo número especifica a coluna. Este loop executa 10 vezes: 1. A instrução For especifica que RowCounter começa com um valor de 1. 2. Quaisquer instruções entre For e Next são executadas. Nesse exemplo, o valor atual de RowCounter é colocado na célula definida pela linha RowCounter e a primeira coluna. 3. A instrução Next incrementa RowCounter em 1 e o controle do programa retorna à parte superior do loop. 4. O loop executa novamente, com RowCounter igual a 2. Então executa novamente, com RowCounter igual a 3. A cada vez, ele executa quaisquer instruções localizadas entre a instrução For e a instrução Next.
Capítulo 7 – Revisão dos princípios básicos do VBA
169
Depois de 10 circuitos pelo loop, o valor de RowCounter é incrementado a 11. Mas isso é um a mais que o valor final especificado pela instrução For, então o loop termina antes de poder executar uma décima primeira vez. O resultado é que na planilha ativa, a célula A1 contém 1, a célula A2 contém 2,…, a célula A10 contém 10. Este é um uso quase trivial do loop For-Next, naturalmente, mas ilustra a idéia básica em alguns aspectos do loop For-Next. E simples como é, tem suas utilidades. Suponha que você esteja utilizando o loop para retornar registros, um por um, de uma tabela em um banco de dados e em uma planilha. Você quer que a planilha associe os valores dos registros em um campo específico com seus números de registro. Uma boa maneira de mostrar números de registro é colocar o número do registro na coluna A, como nesse exemplo, e o valor do registro em um campo como Idade na coluna B. Utilizar o valor do contador em cada circuito pelo loop é uma maneira útil de fornecer um número de registro.
Fazendo loop com números de item Anteriormente este capítulo advertiu-o sobre utilizar um número de item para referenciar um objeto em particular. Ao escrever uma instrução pela primeira vez, Worksheets(2) poderia referenciar a planilha chamada Sheet2. Se mais tarde, porém, outra planilha for inserida antes de Sheet2, não há nenhuma informação sobre o que a planilha Worksheets(2) referencia. Mas é uma situação diferente de quando você quer referenciar cada objeto em uma coleção. Nesse caso, você não está interessado em uma planilha particular, mas em afetar todas as planilhas, uma por uma. Então um número de item utilizado em conjunção com um loop faz mais sentido. Eis um exemplo: Option Explicit Sub RefreshAllPivotTables() Dim i As Integer, j As Integer Dim SheetCount As Integer, PivotTableCount As Integer
O código inicia declarando quatro variáveis. As duas primeiras, i e j, serão utilizadas como contadores nos loops For-Next. As duas segundas, SheetCount e PTCount, serão utilizadas para determinar quantas vezes os loops executam. SheetCount = ActiveWorkbook.Worksheets.Count
Esta instrução configura a variável SheetCount como um valor em particular: o número de planilhas na pasta de trabalho ativa. A notação de ponto inicia com a pasta de trabalho ativa e movese daí até a coleção de planilhas na pasta de trabalho ativa. A coleção de planilhas, como é verdade em todas as coleções, tem a propriedade Count. Nesse caso, a propriedade Count retorna o número de planilhas na pasta de trabalho ativa. Esse número é atribuído à variável SheetCount. For i = 1 To SheetCount
O código introduz um loop For-Next. Ele executará uma vez para cada planilha na pasta de trabalho ativa. O contador i inicia em 1 na primeira vez que circular, incrementa por 2 da próxima vez, 3 na vez seguinte e assim por diante até que seja maior que o valor de SheetCount.
170
Gerenciando dados com o Microsoft Excel
ActiveWorkbook.Worksheets(i).Activate
Essa instrução ativa uma planilha na pasta de trabalho ativa. Qual planilha é ativada depende do valor atual de i: o primeiro, o segundo, o terceiro e assim por diante. PivotTableCount = ActiveSheet.PivotTables.Count
A planilha ativa poderia ter qualquer número de tabelas dinâmicas: zero, um, cinco — você não sabe e para os propósitos desse código você não se importa com isso. Assim como a variável SheetCount foi configurada como o número de planilhas na pasta de trabalho ativa, a variável PivotTableCount é configurada como o número de tabelas dinâmicas na planilha ativa. Observe, porém, que o valor de SheetCount é configurado somente uma vez, antes de qualquer loop ser introduzido. O valor de PivotTableCount é reinicializado mais uma vez, toda vez que uma nova planilha é ativada . O VBA faz toda a contagem por você: ele conta o número de planilhas a ativar e em cada planilha conta o número de tabelas dinâmicas a atualizar. Em seguida um loop For-Next interno inicia. O contador j inicia em 1 e incrementa por 1 quando faz um loop por todas as tabelas dinâmicas na planilha ativa, conforme determinado pelo valor atual de PivotTableCount. For j = 1 To PivotTableCount ActiveSheet.PivotTables(j).PivotCache.Refresh Next j
O loop For-Next interno atualiza o cache de cada tabela dinâmica na planilha ativa. Há três pontos sobre esse loop a observar em particular: • É um loop interno; isto é, existe dentro de outro loop. Esse tipo de estrutura é freqüentemente chamado de loop aninhado e é uma técnica útil a qualquer momento que você estiver lidando com uma coleção (aqui, tabelas dinâmicas) que está subordinada a outra coleção (aqui, planilhas). • O loop interno é controlado por um contador diferente daquele do loop externo. Seria sintaticamente ilegal (bem como logicamente sem sentido) utilizar i tanto para o loop interno quanto para o loop externo. • Nenhum dano é causado se a planilha ativa não tiver tabelas dinâmicas. Nesse caso, PivotTableCount foi configurado como igual a zero; e a instrução For executa o contador de 1 a 0 — isso quer dizer que ela simplesmente não executa, então o código não tenta atualizar nenhuma tabela dinâmica. A C I D
Um contador For pode executar de 1 a 0, mas você tem de organizar as coisas para isso adicionando algo como Step -1 à instrução For: For j = 1 to 0 Step -1 . O padrão é aumentar o contador em 1 toda vez que o loop executa.
Por fim, Next i
faz com que o loop externo continue para o próximo valor de seu contador de loop. End Sub
Capítulo 7 – Revisão dos princípios básicos do VBA
171
Conclui a sub-rotina depois que o loop externo termina. Note a maneira como a sub-rotina é projetada: ela ativa cada planilha na pasta de trabalho, uma por uma. Quando uma determinada planilha foi ativada, ela atualiza cada tabela dinâmica nessa planilha, uma por uma. Então, você não está criando um problema como estaria se referenciasse um número de item específico como Worksheets(2) ou Worksheets("Sheet1").PivotTables(3) .
Fazendo loop com Do While Loops For-Next dão a seu código estrutura; isto é, executam um determinado número de vezes. Mesmo se você não souber de antemão quantas vezes um loop executará, você é forçado a fornecer um ponto final para o loop. No último exemplo, os pontos finais eram SheetCount para o loop externo e PivotTableCount para o loop interno. O VBA avalia esses pontos finais antes de o loop começar a executar, e portanto ele já determina exatamente quantas vezes executará. Uma situação diferente indica um tipo diferente de abordagem. Às vezes é necessário fazer um loop por subconjuntos de registros e cada subconjunto pode ter um número diferente de registros. Essa situação é discutida no próximo estudo de caso.
ESTUDO DE CASO Você tem em uma planilha do Excel uma lista de fornecedores e as quantias que você tem de pagar a cada um durante o ano fiscal anterior. No processo de fechar os livros durante esse ano, você quer armazenar a quantia total paga a cada fornecedor em um banco de dados externo. Seus dados são organizados como mostrado na Figura 7.6.
Figura 7.6 Os conjuntos de dados que têm números variados de registros em grupos diferentes, como aqui, freqüentemente indicam loops Do While .
172
Gerenciando dados com o Microsoft Excel
Você gostaria de obter uma quantia total em dólar para cada fornecedor mostrado na Figura 7.6. Observe que fornecedores diferentes têm números diferentes de registros e que os registros já foram classificados por nome de fornecedor. Você não gostaria de obter uma contagem de registros para cada fornecedor — seria necessário se estivesse utilizando um loop For-Next para somar as quantias de compras de um fornecedor. Se você não pretende mover as informações para um banco de dados, essa seria uma configuração óbvia e ideal para uma tabela dinâmica.Você poderia fazer com que o Assistente de tabela dinâmica utilizasse os dados nas colunas A e B como sua fonte e construir uma tabela com Fornecedor como seu campo de linha e Soma de quantias como seu campo de dados. Mas como você quer automaticamente atualizar um banco de dados com totais de fornecedores, uma tabela dinâmica não o ajudará: uma tabela dinâmica pode importar dados externos, mas não pode exportá-los. Em vez disso, você utiliza o seguinte código em preparação para atualizar o banco de dados. Você começa configurando uma referência como a biblioteca de objeto DAO, como discutido anteriormente neste capítulo em “Utilizando modelos de objeto de outras aplicações". Você então declara a sub-rotina e as variáveis que precisa: Sub TotalVendorPurchases() Dim Dim Dim Dim Dim
InputRow As Long, OutputRow As Long, FinalRow As Long PurchasesFromVendor As Currency CurrentVendor As String VendorBuys As DAO.Recordset Vendors2004 As DAO.Database
Planilhas Excel podem ter até 65.536 linhas. As variáveis InputRow, OutputRow e FinalRow conterão cada uma um número de linha e é concebível que os números de linha serão maiores que 32.767 — o valor legal máximo para uma variável do tipo Integer. Portanto, você declara as variáveis como Long — um tipo numérico inteiro mas que pode assumir valores (muito) maiores que 65.536. Você também declara duas variáveis de objeto, uma para representar o banco de dados e uma para representar um conjunto de registros ou recordset , no banco de dados. Seu código então configura as duas variáveis de objeto. Configura a variável de objeto Vendors2004 igual a um banco de dados do Access existente, localizado no mesmo caminho de sua pasta de trabalho. O código então configura a variável de objeto VendorBuys igual a uma tabela, TotalByVendor, nesse banco de dados. Set Vendors2004 = OpenDatabase(ThisWorkbook.Path & "\2004 Finals.mdb") Set VendorBuys = Vendors2004.OpenRecordset("TotalByVendor", dbOpenDynaset)
Em seguida, você precisa descobrir até onde na planilha seus dados se estendem: FinalRow = Worksheets("Purchases").Cells(65536, 1).End(xlUp).Row
O uso de End(xlUp) é valioso. Você o utiliza aqui para determinar até onde na planilha o código deve examinar em busca de mais registros. Suponha que você ative a célula A65536 (a célula final na Coluna A), que é uma célula vazia, e que você mantenha pressionada a tecla Ctrl e então pressione o botão de seta para cima. O Excel tornaria a célula ativa a primeira acima de A65536 que não estivesse vazia. Isto é o que essa instrução faz: inicia em Cells(65536,1) e segue para o final, subindo até localizar uma célula não vazia. Seja qual for a célula final, seu número de linha é atribuído à variável FinalRow. (Você também pode utilizar xlDown, xlToRight e xlToLeft, dependendo do que você quer realizar.) OutputRow = 1 InputRow = 2
A saída iniciará na linha 1 e a entrada iniciará na linha 2, por causa dos cabeçalhos Vendor e Amount. CurrentVendor = Worksheets("Purchases").Cells(InputRow, 1)
Capítulo 7 – Revisão dos princípios básicos do VBA
173
Você armazena o valor atual do nome do fornecedor na variável CurrentVendor. Enquanto o código prossegue pelas linhas de entrada, o nome de fornecedor muda. Quando isso ocorre, as compras totais desse fornecedor são gravadas em uma planilha de saída e no banco de dados e as variáveis são reinicializadas. Do While InputRow <= FinalRow
O loop externo executará contanto que a linha da qual a entrada é tomada seja menor que ou igual à linha final de dados de entrada. Do While CurrentVendor = Worksheets("Purchases").Cells(InputRow, 1)
O loop interno executa contanto que o nome do fornecedor na linha atual de entrada seja igual ao valor de CurrentVendor. PurchasesFromVendor = PurchasesFromVendor + _ Worksheets("Purchases").Cells(InputRow, 2)
Até que o loop interno termine (quando o nome de fornecedor muda), o código continua a incrementar as compras totais para esse fornecedor pelo valor localizado na linha atual de entrada, segunda coluna. InputRow = InputRow + 1
E incrementa o valor de InputRow, assim na próxima passagem, o loop verá a próxima linha abaixo na planilha. Loop
A instrução Loop marca o fim do loop interno. Quando o nome de fornecedor muda, o loop intern o termina e o seguinte código executa: Worksheets("TotalByVendor").Cells(OutputRow, 1) = CurrentVendor Worksheets("TotalByVendor").Cells(OutputRow, 2) = PurchasesFromVendor With VendorBuys .AddNew .Fields("VendorName") = CurrentVendor .Fields("PurchaseTotal") = PurchasesFromVendor .Update End With OutputRow = OutputRow + 1 CurrentVendor = Worksheets("Purchases").Cells(InputRow, 1) PurchasesFromVendor = 0
Em linguagem natural (não na linguagem de programação BASIC): escreve o nome do fornecedor atual na coluna 1 da planilha TotalByVendor, na linha identificada por OutputRow. Escreve as compras totais desse fornecedor na coluna 2 da mesma linha. Adiciona um novo registro à tabela de banco de dados e configura esses dois campos do registro igual ao nome do fornecedor atual e o total das compras desse fornecedor.Estabelece o novo registro chamando o método Update da tabela. Incrementa OutputRow em 1. Obtém o próximo nome de fornecedor da planilha Purchases e armazena em CurrentVendor. Reinicializa PurchasesFromVendor como zero. Loop End Sub
Termina o loop externo quando InputRow tornar-se maior que FinalRow e conclui a sub-rotina. A Figura 7.7 mostra como é a saída quando o código conclui.
174
Gerenciando dados com o Microsoft Excel
Figura 7.7 Esses registros parecem ser os mesmos registros na tabela de banco de dados e no intervalo de planilha.
Utilizando blocos With Uma maneira conveniente e útil de utilizar notação de ponto refere-se ao que denominamos de blocos With-End With ou simplesmente blocos With. Você utiliza a palavra-chave With, associada a um objeto de algum tipo, para indicar que uma ou mais instruções subseqüentes pertencem a outros objetos (ou propriedades ou métodos) que pertencem ao objeto nomeado na instrução With. Tudo desde a instrução With até a instrução End With é considerado parte do bloco With. É muito mais fácil entender os blocos With observando-os na prática do que lendo a teoria. Eis um exemplo, primeiro sem o bloco With e depois utilizando um: ActiveSheet.Rows(2).Font.ColorIndex = 3 ActiveSheet.Rows(2).Font.Bold = True
Essas duas instruções, quando executadas, mudam a cor da fonte da segunda linha da planilha ativa para vermelho e seu estilo para negrito. With ActiveSheet.Rows(2) .Font.ColorIndex = 3 .Font.Bold = True End With
Capítulo 7 – Revisão dos princípios básicos do VBA
175
Essas quatro instruções fazem a mesma coisa que as duas anteriores. Dentro do bloco — isto é, depois da instrução With — tudo que começa com um ponto é considerado como pertencente ao objeto citado na instrução With. No exemplo anterior, a fonte, seu índice de cor e seu estilo são considerados como pertencentes à segunda linha da planilha ativa. Você pode estender o alcance da instrução With além do que foi mostrado antes. Como a propriedade Font inicia ambas as instruções depois da instrução With, ela poderia ser movida até o With externo: With ActiveSheet.Rows(2).Font .ColorIndex = 3 .Bold = True End With
Blocos With aninhados As instruções With podem ser aninhadas dentro de outras instruções With. O requisito, naturalmente, é que o elemento utilizado no With interno deve pertencer ao elemento utilizado no With externo. Eis um exemplo que não utiliza um With aninhado, mas poderia: With ActiveSheet.Cells(10, 12) .VerticalAlignment = xlTop .WrapText = True .Font.Name = "Times New Roman" .Font.Size = 12 End With
Esse código configura quatro propriedades para a célula L10 na planilha ativa: seu valor é exibido começando na parte superior da célula, texto longo é quebrado, sua fonte é Times New Roman e seu tamanho de fonte é 12. Como a propriedade Font da célula é utilizada duas vezes dentro do bloco, faz sentido revisá-la como um With aninhado, como segue: With ActiveSheet.Cells(10, 12) .VerticalAlignment = xlTop .WrapText = True With .Font .Name = "Times New Roman" .Size = 12 End With End With
Observe o segundo bloco With interno. O fato de seu elemento, .Font, iniciar com um ponto significa que ele pertence a um With anterior — aqui, a célula L10 na planilha ativa.
With Entendendo o raciocínio dos blocos Os blocos With são propagados como uma maneira de acelerar o processamento. A noção é de que leva mais tempo interpretar estas duas instruções ActiveSheet.Rows(2).Font.ColorIndex = 3 ActiveSheet.Rows(2).Font.Bold = True
do que interpretar este bloco With, embora ele tenha duas instruções adicionais:
176
Gerenciando dados com o Microsoft Excel
With ActiveSheet.Rows(2).Font .ColorIndex = 3 .Bold = True End With
Embora seja verdade que há um aumento em velocidade, para a maioria dos propósitos, o aumento é imperceptível. As duas sub-rotinas a seguir foram executadas em um Pentium 4 de 1.8GHz. A primeira não tem o bloco With e levou 17 segundos para executar concluindo em uma planilha nova: Sub NoWith() Dim i As Integer For i = 1 To 10000 ActiveSheet.Cells(i, ActiveSheet.Cells(i, ActiveSheet.Cells(i, ActiveSheet.Cells(i, Next i End Sub
1).VerticalAlignment = xlTop 1).WrapText = True 1).Font.Name = "Times New Roman" 1).Font.Size = 12
A segunda utiliza blocos With aninhados e levou 14 segundos, novamente em uma planilha nova: Sub WithWiths() Dim i As Integer For i = 1 To 10000 With ActiveSheet.Cells(i, 1) .VerticalAlignment = xlTop .WrapText = True With .Font .Name = "Times New Roman" .Size = 12 End With End With Next i End Sub
Quatorze segundos versus 17 segundos é um aumento de 18% em velocidade. Mas se formatasse 100 linhas em vez de 10.000 seria improvável notar esses 18%. E algumas tarefas simplesmente não são aceleradas utilizando blocos With. Blocos With são considerados uma maneira de encorajá-lo a estruturar seu código mais compactamente. Eles poupam digitação, embora sempre adicionem duas instruções ao código (a With e a End With ). Como não é necessário digitar ou copiar e colar o elemento repetidamente na instrução With, costuma-se utilizar blocos With como um poupador de tempo. Uma conseqüência é a melhor estrutura em seu código. Por exemplo, não é incomum introduzir uma instrução VBA que configura alguma propriedade de um objeto: o tamanho da fonte da célula ou o valor máximo mostrado em um eixo de gráfico ou a propriedade de formato de número de um campo de dados da tabela dinâmica. Subseqüentemente, 10 instruções adicionais em seu código, você decide configurar alguma outra propriedade que pertence ao mesmo objeto: a altura da célula ou o alinhamento de texto do eixo de gráfico ou a estatística de resumo do campo de dados. É realmente tentador digitar a instrução que teria configurado a propriedade.
Capítulo 7 – Revisão dos princípios básicos do VBA
177
Mas se tiver em mente seus problemas posteriores — manter o código ou depurá-lo ou passar outra pessoa por ele — você voltará e colocará essa instrução junto com a instrução anterior em um bloco With. Quando você puder fazer isso racionalmente, é melhor manter as instruções que operam no mesmo objeto juntas. Como um bônus, você se encontrará digitando menos depois de ter estabelecido o bloco With — e quanto menos digitar, melhor.
Entendendo o código do programa de gravação de macros Muito provavelmente você sabe que pode gravar código VBA que, quando executado, repete quaisquer ações que você realizou enquanto o gravador estava executando. O raciocínio por trás disso não é tanto gravar um utilitário que o poupa de repetir a mesma seqüência de teclas ou movimentos do mouse várias vezes, embora isso aconteça. O benefício real em gravar macros é aprender mais sobre o VBA. Se não estiver familiarizado com o programa de gravação de macros, experimente. Escolha Ferramentas, Macro, Gravar nova macro. Clique em OK na caixa de diálogo Gravar macro (veja a Figura 7.8). Figura 7.8 Para algo que é utilizado freqüentemente, você pode armazenar a macro gravada em uma pasta de trabalho de Macros pessoal.
Agora faça algo relevante para o Excel — insira um valor em uma célula ou classifique um intervalo de valores ou copie e cole algo. Clique no botão Parar gravação ou escolha Ferramentas, Macro, Parar gravação (veja a Figura 7.9). Figura 7.9 Clique no botão Parar gravação quando terminaro de tomar ações que você quer gravadas no VBA. Botão Parar gravação
Pressione Alt+F11 para alternar para o VBE. Você verá algo semelhante ao que é mostrado na Figura 7.10. O código que você vê na Figura 7.10 foi criado pelo programa de gravação de macros em resposta a estas ações: 1.
Configure o formato de número da célula ativa como Moeda.
178
Gerenciando dados com o Microsoft Excel
2.
Configure o alinhamento horizontal da célula ativa como Direita (Recuo).
3.
Configure a fonte da célula ativa como Courier.
4. Atribua uma
borda esquerda fina à célula ativa.
O programa de gravação de macros tenta fornecer código que seja abrangente e assim não se limita estritamente aos resultados de ações realizadas pelo usuário. Por exemplo, ele utiliza um bloco With para configurar nove aspectos de formatação da célula selecionada: formato de número, alinhamento horizontal, alinhamento vertical, disposição do texto e assim por diante. Ele faz isso mesmo que as únicas propriedades que foram realmente alteradas tenham sido seu formato de número, alinhamento horizontal, sua fonte e sua borda esquerda. Figura 7.10 O programa de gravação de macros normalmente grava muito mais do que realmente aconteceu.
A T O N
Embora o programa de gravação de macros tente ser abrangente, há ações que você talvez realize enquanto grava que não ficam registradas, ou que não gravam enquanto você talvez as espere. Por exemplo, se alternar para outra aplicação ou mesmo para o VBE, enquanto grava uma macro, essa ação não vai ser gravada. Se você executar uma ação repetidamente, o gravador não irá inserir um loop For-Next ou um Do While dentro do código; ele apenas gravará cada ação separadamente. O gravador não declara variáveis por você. Se você não puder fazer isso na planilha, o gravador o ignora.
O gravador não responde às alterações nos elementos de planilha, mas às suas propriedades seguindo alguma ação realizada pelo usuário. Depois de configurar o alinhamento horizontal como Direita (Recuo), as outras propriedades (alinhamento vertical, disposição do texto e assim por diante até mesclagem de célula) estavam do modo mostrado no código gravado. Elas não foram alteradas, mas o gravador não sabe disso.
Capítulo 7 – Revisão dos princípios básicos do VBA
179
Trabalhar diretamente com o código criado pelo gravador tem vários benefícios. Dois dos mais importantes são discutidos nas próximas duas seções.
Aprendendo os nomes no modelo de objeto O modelo de objeto do Excel é tão grande que não é possível, nem mesmo sensato, aprender todos os seus objetos, métodos e propriedades. Mesmo se o fizer, você ainda precisaria dos nomes dos valores que as propriedades podem assumir. Por exemplo, utilizar a propriedade End do objeto Range pode levá-lo para cima ou para baixo de uma seleção — você pode utilizar xlUp ou xlDown. Mas para ir à esquerda ou à direita, você tem de especificar xlToLeft ou xlToRight. É insensato lembrar-se disso. Se não lembrar um nome, utilize o programa de gravação de macros para fornecê-lo a você. Por outro lado, há alguns objetos e construções que são tão freqüentemente ao desenvolver o código que é realmente necessário saber seus nomes. Caso contrário, seu tempo será gasto de maneira ineficiente. Suponha que você costume formatar células, como Moeda ou Data ou Porcentagem. Nesse caso, é importante saber que string é utilizada para chamar um formato de número em particular. E a maneira mais rápida de localizar raramente é por meio da documentação de Ajuda, mas gravando uma macro que atribui um formato a uma célula e então examinando o código resultante.
Adaptando o código a um outro propósito É comum acontecer de você estar codificando alguma tarefa quando de repente se dá conta: você nota que seguiu o próximo passo muitas vezes na planilha mas nunca no código. Inseriu um novo nome em um intervalo, por exemplo, ou classificou um intervalo de células em ordem crescente. Agora você quer fazer algo semelhante em seu código VBA, mas não tem nenhuma idéia de como fazer isso. O programa de gravação de macros é uma solução ideal aqui. Suponha que queira saber como chamar o VBA para classificar os dados em um intervalo de células. Acontece que os dados nas células A1:B17 e as células A1s e B1 contêm cabeçalhos de linha (em outras palavras, você tem uma lista de Excel em A1:B17). Então, clique em alguma célula dentro de A1:B17 — digamos, A7 — e escolha Ferramentas, Macro, Gravar nova macro para descobrir como classificá-la utilizando o VBA. Depois classifique a lista. Suponha que você classifique primeiro os valores localizados na Coluna B e então os valores localizados na Coluna A e que queira uma classificação ascendente em ambas as colunas. Na caixa de diálogo Classificar, se necessário, você identifica a Linha 1 como uma linha de cabeçalho. Clique em OK, pare o programa de gravação de macros, alterne para o VBE e veja o seguinte: Range("A7").Select Range("A1:B17").Sort Key1:=Range("B2"), Order1:=xlAscending, Key2:=Range( _ "A2"), Order2:=xlAscending, Header:=xlGuess, OrderCustom:=1, MatchCase _ :=False, Orientation:=xlTopToBottom, DataOption1:=xlSortNormal, _ DataOption2:=xlSortNormal
A primeira instrução apenas mostra que você começou selecionando a célula A7, uma célula dentro do intervalo que você quer classificar.
180
Gerenciando dados com o Microsoft Excel
A próxima instrução mostra que em geral você faz o VBA classificar um intervalo de células chamando o método Sort desse intervalo. No código para a tarefa mais generalizada que você está codificando, talvez utilize algo como isto: Dim SortRange As Range Set SortRange = ActiveSheet.Range(Cells(1, 1), Cells(17, 2)) SortRange.Sort Key1:=SortRange.Offset(1, 1).Resize(1, 1), _ Order1:=xlAscending, Key2:=SortRange.Offset(1, 0).Resize(1, 1), _ Order2:=xlAscending, Header:=xlYes
Há vários aspectos nesse código que valem mencionar: • O endereço do intervalo de classificação não é especificado na instrução que realiza o método Sort. Em vez disso, uma variável de objeto (veja a próxima seção deste capítulo, “Utilizando variáveis de objetos) que representa um intervalo é atribuída a um intervalo de células (aqui, A1:B17). Essa variável de objeto, representada dentro do intervalo real, é classificada. • As chaves de classificação ainda são B2 e A2 (nessa ordem), mas são referidas como deslocamentos em relação à célula superior esquerda do intervalo. A célula B2 é deslocada de A1 por uma linha e uma coluna; a célula A2 é deslocada de A1 por uma linha e zero colunas. Dessa maneira, a instrução de classificação não utiliza as referências de célula “B2” e “A2” que são fornecidas pelo programa de gravação de macros. Isso torna possível utilizar a instrução de classificação em outra parte em seu código quando você quiser classificar um intervalo que não inclua B2 ou A2. • A especificação Header no código fornecido pelo programa de gravação de macros utiliza xlGuess; isto é, o Excel deve determinar se o intervalo tem uma linha de cabeçalho. O código revisado especifica xlYes porque o programador sabe que o intervalo tem uma linha de cabeçalho. • Eliminaram-se do código especificações estranhas, como DataOption1 e MatchCase . Ao adaptar algo que você obtém do programa de gravação de macros, você gasta algum tempo se livrando de código desnecessário que o gravador, em seu esforço para ser abrangente, insiste em fornecer. Você notará que o código gerado pelo programa de gravação de macros freqüentemente utiliza Select, configura as propriedades das seleções e executa métodos que se aplicam às seleções. Por exemplo Range("A2").Select Selection.Font.ColorIndex = 3
Se você utilizar isso como um bloco de construção para uma sub-rotina diferente, você pode, e deve, mudar isso para algo como Range("A2").Font.ColorIndex = 3
Não há nenhuma exigência de você selecionar um objeto antes de configurar uma de suas propriedades; fazer isso torna o código lento sem necessidade e muito mais difícil de manter.
Capítulo 7 – Revisão dos princípios básicos do VBA
181
Utilizando as variáveis de objeto As variáveis de objeto ocupam um lugar importante no VBA para Excel e, mais adiante neste livro, quando as estruturas de banco de dados forem discutidas, elas assumirão importância ainda maior. É possível utilizar o VBA eficientemente sem saber como utilizar as variáveis de objeto, mas não é fácil.
Entendendo variáveis de objeto Considere a idéia de idade como uma variável. É algo que você avalia ao lidar com informações sobre as pessoas. Se estiver emitindo carteiras de motorista, você avalia a idade dos motoristas. Se os emprega, você armazena os dados sobre a idade — pelo menos, datas de nascimento — em um arquivo. Se os encontra socialmente, você imagina uma estimativa aproximada de suas idades. A idade é uma variável. Varia de pessoa para pessoa. Cada pessoa tem uma idade: 75 anos, 52 anos, 16 meses, 25 dias e assim por diante. Uma idade é um valor que a variável Age pode assumir. No VBA, se estiver lidando com informações sobre pessoas, talvez você declare uma variável chamada Age: Dim Age As Single
Declarar Age dessa maneira, como um número de precisão simples, significa primeiro que ele é um número e não, digamos, uma string de texto como “Tom”; segundo, significa que Age pode ter valores fracionários, como 75,083 e 1,3. Depois de declarar uma variável, você pode utilizá-la em instruções como esta: Age = 52.5
O VBA não sabe nada sobre variáveis comuns como Age, exceto que como declarado aqui é um número e pode ter valores fracionários. Ele não sabe que, nas pessoas, os valores de idade normalmente variam de zero a oitenta ou que é preciso ter pelo menos 18 para poder votar em eleições estaduais e federais ou que a sociedade atribui certas atitudes a certas faixas etárias. Ao contrário, uma variável de objeto no VBA é um tipo especial e o VBA entende muito bem isso. Uma variável de objeto pode representar um intervalo de planilha do Excel, por exemplo. Tenho que declará-la como tal; por exemplo, Dim TheRange As Range
mas depois que fizesse isso, o VBA saberia que • TheRange pode assumir valores como A1:B17 ou C4:C65536 ou mesmo D4 (sim, uma única célula é um intervalo no que diz respeito ao modelo de objeto). TheRange pode assumir qualquer conjunto de células como seu valor. • TheRange tem algum número de colunas, algum número de linhas, possivelmente uma fonte, a capacidade de ser classificada e todas as outras propriedades e métodos que pertencem aos intervalos, independentemente de quais células eles abrangem. Na seção anterior, você viu esse código sendo utilizado:
182
Gerenciando dados com o Microsoft Excel
Dim SortRange As Range Set SortRange = ActiveSheet.Range(Cells(1, 1), Cells(17, 2))
Ele utiliza SortRange como uma variável de objeto, primeiro declarando para o VBA que ela representará valores de intervalo (que são conjuntos de células) e então atribuindo um conjunto particular de células, A1:B17, à variável de objeto SortRange. Atribuindo os endereços de célula à variável de objeto e então utilizando o método Sort no intervalo que a variável representa, o programador mantém endereços de célula específicos fora da instrução de classificação. Por sua vez, isso permite ao programador utilizar a instrução de classificação repetidamente, em conjuntos diferentes de células, apenas atribuindo intervalos diferentes à variável de objeto SortRange.
Configurando variáveis de objeto Observe como o intervalo de células — o valor — é atribuído à SortRange — a variável. Ao atribuir algum valor a uma variável simples como Age, tudo que você precisa fazer é mencionar o nome da variável, introduzir um sinal de igual e então introduzir o valor que você quer que a variável tenha. Ao contrário, quando estiver trabalhando com uma variável de objeto, você tem de utilizar a palavra-chave especial Set. Depois continue como faria com uma variável simples: mencione seu nome, forneça um sinal de igual e então introduza o valor que você quer que a variável de objeto tenha. Eis mais alguns exemplos: Dim WS As Worksheet Set WS = ThisWorkbook.Worksheets("Sheet1") WS.Move Before:=Sheets(1) Dim TheName As Name Set TheName = ActiveWorkbook.Names("Revenues") TheName.RefersTo = "=Purchases!$A$1:$B$19" Dim TheChart As Chart Set TheChart = Workbooks("Quarter2.xls").Charts("Costs") TheChart.ChartType = xlLine
Declarando variáveis de objeto As variáveis de objeto podem representar muitos objetos do Excel além de intervalos. Uma pessoa que escreve código VBA para Excel freqüentemente utiliza variáveis de objeto para representar colunas, linhas, barras de ferramentas, gráficos, componentes de gráfico como eixos e série de dados, planilhas e assim por diante. É até mesmo possível atribuir um módulo com código VBA a uma variável de objeto. Em geral, você pode utilizar o VBA para declarar uma variável de objeto representando qualquer coisa em uma pasta de trabalho do Excel que esteja no modelo de objeto. Fazendo isso, você declara que ela é uma variável de objeto. Depois de ter declarado uma variável de objeto, você não pode atribuir a ela nada que seja de um tipo diferente. Por exemplo, se declarou TheAxis para representar um eixo de gráfico Dim TheAxis As Axis
você não pode atribuir um valor como 2 a essa variável. Isto é, esta instrução
Capítulo 7 – Revisão dos princípios básicos do VBA
183
Set TheAxis = 2
resultaria no erro de compilação Tipos
Incompativeis .
Objetos em loops For Each Este capítulo já discutiu os loops For-Next e os loops Do While na seção “Utilizando loops”. Outro tipo de loop, For Each , é útil quando você estiver trabalhando com variáveis de objeto. Como um loop For-Next , um loop For Each executa um número específico de vezes, uma vez para cada instância de uma variável. Em um loop For-Next, a variável é simples, em geral um Integer ou Long Integer, que executa desde um valor inicial até um valor final. Em um loop For Each , essa variável é uma de objeto que assume cada instância disponível de seu tipo de objeto. Suponha que você queira atribuir um nome representando um ano (2003, 2004, 2005 e assim por diante) a cada planilha em uma pasta de trabalho chamada Annual Results. O seguinte código mostra uma maneira de fazer isso: Dim WS As Worksheet Dim WhichYear As Integer WhichYear = 2003 For Each WS In Workbooks("Annual Results").Worksheets WS.Name = WhichYear WhichYear = WhichYear + 1 Next WS
Duas variáveis são declaradas: uma variável de objeto, WS, para representar planilhas e uma variável simples, WhichYear, para representar números inteiros. WhichYear é inicializada como 2003 e um loop For Each inicia. A instrução For Each , associada com a instrução Next , faz com que a variável de objeto WS assuma, por sua vez, cada planilha na coleção de planilhas que pertence à pasta de trabalho chamada Annual Results. A cada circuito do loop, a variável de objeto WS representa uma planilha diferente e o nome dessa planilha é configurado com o valor atual de WhichYear . Então WhichYear é incrementada, Next WS atribui a próxima planilha a WS e o loop se repete. Quando a planilha final na coleção tiver sido processada, o loop termina. Estes elementos são necessários para que um loop For
Each funcione adequadamente:
• O nome de uma variável de objeto deve seguir a palavra-chave For
Each .
• A variável de objeto deve ser seguida por uma coleção e a coleção deve ser do mesmo tipo que a variável de objeto. No exemplo dado anteriormente, a variável de objeto WS é declarada com o tipo Worksheet. A coleção de planilhas em uma pasta de trabalho também é do tipo Worksheet. • Normalmente é necessário especificar a que a coleção pertence. No exemplo, a coleção de planilhas pertence à pasta de trabalho chamada Annual Results.
184
Gerenciando dados com o Microsoft Excel
Olhando para frente Este capítulo revisou os aspectos do VBA que ajuda você a obter dados existentes em uma planilha. O VBA costuma ser a melhor maneira de automatizar a transferência de dados de um banco de dados em uma planilha e é sempre a melhor maneira de automatizar a exportação de dados do Excel para um banco de dados. Mas você tem de instruir o VBA; e técnicas como loops For-Next, loops Do While e loops For Each são fundamentais para ajudar o VBA a localizar dados existentes e a mover-se por esses dados. Outras ferramentas do VBA como estruturas With e variáveis de objeto são vitais para manter seu código gerenciável. O Capítulo 8, “Abrindo bancos de dados”, complementa o material sobre o qual você leu neste capítulo. Aqui você viu como utilizar o VBA para alcançar e manipular os dados na planilha. No Capítulo 8, verá como utilizar o VBA para alcançar e manipular as tabelas em bancos de dados externos. Os capítulos 9 a 12 combinam essas informações para mostrar como automatizar a comunicação de duas vias entre pastas de trabalho do Excel e bancos de dados externos.
8 Abrindo bancos de dados Conectando-se a um banco de dados O Capítulo 7 mencionou diferenças entre Data Access Objects (DAO) e ActiveX Data Objects (ADO). Em algumas áreas, ambos são idênticos — pelo menos da perspectiva de escrever o código que utiliza os objetos. Por exemplo, um modo de mover um registro para o próximo é EmployeeRecords.MoveNext
Você pode usar esse comando se fez sua conexão com banco de dados utilizando o DAO ou ADO. Em outras áreas, as duas bibliotecas de objeto não poderiam ser mais diferentes. A abordagem do DAO mais antiga envolve uma hierarquia mais rigorosa e seu código tem de observá-la. Uma seqüência típica do DAO de eventos é a seguinte: 1.
Configure uma variável de objeto, como TheDB, que representa um banco de dados.
2.
Configure outra variável de objeto, como TheQuery, que representa uma consulta ou tabela em TheDB.
3.
Configure ainda outra variável de objeto, como TheRecords, que representa os registros e campos em TheQuery. (Tanto o DAO como o ADO denominam isso de recordset.)
Mesmo se você não configurasse cada uma dessas explicitamente como uma variável de objeto, o DAO insistiria que você adaptasse a sua hierarquia: referir-se primeiro ao banco de dados e então a uma tabela ou consulta no banco de dados e, em seguida, aos registros na tabela ou na consulta. Você poderia adotar a mesma abordagem utilizando ADO, mas o ADO é muito mais flexí vel. Os objetos em ADO ainda correspondem a outros objetos, mas ele é comparativamente complacente quanto a quando e onde objetos, propriedades e métodos são utilizados. Por exemplo, utilizando o ADO você poderia declarar e criar um recordset, completo com campos e registros, antes mesmo de mencionar um banco de dados para conter o recordset. O ato de conectar-se a um banco de dados, o tópico deste capítulo, é uma área onde as diferenças entre ADO e DAO são mais pronunciadas. O ADO é discutido em seguida em “Conectando-se utilizando o ADO”; se você não tem acesso ao ADO ou prefere utilizar o DAO, encontrará as informações necessárias em “Abrindo um banco de dados com o DAO”.
Conectando-se utilizando o ADO Há três objetos para você usar ao estabelecer uma conexão com um banco de dados utilizando o ADO. Em grande parte serão desconhecidos se você tiver utilizado o DAO. Que objeto utilizar depende em parte do que você quer realizar. Um objeto Connection é discutido aqui. Os outros
186
Gerenciando dados com o Microsoft Excel
dois, o objeto Command e o objeto Recordset, são discutidos no Capítulo 10, “Definindo campos e registros com o ADO e o DAO”, e no Capítulo 11, “Obtendo dados do Access para o Excel com o ADO e o DAO”.
Estabelecendo uma referência à biblioteca ADO Antes de poder utilizar o ADO, você precisa estabelecer uma referência à biblioteca ADO. Se necessário, alterne para o VBE e escolha Referências do menu Ferramentas. Role para baixo até encontrar a referência desejada e marque sua caixa de seleção (veja a Figura 8.1). Se quiser estabelecer mais de uma referência, continue procurando e marcando suas caixas de seleção. Quando concluir, clique em OK. Figura 8.1 A biblioteca ADO mostrada na caixa de listagem fornece referências a objetos ADODB.
Embora o conteúdo da biblioteca de objetos seja em geral referido como ADO ou ActiveX Data Objects , o ADO não é uma única biblioteca, mas um conjunto de bibliotecas. Aquilo ao qual você realmente está fazendo referência ao marcar, digamos, Microsoft ActiveX Data Objects 2.7 Library, é o ADODB. Ao fazer referência à biblioteca no código VBA declarando uma variável objeto, você deve muitas vezes qualificar a referência com ADODB. Normalmente é melhor escolher o nível de versão mais alto disponível. Se achar que um nível de versão mais alto (o termo é equivalente a mais novo, mas infelizmente, nem sempre a melhor ) não funciona como esperava, você pode dar um passo para trás. Como mostrado na Figura 8.1, a caixa de diálogo Referências tem uma caixa de listagem contendo as bibliotecas disponíveis. Das bibliotecas marcadas, quanto mais altas na caixa de listagem, mais alta a prioridade da biblioteca para resolver conflitos de denominação. Por exemplo, tanto a biblioteca de objetos do Excel como a biblioteca de objetos ADO têm um objeto Parameter.O VBA utilizará o objeto Excel Parameter se a biblioteca do Excel estiver mais A alta na caixa de listagem e o objeto ADO Parameter se a biblioteca ADO estiver mais alta. Ainda assim, é melhor deixar T O as coisas explícitas. Se quiser declarar uma variável de objeto para representar um objeto ADO, qualifique-a com N ADODB, um objeto Excel com Excel e assim por diante: Dim rsRevenues As ADODB.Recordset Dim dbFinancials As DAO.Database Dim prmYear As Excel.Parameter
Capítulo 8 – Abrindo bancos de dados
A C I D
187
Há algo sobre a estrutura da caixa de listagem Referências Refe rências disponíveis que tende a enganar os usuários. É fácil clicar e destacar uma biblioteca e então clicar em OK para dispensar a caixa de diálogo. Se fizer isso, talvez ache que estabeleceu uma referência a uma biblioteca quando de fato você não o fez. Naturalmente, é necessário marcar a caixa de seleção da biblioteca antes de fechar a caixa de diálogo. diálo go. Mas vi muitos usuários simplesmente clicar em um nome da biblioteca e imediatamente imediatament e clicar em OK — então ficam confusos quando seu código não reconhece uma biblioteca. Fiz isso mais vezes do que gostaria de admitir. Se seu código não reconhece uma biblioteca que você pensa ter selecionado, verifique a lista de referências.
Preparando um objeto de conexão Você declara um objeto Connection Você Connection de modo muito parecido com o que você declara declara qualquer variável de objeto no VBA. Por exemplo Dim cnConnectToLedger As ADODB.Connection
Se estiver certo de que você não configurou uma referência a qualquer outra biblioteca que tem um objeto Connection, você poderia em vez disso declarar desta maneira: Dim cnConnectToLedger As Connection
Como observado anteriormente, porém, você também poderia ter referenciado outra biblioteca que tem um objeto Connection e essa biblioteca bibliote ca talvez seja mais alta na hierarquia de prioridade do que a biblioteca ADO. Parcialmente é uma questão do quanto você está confiante e, em parte, do estilo de programaçã programação, o, mas você deve pelo menos considerar preceder a classe Connection com o qualificador ADOD ADODB B.
A T O N
Uma classe é a definição de um objeto. Ela contém todas as informações que o VBA precisa para criar uma nova instância do objeto: objet o: quais quais propriedades se aplicam a ele e que valores elas podem assumir,métodos que podem ser utilizados com o objeto e assim por diante. É uma espécie de modelo para seu objeto.
Depois de ter declarado uma variável de objeto como Connection, você pode criar uma nova instância dela: Set cnConnectToLedger = New ADODB.Connection
A instrução Dim provavelmente pareceu razoavelmente familiar a você, mas essa instrução Set não. Todas as instruções Set que foram discutidas no Capítulo 7 configuram uma variável de objeto igual a uma planilha em particular ou a um eixo de gráfico ou a um intervalo de células — qualquer que seja a variável de objeto representada, o objeto já existia. Desse modo, foi possível configurar uma variável de objeto para representar Sheet1 ou o eixo vertical do gráfico ou o intervalo A3:D15. Entretanto, ao configurar um objeto Connection pel a primeira vez, ele ainda não existe. Então, sua instrução Set utiliza a palavra-chave New para informar ao VBA de que ele precisa criar uma nova instância de um ADODB.C onnection .
188
Gerenciando dados com o Microsoft Excel
Nesse ponto, você não apontou a conexão, cnConnectToLedger , para nenhum banco de dados; não abriu a conexão e não declarou que a conexão utilizará o mecanismo de bancos de dad os Jet ou SQL Server ou algum outro provedor. provedo r. Mas isso é simples.
Utilizando a palavra-chave New O exemplo dado na seção anterior foi Dim cnConnectToLedger As ADODB.Connection Set cnConnectToLedger = New ADODB.Connection
e a palavra-cha palavra-chave ve New foi utilizada na instrução Set. A instrução Dim poderia ter sido utilizada: Dim cnConnectToLedger As New ADODB.Connection
Há uma diferença entre os dois usos. Ao utilizar a palavra-ch palavra-chave ave New na instrução Set, como foi feito na seção anterior, você está admitindo a possibilidade de que a variável de objeto talvez já tenha representado outro objeto. obje to. Nesse caso, ao configurar a variável de objeto como algum outro ou tro objeto, a primeira referência é liberada e o VBA cria uma nova instância da classe. Ao contrá contrário, rio, suponh suponhaa que você declar declaree a variáv variável el de objeto como uma New ADODB.Connection , como nesse exemplo. Se fizer isso, você não utiliza Set para criar uma nova instância da classe. Utiliza apenas a variável de objeto. Por exemplo Dim cnConnectToLedger As New ADODB.Connection With cnConnectToL cnConnectToLedge edger r .Provider = "Microsoft.Jet.OLE "Microsoft.Jet.OLEDB.4.0" DB.4.0" .ConnectionString = "C:\GeneralLedger "C:\GeneralLedger.mdb" .mdb" End With
Observe que uma instrução Set não é utilizada.
Abrindo a conexão ADO Depois de ter estabelecido a conexão, você precisa refiná-la. Há várias propriedades que você pode configurar e métodos que gostará de invocar. Por causa da grande flexibilidade do ADO, você achará que tem maneiras diferentes de fazer isso. Se tiver utilizado o DAO no passado e só agora estiver aprendendo o ADO, talvez você se pergunte legitimamente por que deve mudar isso. Uma razão é que o mecanismo Jet não estará presente para sempre. O Microsoft Access é modular. Parcialmente é um conjunto de ferramentas de desenvolvimento, fornecendo ao usuário meios de projetar p rojetar tabelas, consultas, formulários, relatórios e código VBA. Empacotado com todas essas ferramentas está um mecanismo de bancos de dados que realmente gerencia o armazenamento, a modificação e a recuperação dos dados. O mecanismo de banco de dados do Access é chamado de mecanismo Jet , e o DAO é otimizado para trabalhar com bancos de dados Jet. Se você nunca fosse lidar com bancos de dados diferentes daqueles que usam o Jet, haveria uma pequena razão para utilizar qualquer biblioteca de objetos diferente da biblioteca DAO. O DAO é otimizado para o Jet e é mais eficiente trabalhar com o Jet do que com outros modelos como ADO.
Capítulo 8 – Abrindo bancos de dados
189
Mas a Microsoft tem se distancia distanciado do do Jet — não, porém, do Access — desde pelo menos a versão 2000 do Office. Office. A Microsoft desenvolveu desenvolveu um produto mais novo, o SQL SQL Server, Server, que foi planejado para fornecer um mecanismo mais robusto e tratar muito mais tráfego de usuário do que o Jet. O próprio Access é capaz de utilizar outros mecanismos de bancos de dados. Por exemplo, você pode utilizar utilizar o Access como como um front-end front-end para para um back-end back-end SQL SQL Server. Server. Você Você continua continua a projetar suas tabelas, consultas, formulários, relatórios e código no Access, que então transfere o gerenciamento gerenciamen to de dados para o SQL Server em vez de para o Jet. Mas o DAO é compa comparati rativamen vamente te inefi ineficaz caz para trab trabalha alharr com o SQL Serve Serverr ou, nesse sent sentido, ido, com fontes de dados diferentes daquelas fontes gerenciadas pelo pe lo Jet. O ADO, por outro lado, trata efetivamente efetivame nte uma ampla variedade de fontes de dados, incluindo SQL Server. Isso não pretende ser um argumento a favor ou contra o uso do DAO. É apenas para lembrar que ao utilizar o ADO, você deve especificar um provedor para a conexão. Eis um exemplo: Dim cnConnectToLedger As New ADODB.Connection cnConnectToLedger.Provider = "Microsoft.Jet.OLEDB.4.0"
Você poderia utilizar essa sintaxe para especificar que a conexão é para Você para utilizar um banco de dados do Access que gerencia seus dados com Jet. Se estivesse se conectando conectan do ao SQL Server, você utilizaria algo como isso: Dim cnConnectToLedger As New ADODB.Connection cnConnectToLedger.Provider = "SQLOLEDB.1"
A especificação especificação real do provedor dependeria da versão do provedor que você você instalou. instalou.
Especificando a fonte de dados O objeto Connection tem uma propriedade, ConnectionString, que você pode utilizar para informar o ADO sobre onde encontrar a fonte de dados, bem como com o as informações sobre como abri-la. Eis um exemplo simples: Dim cnConnectToLedger As New ADODB.Connection With cnConnectToL cnConnectToLedge edger r .Provider = "Microsoft.Jet.OL "Microsoft.Jet.OLEDB.4.0" EDB.4.0" .ConnectionString .ConnectionStrin g = _ "Data Source=C:\Documents and Settings\GL.mdb" End With
Criando conexões mais complicadas A ConnectionString é uma propriedade de amplo espectro e exemplifica a flexibilidade do ADO: você pode utilizá-la para especificar aspectos da conexão que também podem ser especificados em outra parte. O exemplo dado anteriormente especificou o provedor utilizando a propriedade Provider. Também poderia ter sido administrada dessa maneira: Dim cnConnectToLedger As New ADODB.Connection
190
Gerenciando dados com o Microsoft Excel
With cnConne cnConnectToL ctToLedge edger r .ConnectionString .ConnectionStrin g = _ "Provider = Microsoft.Jet.OL Microsoft.Jet.OLEDB.4.0;" EDB.4.0;" & _ "Data Source=C:\Documen Source=C:\Documents ts and Settings\GL.mdb" End With
Observe que o provedor é especificado dentro da string de conexão em vez de em sua própria propriedade.
Criando strings de conexão com arquivos UDL Se quiser, você pode obter ajuda na criação de uma string de conexão. A abordagem conta com a criação de um arquivo de universal data link ou UD UDL L. O arquivo é salvo como texto, então você pode abri-lo e visualizá-lo visualizá-l o com o Bloco de notas, Word Word ou qualquer aplicação que possa ler arquivos arqui vos de texto. Você começa criando um arquivo de vinculação de dados em branco e então o abre clicando Você duas vezes nele. A caixa de diálogo Data Link Properties aparece e ajuda você na construção da vinculação. Quando terminar, terminar, as informações informações são salvas como texto, de maneira maneira muito parecida parecida com um arquivo DSN. Ao abrir o arquivo de vinculação de dados com um leitor de texto, você verá a string de conexão e poderá copiá-la e colá-la no código VBA onde estiver utilizando uma string de conexão. Mais explicitamente, explicitamente, siga esses passos: passos: 1.
Inicie o Bloco de notas. Sem digitar nada, escolha Salvar como a partir do menu Arquivo. Na caixa Nome do arquivo, digite algo como Nova vinculação de dados.udl. O nome do arquivo é irrelevante (embora ajude a torná-lo expressivo); o que importa é que o arquivo tenha a extensão .udl . Feche o Bloco de notas.
2.
Utilizand o o Windows Explorer ou Meu Computador, Utilizando Computad or, navegue para o local em que salvou salvo u o arquivo no passo 1. Localize o arquivo e dê um clique duplo em seu ícone. A janela Data Link Properties se abre. Clique na guia Providers, mostrada na Figura 8.2.
Figura 8.2 Todos os provedores disponíveis para sua estação de trabalho aparecem na caixa de listagem.
Capítulo 8 – Abrindo bancos de dados 3.
191
Selecione o provedor que você quiser da caixa de listagem OLE DB Providers. Para um banco de dados do Access, selecione Microsoft Jet 4.0 OLE DB Provider ou (para mais opções) selecione Microsoft OLE DB Provider for ODBC Drivers. Para um banco de dados do SQL Server, selecione Microsoft Micros oft OLE DB Provider for SQL Server. Serv er. Para uma pasta de trabalho do Excel, selecione Microsoft OLE DB Provider for ODBC Drivers. Depois de selecionar um provedor, provedor, clique na guia Connection ou no botão Next. A guia Connection aparece (veja a Figura 8.3).
Figura 8.3 Essa guia Conexão é baseada na seleção Microsoft Jet 4.0 OLE DB Provider na guia Provedor.
4.
Clique o botão da caixa à direita no passo 1. Isso permite navegar até a localização de um banco de dados do Access.
A T O N
O conteúdo da guia Connection varia, dependendo do provedor que você selecionou na guia Provider. O provedor ODBC Drivers, por exemplo, permite que você forneça um DSN.
O nome de usuário fornecido por padrão no passo 2 é Admin. Esse também é o nome de usuário padrão para abrir um banco banc o de dados do Access. O estado normal norm al é para o usuário Admin não ter nenhuma senha. Quando o usuário Admin tiver uma senha, o Access exige que o usuário forneça um nome de usuário e senha reconhecidos. reconhecidos. Forneça um nome de usuário e senha se o banco de dados tiver sido protegido e o usuário Administrador tiver uma senha. Se você não fornecer uma senha ou se marcar a caixa de seleção Senha em branco, o arquivo de texto resultante mostrará, entre outras coisas, Password="". Se fornecer uma senha e marcar a caixa de seleção Allow saving password, a senha será salva no arquivo de texto, facilmente lido por qualquer pessoa que queira apontar o Bloco de notas para ele. (Você (Você receberá um aviso se tentar tent ar isso.)
5.
6.
Clique no botão Test Test Connection. Se a fonte de dados puder ser aberta com as informações que você forneceu, verá uma caixa de mensagem mens agem que diz que Test connection succeeded. Se
192
Gerenciando dados com o Microsoft Excel
por alguma razão a conexão tiver falhado, você verá uma mensagem sobre esse efeito, junto com algumas breves informações sobre a razão da falha. Clique na guia Advanced. Advanced . Para bancos de dados Jet e alguns outros provedores, como ODBC, você pode verificar níveis de permissão que se aplicarão a essa essa conexão conexão (veja (veja a Figura 8.4 ).
7.
Figura 8.4 Uma permissão de acesso adicional, Write, está oculta na caixa de listagem até que você role para baixo até ela.
8. Marque
a caixa de seleção de uma ou mais permissões. Se o banco de dados tiver sido protegido, certifique-se certifiq ue-se de que as permissões que você atribuiu a essa conexão não conflitam com as atribuídas no banco de dados para o nome de usuário na guia Connection.
Se quiser, clique na guia All na caixa de diálogo Data Link Properties. Lá, como mostrado na Figura 8.5, você verá todas as propriedades que podem ser configuradas para o provedor que você escolhe escolheu: u: os compon componentes entes dessa dessa lista lista depend dependem em de qual qual provedor provedor você escol escolheu. heu. Você Você normalmente não deve achar necessário editar uma propriedade. Mas se você achar necessário, ne cessário, pode editar cada uma delas a partir dessa guia selecionando-a e clicando no botão Edit Value. Value.
9.
10.
Clique no botão OK sempre que terminar. termi nar.
11. Agora
reinicie reinic ie o Bloco de notas ou qualquer outra aplicação aplicação que você utilizou para criar criar o arquivo UDL da primeira vez. Escolha Abrir do menu Arquivo, navegue até a localização onde você o salvou, ajuste a lista suspensa Arquivos do tipo para Todos os arquivos e abra o arquivo UDL. Você verá algo assim: [oledb] ; Everything after this line is an OLE DB initstring Provider=Microsoft.Jet.OLEDB.4.0;Password=""; _ Data Source=C:\Documents and Settings\Owner\My Documents\GL.mdb; _ Persist Security Info=True
Capítulo 8 – Abrindo bancos de dados
193
Figura 8.5 Quaisquer dessas propriedades, incluindo as mais abaixo na lista, podem ser configuradas na string de conexão.
Utilizando o código O código desenvolvido na seção anterior precisa de alguns comentários: • Você pode nomear nomear o arquivo arquivo UDL em uma uma string de conexão, como faz com um DSN. DSN. Sua string de conexão talvez se pareça com isto: Dim cnConnectToLedger As New ADODB.Connection With cnConnectTo cnConnectToLedge Ledger r .ConnectionString = "File Name=C:\Documents and Settings\_ Owner\My Documents\MyUDL. Documents\MyUDL.udl" udl" End With
• A primei primeira ra linha linha do do arquiv arquivo o UDL, UDL, [OLE DB], é uma declaraçã declaração o de seção. A segunda linha, começando com o ponto-e-vírgula, é um comentário. Se você utilizar o arquivo UDL diretamente, nomeando-o em uma string de conexão, certifique-se de que tanto a declaração de seção quanto a linha de comentário estejam no local apropriado — se qualquer uma não estiver,, você receberá uma mensagem de erro The file is not a valid compound file . estiver • Você pode utilizar a string de conexão conexão do arquivo arquivo UDL indiretamente, indiretamente, colocando colocando seu conteúdo em sua string de conexão. Então, sua string de conexão talvez se pareça com isto: Dim cnConnectToLedger As New ADODB.Connection With cnConnectTo cnConnectToLedge Ledger r .ConnectionString="Provider=Microsof .ConnectionString ="Provider=Microsoft.Jet.OLEDB.4.0;" t.Jet.OLEDB.4.0;" & _ "Data Source=C:\Documents and Settings\Owner\My Documents\GL.mdb;" & _ "Persist Security Info=False" End With
194
Gerenciando dados com o Microsoft Excel
• Se colar a string de conexão do arquivo UDL em uma string de conexão real em seu código VBA, certifique-se de substituir isso Password="";
por isso Password=;
ou exclua o argumento Password completamente para evitar confundir o interpretador quanto a onde termina a string de conexão.
Abrindo a conexão Com a conexão declarada, você pode abri-la onde que que faça sentido para seu código VBA. Depois que estiver aberta, você pode começar a mover dados de um lado a outro através da conexão. Essa é outra área que destaca a flexibilidade em ADO: como verá, é até mesmo possível estabelecer a conexão e abri-la simultaneamente. Dependendo de suas preferências, você pode fornecer a string de conexão para o objeto Connection diretamente, como mostrado na seção anterior. Ou pode fornecê-la como um argumento ao método Open do objeto Connection, como mostrado nas seções a seguir.
Utilizando o método Open sem argumentos Se já forneceu as informações de conexão necessárias para o objeto Connection, você pode simplesmente invocar o método Open do objeto Connection. Por exemplo Dim cnConnectToLedger As New ADODB.Connection Dim strConnectToLedger As String strConnectToLedger = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=C:\Documents and Settings\Owner\My Documents\GL.mdb;" & _ "Persist Security Info=False" With cnConnectToLedger .ConnectionString = strConnectToLedger .Open End With
Essa abordagem é útil quando for parte de uma sub-rotina que você chama repetidamente, utilizando outra sub-rotina para enviar a string de conexão como um argumento: Sub Dim Dim For
GetConnectionStrings strConnectData As String i as integer i = 1 to 10 strConnectData = Sheets("LedgerAccounts").Cells(i,1) MakeTheConnection (strConnectData) Next i End Sub Sub MakeTheConnection(strConnectData As String) Dim cnConnectToLedger As New ADODB.Connection
Capítulo 8 – Abrindo bancos de dados
195
With cnConnectToLedger .ConnectionString = strConnectData .Open End With 'Instruções que utilizam a conexão entram aqui End sub
A passagem de um argumento de uma sub-rotina para outra é uma prática comum em todas as linguagens de programação, incluindo VBA. No exemplo precedente, a sub-rotina denominada GetConnectionStrings pega uma série de 10 strings da planilha denominada LedgerAccounts por meio de um loop For-Next. Depois que o loop obtiver uma string da planilha, passa a string à sub-rotina chamada MakeTheConnection. Essa sub-rotina utiliza a string que recebe como o valor atribuído à string de conexão do objeto Connection. Dessa maneira, a sub-rotina principal GetConnectionStrings pode obter uma série de strings, cada uma contendo informações de conexão diferentes e utilizando-as para direcionar a conexão a diferentes fontes de dados. Por exemplo, se os dados do livro razão forem armazenados em bancos de dados diferentes, as localizações de fonte na planilha talvez apareçam como mostrado na Figura 8.6. Na primeira vez que o loop executa, as informações na célula A1 são obtidas e passadas para MakeTheConnection. Na segunda passagem pelo loop, as informações em A2 são obtidas e passadas. A cada vez, MakeTheConnection configura uma conexão diferente com base na string de conexão que recebe de GetConnectionStrings.
Especificando a string de conexão no método Open Se preferir, você pode especificar a string de conexão como parte do próprio método Open. O método Open pode ter de zero a quatro argumentos. A seção anterior mostrou como você pode utilizá-lo sem argumentos; naturalmente, naquele caso, o próprio objeto Connection já deve ter as informações sobre onde os dados estão localizados. A sintaxe do método Open é Connection.Open ConnectionString, UserID, Password, Options
Figura 8.6 Redefinindo sua string de conexão, você pode utilizar o mesmo objeto Connection repetidamente.
196
Gerenciando dados com o Microsoft Excel
Cada um de seus argumentos é opcional. Suponha que você precise abrir os bancos de dados com um ID de usuário diferente. Talvez coloque o ID de usuário na própria string de conexão ou em alguma outra localização — talvez perto da string de conexão na planilha, como mostrado na Figura 8.7. Figura 8.7 Se estiver suficientemente confiante em seus planos de segurança, você poderia especificar senhas junto com IDs de usuário.
Com esse tipo de configuração, seu código VBA talvez seja como segue: Sub Dim Dim For
GetConnectionStrings strConnectData As String, strPassword as String i as integer i = 1 to 10 strConnectData = Sheets("LedgerAccounts").Cells(i,1) strPassword = Sheets("LedgerAccounts").Cells(i,2) MakeTheConnection strConnectData, strPassword Next i End Sub Sub MakeTheConnection(strConnectData As String, strPassword As String) Dim cnConnectToLedger As New ADODB.Connection cnConnectToLedger.Open strConnectToLedger, strPassword 'Instruções que utilizam a conexão entram aqui End sub
Aqui o código não pega uma, mas duas strings da planilha: uma string de conexão e uma senha. Ambas são passadas para a sub-rotina MakeTheConnection e utilizadas como argumentos para o método Open do objeto Connection. Há dois aspectos desse código a lembrar: • Ao fornecer a string de conexão como parte do método Open, você não precisa fazer nada especial com o objeto Connection a não ser abri-lo. • Lembre-se de que pode fornecer um ID de usuário como parte da própria string de conexão. Utilizando os argumentos UserID e (se necessário) Password para o método Open, você não precisa fornecê-los com a string de conexão. Se o fizer, porém, os valores fornecidos como argumentos do método Open sobrescrevem quaisquer valores fornecidos dentro da string de conexão.
Capítulo 8 – Abrindo bancos de dados
A T O N
197
Além do objeto Connection, o ADO tem dois outros objetos importantes, o objeto Recordset e o objeto Command. Ambos podem ser utilizados para estabelecer conexões e eles têm outras propriedades fornecidas pelo objeto de Connection. Os objetos Recordset e Command são discutidos nos capítulos 10 e 11.
Abrindo um banco de dados com o DAO Até aqui, este capítulo focalizou a abertura de um banco de dados utilizando o ADO. Se não tiver acesso ao ADO ou se estiver abrindo um banco de dados que utiliza o mecanismo de bancos de dados Jet (como uma questão prática, isso significa bancos de dados do Microsoft Access), você deve considerar o uso do DAO. Como mencionado anteriormente neste capítulo, o DAO é otimizado para o Jet e você obterá melhor desempenho utilizando DAO em bancos de dados Jet do que utilizando ADO. (De fato, a delta não é grande.) E a sintaxe do DAO é idêntica à sintaxe do ADO sob vários aspectos, então quando chegar a hora de mudar seu código utilizando DAO para utilizar ADO, a alteração não será tão dolorida quanto talvez tenha sido mudar de, digamos, Lotus 1-2-3 para Excel.
Declarando um banco de dados do DAO Você começa utilizando DAO para acessar um banco de dados Jet estabelecendo uma referência ao DAO, assim como fez para o ADO. Com o VBE ativo, selecione Referências do menu Ferramentas. Role para baixo até encontrar a biblioteca de objetos DAO, marque sua caixa de seleção e clique em OK (veja a Figura 8.8). Com a referência estabelecida, você declara uma variável de objeto para representar o banco de dados. Por exemplo Dim dbGLDatabase As Database
Figura 8.8 A última versão do DAO depende de qual versão do Office você instalou.
Você normalmente não utilizaria a palavra-chave New na declaração, como você geralmente utilizaria ao declarar uma conexão ADO. A razão é que em ADO uma conexão realmente inicia como uma nova: ela não existe antes de seu código começar a executar (embora sua fonte de dados normalmente exista).
198
Gerenciando dados com o Microsoft Excel
Em DAO, não há nenhum objeto que corresponda diretamente a uma conexão ADO. (Um objeto Connection do DAO é uma referência a um banco de dados; em outras palavras, é um objeto Database.) Você aponta seu código em um banco de dados existente e abre. Eis um exemplo típico: Dim dbGLDatabase As DAO.Database Dim strDBPath As String strDBPath = "C:\Documents and Settings\Owner\My Documents\GL.mdb" Set dbGLDatabase = OpenDatabase (strDBPath)
A sintaxe completa da instrução que configura o objeto de banco de dados é Set dbObject = Workspace.OpenDatabase (Name, Options, ReadOnly, Connect)
onde • dbObject é uma variável de objeto declarada como um Banco de dados de DAO. • Workspace é um objeto que define como seu código interage com a fonte de dados. Utiliza o mecanismo de bancos de dados Jet ou ODBC Direct — um método de interagir com fontes de dados ODBC que desvia o mecanismo Jet. Em geral, se você estiver utilizando DAO, é porque quer utilizar um banco de dados do Access e o objeto Workspace portanto utilizará Jet. Workspace é um argumento opcional e na maioria dos casos você não precisa utilizá-lo. • O argumento Name para o método OpenDatabase especifica o caminho para e o nome do banco de dados que está sendo aberto. • O argumento Options, para espaços de trabalho do Jet, é True (que significa que o banco de dados é aberto no modo exclusivo) ou False (o padrão, significando que o banco de dados é aberto em modo compartilhado). No modo exclusivo, nenhum outro usuário pode subseqüentemente abrir o banco de dados. Se quiser modificar, digamos, a estrutura de uma tabela, você não receberá uma mensagem dizendo que você não pode porque outro usuário já a abriu: ninguém mais pode entrar. Mas se outro usuário tiver o banco de dados aberto antes de você tentar abri-lo no modo exclusivo, você receberá uma mensagem de erro de tempo de execução a esse efeito. • O argumento ReadOnly também aceita um valor True ou False. Se False, o banco de dados é aberto para acesso de leitura e gravação. Se True, é aberto somente para leitura. Abrir um banco de dados como somente leitura é ocasionalmente útil em circunstâncias especiais. Entretanto, configurar permissões para bancos de dados ou proteger por senha projetos e pastas de trabalho do Excel normalmente é uma estratégia melhor. • O argumento Connect é quase sempre utilizado em conjunção com fontes de dados ODBC não-Jet. Deixando o usuário localizar o banco de dados
Às vezes, você não sabe qual banco de dados seu código deve abrir. Isso costuma ocorrer quando seu código se destina a suportar um usuário cuja familiaridade com o uso de banco de dados e gerenciamento de dados é limitada a saber onde o banco de dados está armazenado.
Capítulo 8 – Abrindo bancos de dados
199
O VBA fornece um método, GetOpenFilename, que permite deixar o usuário navegar pelo banco de dados que ele quiser abrir. O método é um membro do objeto Application.
A T O N
O método GetOpenFilename não está limitado ao uso com bancos de dados — você pode utilizá-lo em seu código VBA para deixar o usuário identificar pastas de trabalho do Excel, documentos do Word, apresentações do PowerPoint, até executáveis — qualquer arquivo cujo caminho e nome seu código precisa para acessar. Além disso, você pode utilizá-lo no código VBA independentemente da aplicação host; por exemplo, você pode utilizálo no código VBA que você está escrevendo para Access ou Word.
Em sua forma simples, a utilização de GetOpenFilename é meramente Dim strFileToOpen As String strFileToOpen = Application.GetOpenFilename
A Figura 8.9 mostra o que o usuário vê quando a segunda instrução executa. Figura 8.9 O usuário vê o conteúdo da pasta a partir da qual ele abriu um arquivo pela última vez.
Se, na Figura 8.9, o usuário escolhesse selecionar o banco de dados do Access chamado Compact Indicators.mdb e depois clicasse em OK, a variável strFileToOpen receberia algo como este valor: C:\Technical Documentation\Compact Indicators.mdb
O valor retornado pelo método GetOpenFilename inclui seu caminho. Lembre-se de que quando seu código confronta o usuário com uma caixa de diálogo GetOpenFilename, nenhum arquivo realmente é aberto quando ele clica em OK. Tudo o que acontece é que o nome e caminho do arquivo selecionados pelo usuário são capturados e armazenados em uma variável. Se o arquivo deve ser aberto, cabe a seu código fazê-lo. E se, em vez disso, o usuário clicar no botão Cancelar? Nesse caso, a variável que resulta do método assume o valor False. Para impedir que seu código tente abrir um arquivo chamado False, você precisa fornecer uma maneira de o código se recuperar do erro.
200
Gerenciando dados com o Microsoft Excel
Cuidando do seu usuário Se quiser, você pode exercer mais controle sobre o que o método GetOpenFilename exibe para o usuário. Suponha que você saiba que o usuário sempre irá querer abrir um dos bancos de dados localizado em uma pasta em particular. Nesse caso, você pode poupar alguns passos do usuário por meio de um código como este: Dim strFileToOpen As String Dim strOldFolder As String Dim dbGLDatabase As Database strOldFolder = CurDir ChDir "C:\" strFileToOpen = Application.GetOpenFilename If strFileToOpen = "False" Then MsgBox "No file was selected." Exit Sub End If Set dbGLDatabase = OpenDatabase (strFileToOpen) ChDir strOldFolder
Duas variáveis string são declaradas: uma para armazenar o caminho e o nome do arquivo a abrir, e uma para armazenar o caminho do diretório atual. A função CurDir é utilizada para armazenar o diretório atual. Então, como você sabe que o usuário vai querer abrir um arquivo na raiz da unidade C:, você utiliza a instrução ChDir para mudar o diretório padrão para C:\. O método GetOpenFilename então retorna o nome do arquivo que o usuário quer abrir. Caso o usuário clique no botão Cancelar, o bloco If inicia: o usuário vê uma mensagem para confirmar que nenhum arquivo foi selecionado e a sub-rotina é encerrada. Se o usuário clica em OK, o caminho e o nome do arquivo selecionado são armazenados em strFileToOpen, que é utilizada pelo método OpenDatabase para atribuir o arquivo à variável de objeto dbGLDatabase . Por fim, o diretório padrão é reinicializado para a localização em utilização antes de ser mudado para C:\. Essa é simplesmente uma questão de boas práticas de codificação.
A C I D
É uma boa idéia colocar esse tipo de código em sua própria sub-rotina ou função. Se uma função,você então poderia configurar seu valor como o nome do arquivo selecionado ou como False se o usuário cancelasse. Se uma subrotina, você poderia aceitar uma variável de string como um argumento; o código atribui o nome de arquivo ou False à variável, que é então (por padrão) retornada ao procedimento de chamada. De qualquer maneira, o procedimento de chamada sabe o que fazer: continua normalmente em resposta a um caminho válido e nome de arquivo ou impede os resultados incorretos e pára o processamento em resposta a False.
Filtrando tipos de arquivo para seu usuário Você pode até arrumar mais ainda as coisas se quiser, especificando o tipo de arquivo que o usuário pode selecionar na caixa de diálogo GetOpenFilename. strFileToOpen = Application.GetOpenFilename _ ➥("Access databases (*.mdb), *.mdb")
Capítulo 8 – Abrindo bancos de dados
201
Essa instrução utiliza o argumento FileFilter do método GetOpenFilename. Ela limita os arquivos mostrados na janela principal da caixa de diálogo GetOpenFilename. Como utilizado anteriormente, essa instrução mostra somente os arquivos com a extensão .mdb. Ela também restringe a arquivos MDB os tipos de arquivos mostrados na lista suspensa Arquivos do tipo. Se quiser dirigir a atenção do usuário para arquivos MDB, mas admitir a possibilidade que o usuário talvez queira especificar outro tipo de arquivo, você pode colocar dois (ou mais) tipos de arquivo no argumento FileFilter. O seguinte exemplo dá ao usuário acesso a arquivos MDB e, secundariamente, a qualquer tipo de arquivo, através da utilização da lista suspensa Arquivos do tipo. strFileToOpen = Application.GetOpenFilename _ ("Access databases (*.mdb), *.mdb,All files (*.*),*.*")
A Figura 8.10 mostra o efeito dessa especificação do argumento FileFilter. Figura 8.10 Observe que somente arquivos MDB estão visíveis. Todos os arquivos tornam-se visíveis quando o usuário escolhe All Files (*.*).
O método GetOpenFilename tem três outros argumentos que você talvez ache útil: •
FilterIndex . Se você tiver especificado mais de um filtro no argumento FileFilter , FilterIndex
identificaria qual é para utilizar. O padrão é 1, que significa que o primeiro filtro é utilizado. Um valor de 2 utilizaria o segundo filtro primeiro; o usuário ainda pode selecionar o primeiro filtro da lista suspensa Arquivos do tipo: strFileToOpen = Application.GetOpenFilename _ ("Access databases (*.mdb), *.mdb,All files (*.*),*.*",2)
•
Title.
Utilize esse argumento para fornecer o texto a ser exibido na barra de título da caixa de diálogo. O seguinte fragmento de código mostra arquivos MDB por padrão e coloca Select a database na barra de título: strFileToOpen = Application.GetOpenFilename _ ("Access databases (*.mdb), *.mdb,All files (*.*),*.*",1, _ "Select a database")
202 •
Gerenciando dados com o Microsoft Excel
MultiSelect. Esse argumento permite ao usuário selecionar mais de um
arquivo. A variável para a qual o resultado é atribuído deve ser declarada como Variant porque GetOpenFilename retornará uma matriz (mesmo se a matriz contiver somente um elemento). Você então pode fazer o loop através da matriz para obter os nomes de arquivo: Dim varFileArray As Variant, i As Integer varFileArray = Application.GetOpenFilename _ ("Access databases (*.mdb), *.mdb,All files (*.*),*.*",1, _ "Select one or more databases", ,True) For i = 1 To Ubound(varFileArray) ActiveSheet.Cells(i,1) = varFileArray(i) Next i
Lidando com bancos de dados Jet protegidos Este capítulo já discutiu o procedimento com IDs de usuário e senhas em fontes de dados ODBC Direct não-Jet. Isso normalmente é administrado em uma string de conexão, se essa string for fornecida como o valor da propriedade ConnectString do objeto Connection ou como um argumento a seu método Open. Como mencionado na seção “Abrindo um banco de dados com o DAO”, na página 197, o DAO não oferece um objeto Connection que seja diferente de um objeto Database. Além disso, bancos de dados Jet são protegidos de uma maneira muito diferente de, digamos, um banco de dados SQL Server. Esta seção fornece uma visão geral das maneiras como os bancos de dados Jet — especificamente, bancos de dados do Access — são protegidos, bem como o código que permite lidar com eles. Um banco de dados do Access tem dois métodos diferentes pelos quais você pode impedir um usuário de realizar ações não-autorizadas: uma senha de banco de dados e segurança no nível de usuário.
Utilizando senhas de banco de dados do Access Configurar uma senha de banco de dados é sem dúvida o mais fácil dos dois métodos e também mais fácil de ser derrotado. Para proteger um banco de dados do Access por meio de uma senha, você segue estes passos: 1. Em princípio, o banco de dados que você quer proteger deve estar fechado. 2. Comece iniciando o Access. Não inicie dando clique duplo no ícone do banco de dados que você quer abrir. Em vez disso, inicie o Access a partir do menu Iniciar ou de algum outro atalho que tenha como alvo a aplicação Access. 3. Se o Access exibe uma janela que permite abrir um banco de dados em branco ou criar um banco de dados ou abrir um arquivo existente, dispense a janela. 4. Escolha Arquivo, Abrir . Na caixa de diálogo Abrir do Access, navegue até a localização do banco de dados que você quer proteger. Quando encontrar o banco de dados, selecione-o. 5. No canto inferior direito da caixa de diálogo Abrir, clique na seta no lado direito do botão Abrir para exibir a lista drop-down mostrada na Figura 8.11.
Capítulo 8 – Abrindo bancos de dados
203
Figura 8.11 Também é necessário abrir um banco de dados protegido em modo exclusivo se você quiser invalidar uma senha de banco de dados.
6. Clique em Abrir exclusivo para abrir o banco de dados no modo exclusivo. 7. No menu Ferramentas, selecione Segurança. Então selecione Definir senha do banco de dados no menu em cascata. 8. Digite e verifique uma senha na caixa de diálogo Definir senha do banco de dados, como mostrado na Figura 8.12. Então clique em OK . Se precisar utilizar o DAO para abrir um banco de dados que está sendo protegido dessa maneira, você tem de utilizar uma sintaxe semelhante a: Dim dbGLDatabase As DAO.Database Dim strDBPath As String strDBPath = "C:\Documents and Settings\Owner\My Documents\GL.mdb" Set dbGLDatabase = OpenDatabase (strDBPath, False, False, _ "MS Access;PWD=Dismal")
Figura 8.12 Como de costume, é melhor escolher uma senha que consista em uma mistura de letras, números e caracteres especiais.
Para realizar a mesma coisa com o ADO, você utilizaria um código como este: Dim cnGeneralLedger As New ADODB.Connection Dim rsGL As New ADODB.Recordset Dim strDBPath As String strDBPath = "C:\Documents and Settings\Owner\My Documents\GL.mdb"
204
Gerenciando dados com o Microsoft Excel
With cnGeneralLedger .Provider = "Microsoft.Jet.OLEDB.4.0" .Properties("Data Source") = strDBPath .Properties("Jet OLEDB:Database Password") = "Dismal" .Open End With
Isso ilustra um aspecto conveniente das conexões ADO. Depois de ter estabelecido o provedor da conexão, a coleção Properties da conexão é preenchida com propriedades que o Provedor fornece. Aqui, o mecanismo Jet fornece uma propriedade Data Source (assim como o SQLOLEDB.1) e uma propriedade Jet OLEDB:Database Password (o SQLOLEDB.1 não fornece uma propriedade de senha de banco de dados qualificada pelo Jet OLEDB ). Embora essas propriedades não sejam membros das bibliotecas VBA ou ADO, elas são automaticamente fornecidas pelo provedor.
Utilizando bancos de dados protegidos com segurança no nível do usuário Um método de proteger um banco de dados do Access muito mais forte (embora não à prova de falha) que uma senha de banco de dados é por meio de segurança no nível de usuário. Antes de você estar em uma posição de entender o código DAO ou ADO que abre bancos de dados do Access que têm segurança no nível de usuário instalada, você precisa entender como essa segurança é organizada. Ao instalar o Microsoft Access em um computador, um dos arquivos que é instalado é chamado de System.mdw. Sua localização depende da versão do Office que você está utilizando, então talvez precise procurá-lo. O System.mdw é chamado de arquivo de informações de grupo de trabalho e seu propósito é armazenar as informações sobre • Os nomes de usuários que podem abrir um banco de dados Jet e as senhas que os autenticam. • Os nomes de grupos de usuários que podem abrir um banco de dados Jet. • Quais usuários pertencem a quais grupos. Se ninguém tiver modificado o arquivo System.mdw (e você saberia se você tivesse feito isso), há somente um usuário no arquivo de informações do grupo de trabalho e esse usuário chama-se Administrador . Administrador é o usuário padrão de um banco de dados do Access. A qualquer momento que você abrir um banco de dados do Access, o Access assume que você é o Administrador, a menos que você informe algo diferente.
A T O N
Não se engane com o termo Administrador . Embora o nome conote a posse de privilégios principais,o usuário Administrador em um banco de dados do Access protegido tem, em geral, relativamente poucos privilégios. Quando um usuário real abre um banco de dados protegido, esse usuário é considerado o usuário Administrador padrão. Como um banco de dados foi protegido por uma razão ou outra, é comum evitar que o usuário ocasional exclua tabelas ou renomeie consultas ou realize outras ações que prejudicariam a funcionalidade do banco de dados. Portanto,o Administrador com freqüência está restrito a fazer não muito mais que ler os dados.
Capítulo 8 – Abrindo bancos de dados
205
Depois de proteger um banco de dados com segurança no nível de usuário, o processo de abertura depende de uma senha ter sido ou não atribuída ao usuário Administrador padrão. Se o Administrador tiver uma senha, o Access exibe a caixa de diálogo mostrada na Figura 8.13 quando qualquer pessoa tentar abrir o banco de dados protegido.
Figura 8.13 O Access mostra o nome do usuário mais recente para fornecer um. Digite sobre o nome padrão se necessário.
A menos que esteja munido de um profundo senso de determinação, alguma experiência e algumas ferramentas, você não passará além da caixa de diálogo mostrada na Figura 8.13 sem um nome e uma senha reconhecidos. Isso significa que se quiser abrir o banco de dados por meio do DAO (ou mesmo do ADO), você precisará fornecer o nome e a senha no código. Este capítulo discutirá como fazer isso, mas primeiro algumas informações adicionais sobre a configuração da segurança no nível de usuário.
Implementando a segurança no nível do usuário em um banco de dados do Access Como você verá quando começar a utilizar o SQL Server — se já não viu — a segurança no SQL Server pode ser tanto mais forte como mais fácil de administrar do que no Access. Contudo, você provavelmente encontrará bancos de dados no Access que requerem manutenção ainda por alguns anos e até ter necessidade de bancos de dados do Access novos e protegidos.
A T O N
Grande parte das informações nesta seção e em seções subseqüentes também pode ser encontrada em um white paper na Microsoft Knowledge Base. Ele se chama SECFAQ.doc, foi escrito por Mary Chipman, Andy Baron, et al. e inclui uma quantidade considerável de boas informações e conselhos sobre proteção de bancos de dados do Access.
Estabelecendo um arquivo de informações de grupo de trabalho Antes de poder iniciar a segurança no nível de usuário, você precisará de um arquivo de informações de grupo de trabalho. Se, como é provavelmente o caso, você estiver utilizando o Microsoft Office em um contexto em rede, precisará estabelecer esse arquivo em uma pasta de compartilhamento que usuários do banco de dados possam abrir. Para estabelecer um arquivo de informações de grupo de trabalho, você precisa executar Wrkgadm.exe ou o Administrador de grupo de trabalho. Qual você utiliza depende da versão do
206
Gerenciando dados com o Microsoft Excel
Office instalado em seu computador. No Office 97 e Office 2000, você executa Wrkgadm.exe, um arquivo executável normalmente localizado na subpasta System da pasta Windows apropriada ao sistema operacional que você estiver utilizando. Localize esse arquivo e dê um duplo clique para iniciá-lo. No Office 2002 e Office 2003, o administrador do grupo de trabalho é parte do próprio menu Access. Você o encontrará selecionando Segurança do menu Ferramentas e então clicando em Administrador do grupo de trabalho no menu em cascata. Quer você utilize Wrkgadm.exe ou o item de menu Administrador do grupo de trabalho, ao iniciá-lo você verá a caixa de diálogo mostrada na Figura 8.14. Clique em Criar.
Figura 8.14 A caixa de diálogo mostra o caminho e o nome do arquivo de informações de grupo de trabalho que você está utilizando atualmente.
A C I D
Um arquivo de informações de grupo de trabalho pode gerenciar as informações sobre múltiplos grupos e usuários que utilizam bancos de dados diferentes. Mesmo se você estiver gerenciando vários bancos de dados com segurança no nível de usuário,é uma boa idéia manter todos esses dados em um arquivo de informações de grupo de trabalho. Fazer isso ajuda a minimizar as dores de cabeça administrativas.
Ao clicar em Criar, a caixa de diálogo Informações do proprietário do grupo de trabalho mostrada na Figura 8.15 aparece. Figura 8.15 Você deve especificar um ID de grupo de trabalho. Esse ID não é uma senha, mas ajuda a recriar o arquivo se ele for danificado.
Certifique-se de anotar separadamente o nome, a organização e o ID do grupo de trabalho que você especificou e guarde onde você e só você possa ter acesso mais tarde. Embora muitas redes agora realizem backups abrangentes diariamente, pode levar algum tempo para restaurar um arquivo de backup. Talvez você queira recriar o arquivo rapidamente a partir do zero e para fazer isso você precisará das informações mostradas na Figura 8.15.
Capítulo 8 – Abrindo bancos de dados
207
Ao clicar em OK, a caixa de diálogo Arquivo de informações do grupo de trabalho aparece (veja a Figura 8.16).
Figura 8.16 Se digitar o caminho e o nome você mesmo, não se esqueça de dar ao arquivo uma extensão .mdw.
Depois de clicar em OK na caixa de diálogo Arquivo de informações do grupo de trabalho, você verá uma caixa de diálogo Confirmar informações do grupo de trabalho, resumindo os dados que você acabou de digitar. Se estiver correto, clique em OK para retornar para o Administrador do grupo de trabalho e então clique em OK novamente para retornar à interface do Access. Se algo estiver incorreto, clique no botão Alterar para retornar à caixa de diálogo Informações do proprietário do grupo de trabalho, onde você poderá corrigir qualquer erro.
Fazendo o Access solicitar uma senha Se estiver executando, feche o Access, reinicie-o e abra qualquer banco de dados (inclusive um novo). Como o usuário Administrador padrão não tem uma senha, você não será solicitado a fornecer uma. Escolha Segurança do menu Ferramentas e então clique em Contas de usuário e grupo. Essa é a rota para o gerenciamento de usuários e grupos: seus nomes, senhas e associações. A caixa de diálogo Contas de usuário e grupo aparece (veja a Figura 8.17). Figura 8.17 O novo arquivo de grupo de trabalho inicia com três contas padrão: o usuário Administrador e os grupos Administradores e Usuários.
Verifique se o usuário Administrador padrão aparece na lista suspensa Nome. Seu próximo passo é temporariamente atribuir uma senha ao usuário Administrador, então clique na guia Alterar senha de logon, que é mostrada na Figura 8.18.
208
Gerenciando dados com o Microsoft Excel
Figura 8.18 A conta Administrador geralmente não tem nenhuma senha, então você normalmente pula direto para a caixa Nova senha.
Digite e verifique uma senha para a conta Administrador e clique em OK.
A C I D
Eu configuro e removo a senha da conta Administrador com tanta freqüência que dou a ela apenas um único caractere e utilizo a mesma todas as vezes. Sou inclemente: faço isso unicamente para que o Access solicite meu nome e senha. Posso passar por esse processo mais rapidamente se eu nunca tiver de inventar uma nova senha para Administrador e se esta tiver somente um caractere. Não há nenhuma exposição real envolvida porque de qualquer forma o Administrador tem um mínimo de privilégios e eles são os mesmos que um usuário ocasional teria ao abrir um banco de dados protegido por esse arquivo do grupo de trabalho, e depois que eu entro, imediatamente limpo a senha do usuário Administrador.
Estabelecendo-se como um usuário Mais uma vez, escolha Ferramentas, Segurança, Contas de usuário e grupo. Na guia Usuários (consulte a Figura 8.17), clique no botão Novo para estabelecer-se como um usuário. A caixa de diálogo mostrada na Figura 8.19 será exibida. Depois de digitar seu nome e um ID pessoal, clique em OK para retornar à guia Usuários. Com seu nome na caixa Nome, clique no grupo Administradores na caixa Grupos disponíveis e clique no botão Adicionar. Isso o torna um membro do grupo Administradores. Coloque seu nome e ID pessoal na mesma localização em que você armazenou anteriormente o nome do grupo de trabalho, a organização e as informações de ID do grupo de trabalho. Novamente: se precisar restaurar o arquivo de informações do grupo de trabalho, você poderia ser capaz de fazer isso a partir de um backup de fita, mas talvez seja mais rápido fazer isso a partir do zero. Além disso, se em um momento posterior você excluir um usuário e subseqüentemente quiser restaurá-lo para a associação no arquivo de informações do grupo de trabalho, você precisará de seu ID pessoal ( não de sua senha). Feche o Access novamente e reinicie-o. Como deu uma senha à conta Administrador, você será solicitado por um nome e uma senha. Forneça seu nome, não o nome Administrador. Sua conta ainda não tem uma senha, então deixe a caixa Senha em branco.
Capítulo 8 – Abrindo bancos de dados
209
Figura 8.19 O ID pessoal que você fornece não é a senha. Ele deve conter entre 4 e 20 caracteres.
Mas atribua a você mesmo uma senha em algum ponto, assim como atribuiu uma senha à conta Administrador. Certifique-se de que iniciou o Access utilizando sua conta, escolha Ferramentas, Segurança, Contas de usuário e grupo e clique na guia Alterar senha de logon. Seu nome de usuário deve aparecer próximo à parte superior. (Se não aparecer, porque você não efetuou o logon na sua conta, feche o Access e reinicie-o, utilizando seu nome para efetuar o logon.) Forneça uma senha, verifique-a e então clique em OK.
Restringindo as permissões de Administrador Agora é hora de restringir as permissões que pertencem à conta Administrador. Isso é fácil: basta remover o usuário Administrador do grupo Administradores. Escolha Ferramentas, Segurança, Contas de usuário e grupo. Se necessário, utilize a lista suspensa na guia Usuários para selecionar o usuário Administrador. Na lista Membro de, clique em Administradores. Então clique no botão Remover. O grupo Administradores desaparece da lista Membro de e a guia Usuários deve parecer como na Figura 8.20.
Revisando e resumindo o processo Esse provavelmente parece um processo complicado e longo, e é — e você ainda não terminou porque ainda não aplicou a segurança necessária a nenhum banco de dados. Esse é um bom momento para revisar o que foi feito até agora. Você fez o seguinte: 1.
Criou um novo arquivo de informações do grupo de trabalho para conter os nomes, IDs e senhas de usuários de banco de dados, o nome e o ID do próprio grupo de trabalho, os nomes de grupos e os usuários membros dos grupos.
210
Gerenciando dados com o Microsoft Excel
2. Atribuiu uma senha ao usuário Administrador padrão. Isso força o Access a solicitar quem é
você da próxima vez que iniciar o Access.
Figura 8.20 Você não pode remover o Administrador (ou qualquer usuário) do grupo Usuários. Para fazer isso, você tem de excluir completamente a conta do usuário.
3.
Estabeleceu-se como um usuário e se associou ao grupo Administradores.
4.
Fechou e reiniciou o Access para que ele perguntasse quem é você e se identificou. Como um membro do grupo Administradores, você é capaz de remover o usuário Administrador do grupo Administradores e você fez isso.
O efeito geral de tudo isso é estabelecer um arquivo de informações do grupo de trabalho que tem você e somente você como membro do grupo Administradores e que tem tanto você como o usuário Administrador padrão como os membros do grupo Usuários. Uma nota em uma seção anterior sugeriu que você não se aprofundasse muito no nome de usuário Administrador padrão. O mesmo não é verdadeiro sobre o grupo chamado Administradores. Ele inicia com amplos privilégios. Por exemplo, é somente como membro de Administradores que você é capaz de remover o usuário Administrador do grupo Administradores. Isso destaca um aspecto fundamental de usuários e grupos. A idéia (e é uma utilizada pelo Enterprise Manager do SQL Server e outros gerenciadores de bancos de dados) é que o banco de dados provavelmente tenha muitos usuários. Cada usuário talvez tenha requisitos ligeiramente diferentes, mas a maioria dos usuários compartilha muitos requisitos. Por exemplo, a maioria dos usuários precisa ser capaz de ver as informações no banco de dados. Poucos usuários precisam ser capazes de adicionar registros e editar as informações em campos. Até menos precisam adicionar, excluir e modificar campos, tabelas e consultas. E assim por diante.
Capítulo 8 – Abrindo bancos de dados
211
Como há tantos requisitos compartilhados, é uma boa idéia configurar os grupos : um que possa ler dados mas nada mais, um que possa ler e modificar dados, outro que possa modificar tanto os dados quanto as estruturas que os contêm e assim por diante. Dada essa abordagem, é muito mais fácil administrar permissões. Em vez de atribuir permissões individualmente a cada e todo usuário, você atribui permissões a grupos e então organiza as coisas de tal modo que os usuários individuais entrem nos grupos que lhes dão as permissões necessárias. (Um usuário pode pertencer a mais de um grupo.) Se um usuário precisar de uma estrutura de permissão ligeiramente diferente daquela que seus grupos fornecem, você pode adicionar ou remover permissões específicas para esse usuário. Então, a esta altura, você estabeleceu as bases: estabeleceu a si mesmo como o administrador único e agora é hora de estabelecer a base de um banco de dados.
Protegendo o banco de dados Nessa parte do processo, você precisa se certificar de que é o usuário de registro. Você fez isso se ainda estiver executando uma instância do Access na qual se identificou como o usuário na inicialização. Se não, inicie o Access e se identifique como o usuário em resposta ao prompt de Logon do Access, fornecendo uma senha se já criou uma para si próprio. Agora abra um banco de dados que você queira tornar seguro. (No final desse processo, você terá dois bancos de dados: a versão original não-protegida e uma versão nova e protegida.) Escolha Ferramentas, Segurança, Assistente de segurança no nível de usuário. Você verá o primeiro passo do assistente, mostrado na Figura 8.21. Figura 8.21 O usuário atual deve ser membro do grupo Administradores a fim de utilizar o Assistente de segurança.
A T O N
Talvez veja algumas diferenças entre o assistente em seu computador e as figuras nesta seção. Versões diferentes do Access têm diferenças menores na aparência do assistente.
Você já estabeleceu um arquivo de informações do grupo de trabalho, com sua conta de usuário como um membro de Administradores e com a conta Administrador somente um membro de Usuários. Portanto, escolha Modificar meu arquivo de informações do grupo de trabalho atual e clique em Avançar. (Talvez você não modifique realmente o arquivo de informações do grupo de trabalho, mas queira evitar a criacão de outro.) A Figura 8.22 mostra o segundo passo do assistente .
212
Gerenciando dados com o Microsoft Excel
Figura 8.22 Os botões Selecionar tudo e Anular seleção referem-se somente a objetos na guia ativa.
Por padrão, todas as caixas de seleção para todos os objetos no banco de dados estão marcadas, o que significa que eles serão protegidos. Somente circunstâncias incomuns o fariam decidir não proteger todos os objetos no banco de dados, então você normalmente deixará as coisas como estão e clicará no botão Avançar.
A C I D
Se quiser que o assistente pule um ou mais objetos, simplesmente clique na guia apropriada e limpe a caixa de seleção apropriada.
Depois de clicar em Avançar, o assistente aparece como mostrado na Figura 8.23. Figura 8.23 Você pode aceitar os IDs de grupo propostos ou modificálos para outros valores.
Nesse passo, você rejeita (o padrão) ou aceita a criação de novos grupos em seu arquivo de informações do grupo de trabalho. Esses grupos têm permissões predefinidas e é util tê-los configurados. Por exemplo, se seu arquivo de informações do grupo de trabalho tiver o grupo fornecido pelo assistente denominado Usuários de dados novos, um membro desse grupo será capaz de visualizar os dados e inserir novos registros. A menos que um membro também pertença a um grupo com permissões mais amplas, porém, ele não será capaz de modificar ou excluir registros existentes, ou alterar o projeto de qualquer tabela, consulta, formulário ou outro objeto de banco de dados.
Capítulo 8 – Abrindo bancos de dados
213
É útil ter tal grupo já definido porque você provavelmente vai querer dar a vários usuários (Administrador, por exemplo) precisamente essas permissões. Você sempre pode excluir um ou mais desses grupos em um momento posterior, então não mantê-los aí por enquanto não causa nenhum problema. Marque suas caixas de seleção e clique no botão Avançar para visualizar o próximo passo no assistente, mostrado na Figura 8.24. Figura 8.24 Qualquer permissão que você atribua ao grupo Usuários estará disponível para qualquer pessoa que abra o banco de dados.
A T O N
Os IDs de grupo propostos são recriados toda vez que você inicia o Assistente de segurança, então você não precisa se preocupar muito com a possibilidade de outro usuário consegui-los e utilizá-los executando novamente o assistente.
Você pode atribuir algumas permissões ao grupo Usuários nesse passo, mas não é recomendado. Qualquer pessoa que abra o banco de dados como o usuário Administrador padrão é automaticamente membro do grupo Usuários e então terá qualquer permissão que você atribuiu a Usuários. Por exemplo, você talvez atribua uma permissão Ler para tabelas. Isso pode parecer suficientemente benigno, mas fazer isso também atribui uma permissão Ler Projeto, que você talvez não considere tão inofensiva. Ao clicar em Avançar, o próximo passo do assistente aparece (veja a Figura 8.25). A caixa de diálogo Contas de usuário e grupo é um pouco mais poderosa que esse passo do assistente, mas se quiser adicionar outros usuários aqui você pode fazer isso agora e atribuí-los a um ou mais grupos padrão como mostrado na Figura 8.26. Suas escolhas básicas nesse passo estão relacionadas com a facilidade de atribuição, não funcionalidade. Se até agora você adicionou somente alguns usuários, talvez somente um ou dois (Administrador não conta), é mais fácil escolher Selecionar um usuário e atribuir o usuário aos grupos. Se adicionou vários usuários e tiver somente um ou dois grupos, é mais fácil escolher Selecionar um grupo e atribuir usuários ao grupo. Em qualquer outro caso não importa muito; e de fato você não precisa fazer nenhuma atribuição neste ponto: você pode fazer isso mais tarde escolhendo Ferramentas, Segurança, Contas de usuário e grupo.
214
Gerenciando dados com o Microsoft Excel
Figura 8.25 Se você preferir não fazer isso agora, um usuário pode configurar sua própria senha registrando-se com um nome de usuário e indo para Contas de usuário e grupo.
Figura 8.26 Como esse usuário já era membro de Administradores, você não poderá removê-lo desse grupo neste passo.
Depois que tiver terminado de fazer as atribuições, clique em Avançar para alcançar o passo final do assistente (veja a Figura 8.27). Figura 8.27 Por padrão, a versão não protegida do arquivo tem uma extensão .bak.
É uma boa idéia armazenar a versão não protegida do banco de dados em um caminho em que usuários não possam alcançá-la. Uma pasta à qual somente você e seus administradores de sistema da rede têm acesso seria uma boa escolha. Navegue até a localização para obter seu caminho na caixa, edite seu nome se quiser e clique em Concluir.
Capítulo 8 – Abrindo bancos de dados
215
Ao fazer isso, o assistente cria um relatório de resumo das informações que você digitou, como mostrado na Figura 8.28. Você pode imprimi-lo se quiser. Se você simplesmente fechar o relatório, o Access oferece salvá-lo como um arquivo do tipo instantâneo [ snapshot ] (.snp), que você pode abrir e imprimir mais tarde. Você normalmente deve fazer um ou outro, para ter um registro em papel das alterações que você fez.
Figura 8.28 Se salvar o relatório como um arquivo de instantâneo, você pode localizá-lo mais tarde na mesma pasta como o banco de dados protegido.
Seu banco de dados agora foi protegido. Veja a próxima seção para obter informações sobre como abri-lo utilizando ADO.
Abrindo um banco de dados do Access protegido Depois de proteger o banco de dados, você pode abri-lo como de costume se o Administrador não tiver nenhuma senha. Se o Administrador tiver uma senha, você ainda pode abrir o banco de dados, mas precisará fornecer seu nome de usuário e senha na caixa de diálogo Logon. Talvez você também precise saber como abrir o banco de dados utilizando o código VBA. Para abrir o banco de dados com o DAO, você utilizaria código semelhante a este (não se esqueça de configurar uma referência à biblioteca de objetos DAO primeiro): Dim Dim Dim Dim Dim Dim
wkspTempWorkspace As Workspace strSecuredDB As String strUserName As String strPassword As String dbSecureDB As DAO.Database rsTableToOpen As DAO.Recordset
216
Gerenciando dados com o Microsoft Excel
Depois de declarar as variáveis necessárias, informe ao VBA onde localizar o arquivo de informações do grupo de trabalho: DBEngine.SystemDB = _ "C:\Documents and Settings\Resources.mdw"
É recomendável ser muito cuidadoso com esse código porque ele expõe o nome de usuário e sua senha. O nome de usuário deve ser um usuário identificado no arquivo do grupo de trabalho: strUserName = "Conrad Carlberg" strPassword = "KeepThisSecret"
Crie um novo espaço de trabalho temporário e acrescente-o à coleção de espaços de trabalho existente. Ele contém o nome de usuário e senha: Set wkspTempWorkspace = DBEngine.CreateWorkspace _ ("New", strUserName, strPassword) DBEngine.Workspaces.Append wkspTempWorkspace
Agora forneça o caminho e o nome do banco de dados protegido e o abra: strSecuredDB = _ "C:\NCS\Room Reservations\Resources 2003.mdb" Set dbSecureDB = DBEngine.Workspaces("New") _ .OpenDatabase(strSecuredDB) 'Código para manipular o banco de dados dadas as permissões _ do usuário entra aqui.
E feche o banco de dados: dbSecureDB.Close
O código que não é mostrado — aquele que você utilizaria para manipular dados e objetos no banco de dados — executará um pouco mais eficientemente se você utilizar a abordagem do DAO para abrir o banco de dados protegido. Mas, se preferir, pode utilizar o ADO. Este não é tão eficiente porque não é otimizado especificamente para bancos de dados Jet. Mas é muito mais curto. A advertência sobre o nome de usuário e senha expostos ainda se aplica (e não se esqueça de configurar uma referência à biblioteca de objetos ADO primeiro): Dim cnResourceConnection As New ADODB.Connection With cnResourceConnection .Provider = "Microsoft.Jet.OLEDB.4.0" .Properties("Data Source") = _ "C:\NCS\Room Reservations\Resources 2003.mdb" .Properties("Jet OLEDB:System database") = _ "C:\Documents and Settings\Resources.mdw" .Open UserId:="Conrad Carlberg", Password:="KeepThisSecret" End With 'Código para manipular o banco de dados dadas as permissões _ do usuário entra aqui. cnResourceConnection.Close
Capítulo 8 – Abrindo bancos de dados
217
Olhando para frente Este capítulo se baseia em um princípio iniciado no Capítulo 7. Esse princípio coloca você em uma posição de utilizar o VBA em conjunção com o Excel para conectar-se a bancos de dados externos. Entretanto, o VBA e o Excel não podem se conectar a bancos de dados sem ajuda. Você precisa supri-los com uma biblioteca de objetos, uma que contenha as informações sobre os objetos, propriedades e métodos localizados em um banco de dados. ADO e DAO são duas dessas bibliotecas de objeto e este capítulo mostrou como integrá-las com o código VBA e como abrir bancos de dados aplicando referências às bibliotecas. Você também leu sobre a teoria e prática de tornar seguros bancos de dados do Access. Esse material foi fornecido porque à medida que as redes de dados se tornaram cada vez mais acessíveis, os bancos de dados compartilhados se tornaram a regra — e é importante proteger um banco de dados compartilhado. Se quiser integrar com sucesso Excel, VBA e ADO/DAO no gerenciamento de um banco de dados compartilhado, você precisa saber como a segurança do banco de dados funciona. Este capítulo concluiu com alguns exemplos do uso de VBA com DAO ou ADO para legitimamente abrir um banco de dados protegido (isto é, não dominá-lo ilicitamente). O Capítulo 9, “Gerenciando objetos de banco de dados”, continua mostrando como utilizar o VBA e uma biblioteca de objetos para gerenciar tabelas e consultas de banco de dados e seus componentes, do contexto de uma pasta de trabalho Excel.
9 Gerenciando objetos de banco de dados Criando tabelas Antes de explorar como utilizar o Excel para gerenciar um banco de dados, é útil examinar seus objetos fundamentais: tabelas, joins e consultas. Este capítulo discute esses tópicos. Com essa discussão como uma base, o Capítulo 10, “Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects”, mostra como criar e gerenciar esses objetos do Excel. Utilizando o Access, comece com a janela principal de banco de dados. Você cria uma nova tabela clicando na guia ou no botão Tabelas (dependendo de sua versão) se necessário e então clicando no botão Novo. A caixa de diálogo mostrada na Figura 9.1 será exibida. Selecione o modo de Design e então clique em OK para abrir a janela onde você projetará a tabela (veja a Figura 9.2). Quando tiver a janela de design de tabela em sua tela, é hora de definir os campos que compõem a tabela. Figura 9.1 Você tem mais ferramentas à sua disposição no modo de Design que nas outras visualizações.
Figura 9.2 As propriedades na guia Geral não aparecem até que você tenha nomeado o campo.
Capítulo 9 – Gerenciando objetos de banco de dados
219
Definindo campos Você deve nomear um campo antes de especificar quaisquer de suas outras características. Há alguns princípios gerais para se ter em mente ao escolher um nome de campo.
Utilizando nomes significativos Um nome significativo é importante, ainda mais quando estiver escolhendo um nome para uma lista do Excel. Seja por meio da interface do Access ou via Excel, grande parte de seu trabalho com a tabela exigirá que forneça os nomes dos campos da tabela. Se estiver colocando um campo sobre um formulário de tela em que um usuário pode fornecer um valor ou adicionar um campo a uma consulta ou fazer referência a um campo no código VBA, sua tarefa será muito mais fácil se der um nome ao campo como SocSecNum ou FICA_Rate. Sua tarefa é muito mais difícil se tiver utilizado um nome como Field1 ou VariableB. O Excel é diferente. Com uma lista do Excel, você pode freqüentemente dar uma olhada em seus valores para saber o que representam. Ao ver um valor como 800-555-1212 ou Republicano, normalmente está bem claro o que o campo representa. (Mas nem sempre: esse número 800 é uma linha de voz ou uma linha de fax?) Ao contrário, é incomum examinar os valores de um campo durante o trabalho de projeto em um banco de dados ou gerenciamento de um banco de dados a partir da plataforma Excel. Não há um bom indício contextual e você poupará tempo se gastar alguns segundos no começo com a seleção de um nome significativo.
Vivendo com suas escolhas Em algum momento, todos pensamos, “Preciso construir a tabela agora mesmo. Voltarei e corrigirei os nomes de campo mais tarde”. Mas “mais tarde” nunca é cedo o bastante. Logo após ter construído a tabela, você cria consultas que dependem da presença de um nome de campo Field1 e VariableA. Essas consultas pretendem passar dados junto a um formulário de usuário, então o próprio formulário depende desses nomes. E em breve você achará que seu código VBA, seja em Access ou Excel, tem esses nomes espalhados por toda parte. Esses nomes equivocados criam um problema, mas você pode resolver isso alterando cada instância de nome do campo — na tabela, em qualquer consulta que referencie o campo, em qualquer formulário que referencie essas consultas e assim por diante. Você pode ter um projeto importante e propenso a erro, mas é executável porque você pode colocar as suas mãos diretamente no nome do campo independentemente de onde apareça. O problema é pior se você tiver um intervalo de dados externos em uma planilha do Excel. Então, para alterar o nome do campo, você terá de editar a consulta que preenche o intervalo de dados. Se começar alterando o nome do campo no banco de dados, o Microsoft Query não será capaz de localizá-lo; se começar alterando a referência no Microsoft Query, você será informado de que o campo não existe. A solução menos desagradável é alterar o nome do campo no banco de dados e então reconstruir o intervalo de dados externos a partir do zero.
220
Gerenciando dados com o Microsoft Excel
AutoCorreção de nome O Access 2000 introduziu um recurso chamado AutoCorreção de nome. A idéia é você poder alterar o nome de um objeto de banco de dados, como um nome de campo, e o Access automaticamente corrigirá outras referências dos objetos. Então, se em uma tabela você alterar um nome de campo de Profit para NetProfit, uma consulta que utiliza o campo será corrigida automaticamente para referenciar NetProfit. O Access preserva um log dessas alterações. Para invocar a AutoCorreção de Nome, escolha Opções do menu Ferramentas, clique na guia Geral e marque as caixas de seleção apropriadas. Mas esse recurso traz com ele seus próprios problemas. Ele torna lento o processamento, particularmente em um banco de dados com a segurança em nível de usuário ativada. Ele não altera nomes em macros ou procedimentos em VBA. E mensagens em newsgroups da Usenet (não uma fonte de informações uniformemente confiável) o culparam por outros problemas na AutoCorreção de nome — problemas que desaparecem quando o usuário a desativa.
Admito que ainda tenho em uso alguns bancos de dados que têm campos com nomes sem sentido. Até que eu estivesse pronto para encarar os fatos e alterar os nomes onde quer que eles aparecessem, eles teriam se espalhado como mofo, por consultas, formulários, tabelas vinculadas, código VBA, documentação etc. Nesse ponto, é mais eficaz viver com o problema do que corrigilo. Pelo menos esses bancos de dados são uma lembrança constante de que devo me preocupar com os nomes de campo logo no princípio.
Utilizando espaços em nomes Você pode utilizar espaços em nomes de campo; por exemplo, Retained
A T O N
Earnings .
Você não pode começar o nome de um campo com um espaço, nem incluir um ponto, um ponto de exclamação, um acento grave (o caractere embaixo do til), colchetes ou os caracteres de controle representados por ASCII de 0 a 31.
Suponha você que incorpore um espaço em branco em um nome de campo e mais tarde arraste o campo sobre um formulário de usuário para criar um controle. O Access substitui o espaço por um caractere sublinhado em quaisquer referências VBA (veja a Figura 9.3).
A T O N
Há outras maneiras de criar controles em formulários. Por exemplo, você poderia arrastar um controle de caixa de texto da Caixa de ferramentas sobre o formulário. Por padrão seria nomeado de Text12 ou Text25, dependendo de quantos controles já existissem no formulário. Mais tarde, você pode associar essa caixa de texto a um campo em uma tabela e (até que você renomeie o próprio controle) ela reteria seu nome original e padrão.
Capítulo 9 – Gerenciando objetos de banco de dados
221
Figura 9.3 O Access insere um caractere sublinhado no lugar do espaço em branco incorporado.
O nome de campo Retained Earnings é representado no código VBA do formulário como um controle chamado Retained_Earnings. O nome do campo em si não muda. Se abrir a tabela, você notará que ainda é Retained Earnings . E se examinar as propriedades do controle, verá que no formulário ele ainda é chamado de Retained Earnings . Mas em qualquer código VBA salvo com o formulário de usuário, como o código para seu evento After Update, o controle é referido pelo nome Retained_Earnings. É muito conveniente ao código VBA referenciar controles em formulários para utilizar o mesmo nome que o campo que eles representam. Suponha que esteja escrevendo código VBA que executa em resposta a um usuário que clica em um botão OK em um formulário chamado FinancialForm . Você quer realizar um cálculo baseado no valor que o usuário digitou no formulário. Você está calculando os lucros trimestrais médios, dada a cifra anual de lucros fornecida pelo usuário. Se o controle em questão for nomeado Text18, seu código talvez se pareça com isso: MeanQuarterlyEarnings = FinancialForm.Text18 / 4
Subseqüentemente, durante a revisão do código, você percebe que não sabe que tipo de lucros o controle chamado Text18 armazena: lucros retidos, lucros por compartilhamento, lucros diluídos ou qualquer outro lucro. Então, é útil o código referir-se a Retained_Earnings em vez de a Text18, mas ainda há uma fonte de confusão. O nome do seu campo tem um espaço incorporado, mas a referência VBA do controle tem um sublinhado incorporado. Isso causa problemas para o código, especialmente se você estiver escrevendo o código no Excel para gerenciar o banco de dados. Então você tem uma aplicação inteira em direção oposta dos nomes de campo.
222
Gerenciando dados com o Microsoft Excel
Tudo que realmente importa é organizar as coisas de modo que os nomes sejam consistentes. Com o tempo, você se poupará de dores de cabeça adotando uma dessas duas abordagens: • Evite utilizar separadores em nomes de campo. Um nome de campo RetainedEarnings , arrastado sobre um formulário chamado MyForm para criar um controle, é referido como MyForm.RetainedEarnings . A referência VBA é a mesma como o nome do campo. • Se quiser utilizar um separador em um nome de campo, utilize um sublinhado. Um campo chamado Retained_Earnings, arrastado sobre um formulário chamado MyForm para criar um controle, é referido como MyForm.Retained_Earnings. Novamente, a referência VBA é a mesma do nome do campo. Depois de ter determinado uma abordagem — mesmo se escolher utilizar espaços incorporados em nomes — utilize essa abordagem consistentemente. Você gastará muito menos tempo reabrindo bancos de dados e tabelas para verificar como nomeou algo.
Configurando tipos de campo Quando você cria um novo campo em uma tabela, ele recebe um tipo padrão. O tipo determina que espécie de dados o campo pode conter. Você pode, naturalmente, mudar o tipo padrão de um campo para um outro mais apropriado. Texto é o tipo padrão que o Access atribui, a menos que você altere o padrão. Você faz isso escolhendo Ferramentas, Opções e clicando na guia Tabelas/Consultas (veja a Figura 9.4). Figura 9.4 Utilize as caixas Texto e Número para configurar o tamanho do tipo de campo padrão; por exemplo, 50 para Texto ou Byte para Número.
Se quiser que cada novo campo que você estabelece automaticamente seja um tipo Número, escolha Número na lista suspensa Tipo de campo padrão. Então escolha um dos tamanhos de campo de número da lista suspensa Número. Esses tamanhos estão disponíveis para o tipo de dados Número:
Capítulo 9 – Gerenciando objetos de banco de dados
223
• Byte • Inteiro • Inteiro longo • Simples (precisão simples; suporta 38 dígitos à direita do ponto decimal) • Duplo (precisão dupla; suporta 308 dígitos à direita do ponto decimal) • Decimal (suporta 28 dígitos à direita do ponto decimal) • Código de replicação (um campo de 16 bytes normalmente utilizado em réplicas de bancos de dados)
A T O N
As variáveis de simples e dupla precisão podem conter números no intervalo de aproximadamente -3,4 * 10^38 a 3,4 * 10^38 e de aproximadamente -1,7 * 10 ^ 308 a 1,7 * 10 ^ 308, respectivamente. Mas isso não significa que podem fazer isso precisamente. Uma variável de dupla precisão, por exemplo, é precisa para 15 dígitos. Um número que requeira mais de 15 dígitos de exatidão não pode ser armazenado precisamente em uma variável de dupla precisão.
O Access oferece estes tipos de dados: • Texto • Memorando (Access 2000, 2002 e 2003 suportam indexação em campos Memo) • Número • Data/hora • Moeda • AutoNumeração (um Inteiro longo que incrementa a si mesmo automaticamente quando novos registros são adicionados a uma tabela) • Sim/Não • Objeto OLE • Hyperlink A escolha do tipo de dados para um determinado campo normalmente é ditada pelos valores que você quer armazenar. Se puder ter letras — por exemplo, o nome da pessoa — você escolherá Texto (limitado a 255 caracteres) ou Memorando (ilimitado). Se o campo só armazenará números, sua escolha depende de os números serem ou não sempre inteiros, como número de membros da família ou possivelmente um ponto flutuante, como área em metros quadrados. Dada uma escolha entre dois ou mais tipos de dados, você normalmente escolhe o mais simples. Onde possível, prefira Byte a Inteiro, Inteiro a Inteiro longo, Precisão Simples a Dupla e assim por diante. (Memorando é o tipo de dados que ocupa mais espaço.)
224
Gerenciando dados com o Microsoft Excel
Mas há outra consideração, criada pelas joins. Ao associar duas tabelas em uma consulta, os campos associados devem ser compatíveis, e isso significa que você deve planejar. Há três tipos de campo que não podem ser utilizados em uma join: Memorando, Objeto OLE e Hyperlink. Então, se sabe que terá de associar, digamos, uma tabela Hospita is com uma tabela TiposHospital, não dê a qualquer campo, que abrangerá a join, um tipo Memorando, Objeto OLE ou Hyperlink — nem mesmo se ambos os campos tiverem o mesmo tipo. A T O N
Observe que embora você agora possa indexar em campos Memorando e Hyperlink, não significa que pode fazer joins de tabelas utilizando-os.
Uma join pode ser criada com sucesso utilizando dois campos Texto, independentemente de seu comprimento. Por exemplo, se o campo UnitName for um campo Texto com um comprimento máximo de 50 e UnitTypeID for um campo Texto com um comprimento máximo de 2, você pode criar uma join com esses dois campos. Mas você não pode criar uma join utilizando um campo Texto e um campo numérico de qualquer tipo. (Lembre-se de que os campos Data/Hora e Sim/Não se qualificam como tipos numéricos.) Então, uma regra geral fácil é representar campos que talvez sejam utilizados para joins tanto como Texto ou ambos como Número.
Preparando joins de tabela O Access fornece uma janela Relacionamentos, que permite definir joins entre tabelas. Clique no botão Relacionamentos ou escolha Ferramentas, Relacionamentos. Ao fazer isso, a janela Relacionamentos se abre. Se abri-la antes de quaisquer relacionamentos terem sido definidos, uma caixa de diálogo Mostrar tabela também aparece; se já existir pelo menos um relacionamento, você pode exibir a caixa de diálogo Mostrar tabela escolhendo Relacionamentos, Mostrar tabela. Para adicionar uma tabela à janela Relacionamentos, clique na tabela na caixa de diálogo Mostrar tabela e então clique no botão Adicionar ou simplesmente dê um clique duplo na tabela. Quando terminar de adicionar tabelas, clique em Fechar para dispensar a caixa de diálogo Mostrar tabela. Você agora vê as tabelas na janela Relacionamentos (veja a Figura 9.5).
Figura 9.5 A linha entre os dois campos é a inner join padrão.
Na janela Relacionamentos, você pode definir as joins que quiser entre as tabelas em seu banco de dados. Com pelo menos duas tabelas em exibição, clique em um campo de uma tabela e
Capítulo 9 – Gerenciando objetos de banco de dados
225
arraste o ponteiro do mouse para um campo de outra tabela. Ao liberar o botão do mouse, a join entre as duas tabelas aparece. Há uma vantagem em criar a join na janela Relacionamentos em vez de em uma janela de consulta. O Access criará uma join automaticamente para você em uma consulta se houver duas tabelas com campos identicamente nomeados e se um desses campos for uma chave primária. Mas se essas duas condições não forem satisfeitas, o Access não criará a join para você. A janela Relacionamentos pode ajudar aqui. Se criar uma join na janela Relacionamentos, ela aparece automaticamente quando, em seguida, você projetar uma consulta baseada nas tabelas associadas — independentemente de o campo ter ou não o mesmo nome e de um deles ser ou não uma chave primária da tabela. A T O N
Nem a janela Relacionamentos nem a janela Estrutura da Consulta o adverte se você estiver criando uma join não válida, como entre um campo Texto e um campo Inteiro. Você não receberá uma mensagem de erro até que tente utilizar a join, como receberia se tentasse abrir a consulta.
Escolhendo tipos de join As joins de tabelas podem representar tanto relacionamentos um-para-um como um-para-muitos. Em um relacionamento um-para-um, cada tabela tem somente uma instância de um valor em seu campo de join. Em um relacionamento um-para-muitos, uma tabela tem somente uma instância de um valor enquanto a outra tabela pode ter múltiplas instâncias do mesmo valor. ➪
Outro tipo de join, muitos-para-muitos, é na verdade duas instâncias de uma join um-para-muitos; veja “Utilizando múltiplos índices de campo” mais adiante neste capítulo para informações adicionais.
Entendendo os relacionamentos um-para-muitos
Os relacionamentos um-para-muitos são sem dúvida o tipo mais comum. Um exemplo típico consiste em uma tabela, Voters, com dados de muitas pessoas, algumas registradas como Democratas e outras registradas como Republicanos. Um registro recebe um valor 1 no campo PartyID se a pessoa for Democrata e 2 se Republicano. Há muitas instâncias de cada valor na tabela Voters. Outra tabela, Parties, contém dois campos: PartyID e PartyName. Um registro tem um 1 em PartyID e o rótulo Democrata em PartyName. O outro registro tem um 2 em PartyID e o rótulo Republicano em PartyName. Você associa a tabela Voters e a tabela Parties em uma consulta utilizando PartyID como o campo em comum. Em vez de exibir qualquer instância de PartyID, você mostra PartyName. Dessa maneira, você pode ver qual partido cada pessoa se identifica. É um relacionamento um-paramuitos: há uma instância de cada valor PartyID em Parties e muitas instâncias de cada valor PartyID em Voters.
226
Gerenciando dados com o Microsoft Excel
Entendendo os relacionamentos um-para-um Os relacionamentos um-para-um são muito menos comuns. Ocorrem quando cada tabela tem somente uma instância de cada valor nos campos que estabelecem a join. São menos comuns porque você normalmente teria colocado todos os campos em uma tabela, em vez de em tabelas separadas associadas por um campo em comum. Uma razão de utilizar um relacionamento um-para-um é a segurança. Em uma aplicação de recursos humanos em rede, você talvez queira mostrar a todos os usuários o nome completo, departamento e ramal de telefone de cada empregado. Ao mesmo tempo, você talvez queira ocultar da maioria dos usuários o salário e o SSN (Social Security number) do empregado. Uma maneira de tratar isso é dividir os dados em duas tabelas: uma que tenha as informações para consumo público e uma somente para fins de RH. Você pode configurar permissões de banco de dados de modo que todos os usuários possam abrir uma tabela, mas somente certos usuários possam abrir ambas as tabelas. Vinculando as duas tabelas em um relacionamento um-para-um, por meio de algum campo como EmployeeID, você pode associar todos os dados para um empregado em particular, enquanto mantém seguras as informações confidenciais.
Definindo e identificando os relacionamentos No Access, um relacionamento um-para-um é mostrado com um 1 em ambos os finais da linha de relacionamento. Um relacionamento um-para-muitos é denotado por um 1 ao lado da tabela um e um símbolo de infinito ao lado da tabela muitos (veja a Figura 9.6). Figura 9.6 DirectorID é a chave
primária da tabela Directors, então é o lado um do relacionamento um-paramuitos.
Há alguns requisitos que você deve satisfazer antes de poder definir um relacionamento como um-para-muitos ou um-para-um. • Você precisa utilizar a janela Relacionamentos. Você não pode definir um relacionamento um-para-muitos ou um-para-um em uma visualização de estrutura da consulta. • Em um relacionamento um-para-muitos, o campo de join em uma das tabelas deve ser sua chave primária ou a base para um índice único. Isso assegura que aí realmente há apenas uma instância de cada valor no lado um do relacionamento. • Em um relacionamento um-para-um, o campo de join deve ser a chave primária (ou a base para um índice único) em ambas as tabelas. • A integridade referencial deve ser imposta.
Capítulo 9 – Gerenciando objetos de banco de dados
227
Integridade referencial é um termo retórico para um conceito relativamente simples: você tem de preservar valores correspondentes em tabelas associadas. A próxima seção discute essa noção em mais detalhes.
Preservando a integridade referencial Você teve alguma dificuldade em criar um relacionamento entre duas tabelas: Voters e Parties. As duas tabelas têm um campo Byte, PartyID, que é a chave primária da tabela Parties (nesse contexto, a tabela Parties é chamada de tabela primária). A tabela Parties tem somente dois registros: um para Democratas e um para Republicanos. Você adiciona ambas as tabelas à janela Relacionamentos e as associa clicando em PartyID em uma tabela e arrastando para PartyID na outra tabela. A Figura 9.7 mostra o resultado. Figura 9.7 Escolha Relacionamentos, Mostrar direto para ocultar relacionamentos indiretos e para arrumar uma janela obstruída por tabelas.
Subseqüentemente você ajusta a join clicando com o botão direito do mouse na linha de relacionamento e escolhendo Editar relacionamentos do menu de atalho. A caixa de diálogo Editar relacionamentos aparece como mostrada na Figura 9.8.
A T O N
A caixa de diálogo Editar relacionamentos aparece por padrão assim que você termina de criar a join. Se tiver que editar o relacionamento mais tarde, você abre a caixa de diálogo clicando com o botão direito do mouse na linha de relacionamento ou escolhendo Relacionamentos, Editar relacionamentos.
Figura 9.8 As caixas de seleção Propagar são ativadas somente depois que você marcar a caixa de seleção Impor integridade referencial.
Na caixa de diálogo Editar relacionamentos, marque a caixa de seleção Impor integridade referencial e clique em OK. Agora você impediu que você e outros usuários dessas tabelas realizem três ações. Essas ações são as seguintes:
228
Gerenciando dados com o Microsoft Excel
• Você não pode adicionar registros sem correspondência no lado muitos . O Access não permitirá que você adicione um registro à tabela Voters com um valor 3, talvez representando Liberais, no campo PartyID. A idéia aqui é que você deve preparar a base adicionando um novo registro na tabela primária antes de utilizar seu valor na tabela relacionada. Se começar adicionando um registro à tabela Parties, um com 3 em PartyID e Liberal em PartyName, você pode subseqüentemente adicionar um registro à tabela Voters com 3 em PartyID. • Você não pode excluir um registro da tabela primária . O Access não permitirá que você exclua o registro Democrata nem o registro Republicano da tabela Parties se houver pelo menos um registro de correspondência na tabela Voters. Geralmente, sob integridade referencial, você não pode excluir um registro da tabela primária se tiver um registro de correspondência na tabela relacionada. Mas se tivesse um registro Liberal na tabela Parties e nenhum registro Liberal na tabela Voters, você poderia excluí-lo de Parties. • Você não pode alterar um valor na chave primária. Suponha que você decida não querer utilizar o valor 1 em PartyID para representar Democratas — você prefere utilizar 18. Se tentar mudar o 1 em Parties para 18, o Access não permitirá. Fazer isso o deixaria com vários Democratas órfãos na tabela Voters. E é isso o que a integridade referencial significa: você não pode adicionar um registro à tabela relacionada se ele não tiver correspondência na tabela primária; você não pode excluir um registro da tabela primária se ele tiver uma correspondência na tabela relacionada; e você não pode modificar um valor na chave da tabela primária. Na verdade, você pode fazer duas dessas coisas se quiser. Recorra à Figura 9.8 e observe que há duas caixas de seleção adicionais. Se marcar a caixa de seleção Propagar atualização dos campos relacionados, você pode alterar um valor-chave na tabela primária. Utilizando o exemplo Voters, você poderia alterar o registro Democrata na tabela Parties para ter um valor de 18 em vez de 1. Então uma propagação da atualização mudaria o valor 1 para 18 em todos os registros com correspondência na tabela Voters. Se marcar a caixa de seleção Propagar exclusão dos registros relacionados, você pode excluir um registro da tabela primária, mesmo se este tiver uma correspondência a registros na tabela relacionada. Entretanto, os registros relacionados também são excluídos (você será advertido primeiro e terá uma chance de prosseguir).
Estabelecendo chaves O termo chave normalmente significa uma chave primária. Uma chave primária é um campo (ou campos) em uma tabela que identifica unicamente cada registro nessa tabela. Se cada um desses for verdadeiro • Você está representado em um registro em uma tabela chamada ExcelUsers. • Seu registro tem o valor 3 no campo chamado UserID na tabela chamada ExcelUsers. • O campo chamado UserID é a chave primária da tabela chamada ExcelUsers.
Capítulo 9 – Gerenciando objetos de banco de dados
229
então nenhum outro registro em ExcelUsers pode ter o valor 3 no campo chamado UserID. Além disso, nenhum registro pode ter um valor nulo no campo chamado UserID. Ao ler sobre projeto de banco de dados, você com freqüência acha que é aconselhável estabelecer uma chave primária para cada tabela em seu banco de dados. Isso com freqüência é um bom conselho. Mas é mais importante entender por que você deve fazer algo do que saber que deve fazê-lo. Há somente algumas razões para utilizar chaves primárias. As mais importantes são discutidas nas seções a seguir. Localizando um registro específico
Você verá mais sobre índices na próxima seção, mas por enquanto esteja ciente de que um índice único associa uma posição em uma tabela com um valor na chave primária da tabela. Como resultado, um banco de dados é capaz de localizar um registro em particular muito mais rápido do que se você tivesse que examinar todos os registros da tabela, um por um. A T O N
Ao estabelecer um campo como uma chave primária da tabela, o Access indexa automaticamente a tabela nesse campo. Se, além disso, quiser indexar uma tabela por um campo diferente, você deve fazê-lo manualmente ou por meio do recurso AutoIndexar (veja a seção a seguir).
Suponha que uma tabela utilizada por um departamento de recursos humanos tenha o SSN como sua chave primária. Você quer procurar o registro do empregado cujo SSN é 987-65-4321 que, embora você ainda não o conheça, é o registro 32.767 na tabela. O sistema de gerenciamento de bancos de dados ( database management system – DBMS) utiliza um algoritmo de pesquisa muito rápido para localizar este SSN no índice. Depois de encontrar no índice o SSN procurado, o mecanismo de bancos de dados também encontra a localização do SSN na tabela. Isto é o que o índice faz: ele pareia o SSN (nesse caso, 987-65-4321) com a localização na tabela do registro que tem o SSN (nesse caso, 32.767). Saber a localização do registro permite ao DBMS exibir o registro completo para o usuário: o nome do empregado, data de admissão, SSN, valor de salário, valor da contribuição e assim por diante.
Estabelecendo índices Como você viu na seção anterior deste capítulo, “Localizando um registro específico”, os índices podem ajudar um DBMS a localizar um registro muito rapidamente. É uma simples analogia antiga, mas boa: o índice de uma tabela é como o índice de um livro. Com o índice de um livro, você pesquisa a palavra em que está interessado e então localiza a página em que a palavra está. Com o índice de uma tabela, o DBMS pesquisa um valor em que você está interessado e então encontra o registro em que esse valor reside. O DBMS utiliza uma estratégia muito rápida de pesquisa para localizar uma entrada em um índice. A estratégia é chamada de árvore B e envolve fazer uma série de escolhas entre duas alternativas. Continuando com a analogia de índice de livro (que começa a perder o sentido aqui), você poderia examinar a primeira metade do índice para localizar uma palavra que inicia
230
Gerenciando dados com o Microsoft Excel
com c e então no segundo quarto para localizar uma palavra que inicia com ch e, em seguida, no terceiro oitavo para localizar uma palavra que inicia com cha e assim por diante até localizar a palavra chão . De maneira semelhante, o DBMS divide continuamente as entradas no índice em agrupamentos equilibrados, ou balanceados (daí o B em árvore B ), até localizar o valor de campo sendo procurado. No Access, quando você identificar um campo como a chave primária de uma tabela, o Access cria automaticamente um índice para esse campo. Há duas maneiras gerais, diferentes de estabelecer uma chave primária, pelas quais você pode criar um índice para um campo.
Estabelecendo um índice automaticamente Suponha que seja sua prática fornecer um prefixo ou sufixo em particular para nomes de campos que você quer indexar. Por exemplo, talvez você utilize o sufixo CD, abreviação de código. Então seus campos indexados poderiam ser chamados PhysicianCD, AccountCD, ProgramCD e assim por diante. Se em vez disso você utilizasse CD como um prefixo, você poderia nomear os campos como CDPhysician, CDAccount e CDProgram. Se quiser que o Access indexe um campo automaticamente com base em um prefixo ou em um sufixo, comece com um banco de dados ativo e então escolha Ferramentas, Opções. Clique na guia Tabelas/consultas (veja a Figura 9.9).
Figura 9.9 Separe os prefixos ou sufixos por ponto-e-vírgula, não vírgulas.
Seguindo a lista existente de prefixos e sufixos, digite ;CD. Isso estabelece CD como uma string que, afixada a um nome de campo, faz com que o Access automaticamente indexe o campo quando ele for criado.
Criando um índice manualmente Há várias maneiras de criar um índice além da automática. Duas delas utilizam o modo Design da tabela, como mostrado na Figura 9.10.
Capítulo 9 – Gerenciando objetos de banco de dados
231
Figura 9.10 O painel Propriedades do campo permite configurar várias propriedades do campo.
Na Figura 9.10, o campo chamado Age está selecionado. Para configurar um índice nesse campo utilizando o painel Propriedades do campo, siga estes passos: 1. Na guia Geral, clique na caixa à direita da legenda Indexado. 2. Uma seta de lista suspensa aparece. Clique nela para exibir a lista suspensa. 3. Clique em Sim (Duplicação autorizada) ou em Sim (Duplicação não autorizada). 4. Salve a tabela. Para utilizar o outro método, escolha Exibir, Índices (ou clique no botão Índices). A caixa de diálogo mostrada na Figura 9.11 aparece. Figura 9.11 Você pode dar um nome significativo ao índice se utilizar a caixa de diálogo Índices.
232
Gerenciando dados com o Microsoft Excel
Siga estes passos: 1. Digite um nome, que poderia ser o nome do próprio campo, na coluna Nome do índice. 2. Clique na coluna Nome do campo e selecione o campo que você quer indexar a partir da lista suspensa. 3. Indique se quer que o índice coloque os registros em ordem ascendente ou decrescente de acordo com seus valores no campo indexado. 4. Configure as opções Primária, Exclusivo e Ignorar valores nulos como Sim ou Não. 5. Clique no botão X para fechar a caixa de diálogo. Configurar a opção Primária como Sim significa que o campo utilizado pelo índice torna-se a chave primária da tabela. Como você pode redefinir essa opção como Sim em um índice diferente, torna-se claro que uma tabela pode ter mais de uma chave primária — embora possa utilizar somente um índice por vez como sua chave primária. Em alguns casos raros, você desejará indexar um campo que talvez contenha valores nulos. Se tiver configurado Ignorar valores nulos como Sim, os registros que contêm valores nulos nesse campo não entrarão no índice. (Uma chave primária não pode conter valores nulos; portanto, a configuração Ignorar valores nulos não é utilizada para índices de chave primária.) A opção Exclusivo, se configurada como Não, permite mais de uma instância do mesmo valor no campo indexado. Isso levanta a pergunta de quais campos indexar. Os índices são uma faca de dois gumes. Eles normalmente tornam a recuperação de dados mais rápida, embora você não perceba um aumento de velocidade a menos que tenha muitos, muitos registros a pesquisar. Por outro lado, você tem de manter os índices atualizados. Ao adicionar um registro, por exemplo, o DBMS deve considerar o valor desse registro no campo do índice. Ele precisa adicionar ao índice o valor do registro e sua posição na tabela. A situação é semelhante ao excluir um registro. O DBMS deve remover a instância desse registro, seu valor de campo, bem como sua posição na tabela, a partir do índice. Isso de modo algum esgota o trabalho que o DBMS deve realizar para manter o índice atualizado. Se você excluir um registro, por exemplo, isso altera a posição de todos os registros subseqüentes na tabela. Seus valores de posição no índice têm de estar atualizados. O inverso ocorre, naturalmente, ao adicionar um novo registro. Somente a experiência pode dizer se estabelecer um índice para um campo resultará em ganho ou perda — se o ganho de velocidade de recuperação exceder a perda por causa da manutenção do índice. Os DBMSs mais sofisticados ajudam você a fazer esse julgamento com técnicas de amostragem estatísticas e ferramentas que podem ser utilizadas para fazer o ajuste fino de sua escolha de índices. Utilizando múltiplos índices de campo
Você não está limitado a um único campo em um índice. Às vezes pode ser vantajoso indexar uma tabela em dois, e raramente mais de dois, campos. Você pode encontrar um exemplo de um índice de múltiplos campos no banco de dados Northwind que acompanha o Microsoft Access. A tabela Order Details fornece uma associação entre a tabela Products e a tabela Orders.
Capítulo 9 – Gerenciando objetos de banco de dados
233
No banco de dados Northwind, qualquer determinado produto pode aparecer em muitos pedidos diferentes; por exemplo, Johnson’s Ketchup poderia ser parte dos pedidos de número 1, 10 e 25. Mas qualquer pedido pode conter muitos produtos diferentes: Roger’s Clams, Neal’s Wine e Garlic By Fred poderiam todos aparecer no pedido número 31. Esse é um exemplo de um relacionamento muitos-para-muitos . A tabela Order Details de Northwind associa muitos produtos com muitos pedidos. Cada registro na tabela representa um produto em um pedido. O banco de dados Northwind utiliza a tabela de junção para relacionar a tabela Products e a tabela Orders. A Figura 9.12 mostra o projeto de uma consulta que associa as duas tabelas. Figura 9.12 No modo de Design da consulta, a integridade referencial atribui a tabela de junção ao lado muitos dos relacionamentos um-paramuitos.
Para maximizar a eficiência da tabela de junção, sua chave primária deve consistir tanto em um campo de produto como em um campo de pedido. Para estabelecer essa chave de campos múltiplos, siga estes passos: 1. Com a tabela no modo de Design, clique na caixa à esquerda do nome do primeiro dos dois campos. A linha inteira é selecionada. 2. Mantenha pressionada a tecla Ctrl e clique na caixa à esquerda do nome do segundo dos dois campos. Ambas as linhas agora estão selecionadas. 3. Clique no botão Chave primária. O ícone de chave aparece ao lado de ambos os campos na grade de projeto (veja a Figura 9.13). 4. Salve a tabela. Figura 9.13 É a combinação dos dois campos que deve ser única na tabela de join. Qualquer campo pode por si mesmo assumir valores duplicados.
234
Gerenciando dados com o Microsoft Excel
Se preferir, você pode estabelecer um índice de múltiplos campos via a caixa de diálogo Índices. Siga estes passos: 1. Escolha Exibir , Índices. 2. Digite um nome para o índice na coluna Nome do índice da primeira linha em branco. 3. Na mesma linha, selecione o primeiro campo da lista suspensa na coluna Nome do campo. 4. Configure a lista suspensa Primário como Sim . 5. Na próxima linha abaixo, deixe a coluna Nome do índice em branco, mas selecione o segundo campo da lista suspensa na coluna Nome do campo (veja a Figura 9.14). 6. Clique na caixa X para fechar a caixa de diálogo.
Figura 9.14 Observe que ambos os campos têm o ícone de chave, indicando que eles estão combinados para formar a chave primária da tabela.
Quando comecei a projetar bancos de dados, tinha a idéia de que relacionamentos muitospara-muitos do tipo discutido aqui eram raros e exóticos. Foi só quando mantive contato com situações do mundo real que vi o quanto eram corriqueiros e como utilizá-los tornava várias de minhas consultas muito mais eficientes.
Criando consultas Você viu no Capítulo 5, “Utilizando o Microsoft Query”, como criar uma simples consulta Seleção no Access. A consulta Seleção era utilizada como uma fonte de dados para o Microsoft Query, em situações que eram muito complexas para o Microsoft Query gerenciar. Seções anteriores neste capítulo discutiram joins em maior detalhe, bem como as implicações de escolher nomes e tipos de dados para os campos nas tabelas do banco de dados. A presente seção baseia-se nesses conceitos para lidar com consultas de ação e os critérios em consultas seleção.
Capítulo 9 – Gerenciando objetos de banco de dados
235
A consulta Seleção que você viu no Capítulo 5 é indubitavelmente o tipo de consulta utilizada com mais freqüencia. Ela atende a vários objetivos, entre eles • Retornar dados de uma ou mais tabelas associadas por campos comuns. • Retornar resumo de dados, como médias e contagens de um campo de acordo com valores diferentes de outro campo. • Calcular campos novos e temporários construindo expressões baseadas em campos existentes. Em geral, você deve preferir consultas a outros métodos de recuperação ou manipulação de registros em um banco de dados porque SQL das consultas faz uso mais eficiente dos recursos do que fazem outras abordagens (como recordsets DAO e ADO). Há muitas ocasiões em que você precisa recorrer a recordset s porque o SQL não atende às suas necessidades. Por exemplo, você talve z precise modificar a aparência de um formulário com base no valor de um registro em uma tabela. Mas quando possível, deve utilizar consultas — e isso inclui as consultas de ação: Atualização, Acréscimo, Exclusão e Criar tabela.
Utilizando consultas Atualização Utilize uma Atualização quando quiser alterar o valor de um campo em alguns ou todos os registros do campo.
ESTUDO DE CASO Você é responsável por um banco de dados que mantém registros dos empregados em um hospital. Um dos requisitos para o hospital manter seu credenciamento é que todos os empregados passem por um exame anual em procedimentos de segurança: o que fazer se uma criança for raptada, onde encontrar informações de segurança material, como informar um derramamento de material perigoso e assim por diante. O teste é administrado on-line utilizando a rede de dados do hospital, e uma das tabelas do banco de dados é provida de chaves no número de ID único do empregado. Ele também armazena a data que ele fez o teste, sua resposta a cada uma das 30 perguntas do teste e o número de perguntas que ele respondeu corretamente. O departamento de recursos humanos do hospital mantém outra tabela que contém outras informações como o SSN do empregado, o departamento em que ele trabalha, sua data de admissão e todas as outras informações pessoais necessárias para um empregado de uma indústria altamente regularizada. A tabela HR também tem como chave o ID único do empregado. Ocasionalmente, o hospital quer analisar duas variáveis de resumo críticas: a média percentual de respostas certas no teste de segurança para cada departamento e a porcentagem de empregados atuais que fizeram o teste, novamente por departamento. Um departamento com uma contagem média extraordinariamente baixa no teste recebe treinamento adicional. Um departamento com atraso no cumprimento de metas está destinado a receber uma atenção desagradável da administração do hospital. Você utiliza uma consulta Seleção para associar a tabela Safety Test à tabela Employees e utilizar essa consulta (veja a Figura 9.15) como a fonte de dados para um intervalo de dados externos em uma planilha do Excel (veja a Figura 9.16).
236
Gerenciando dados com o Microsoft Excel
Figura 9.15 A consulta retorna para cada departamento uma contagem de empregados e de conclusões de teste para calcular um percentual.
A C I D
Para fazer a consulta mostrada na Figura 9.15 exibir o campoPercentComplete em formato percentual, primeiro clique em qualquer linha na coluna no modo de Design de consulta para selecionar uma célula na grade de projeto. Então clique com o botão direito do mouse na célula (você não pode iniciar clicando com o botão direito do mouse na grade de projeto; o Access assume que você está clicando com o botão direito do mouse na consulta, não na célula). Escolha Propriedades do menu de atalho e na janela Propriedades do campo escolha Porcentagem para o formato do campo. Isso controla a exibição do campo somente no Access — se quiser mostrar o campo como porcentagem no Excel, você também precisará formatá-lo na planilha.
Figura 9.16 Uma análise de porcentagem normalmente deve incluir o denominador da porcentagem (aqui, a contagem de empregado por departamento).
Um dia você fica sabendo que a tabela HR não irá mais manter os dados de ex-empregados em sua tabela Employees, mas que arquivará os dados em uma tabela Terminations. Uma vez por mês, os registros de empregados que deixaram o hospital são removidos da tabela Employees e colocados na tabela Terminations. Isso lhe causa uma pequena aflição. O hospital quer ver os resultados departamentais tanto para os empregados atuais quanto para todos os empregados desde que começou a administrar o teste. A consulta mostrada na Figura 9.15 conta com a tabela Employees
Capítulo 9 – Gerenciando objetos de banco de dados
237
para fornecer o departamento de cada empregado, mas se os ex-empregados não devem ser mais mantidos nessa tabela, você terá de procurar a vinculação desses ex-empregados em outra parte do departamento. Há várias soluções, mas você decide que o mais simples e direto é começar a armazenar o departamento do empregado na tabela Safety Test.Antes de o departamento de RH arquivar os registros de ex-empregados pela primeira vez, você duplica o campo Department da tabela Employees na tabela Safety Test. Então você cria a consulta atualização mostrada na Figura 9.17.
Figura 9.17 Observe que uma inner join é utilizada: ela retorna apenas os registros em que os campos associados de ambas as tabelas são iguais.
Para criar a consulta mostrada na Figura 9.17, siga estes passos:
1. Na janela principal do Access, clique na guia Consultas. 2. Clique no botão Novo, escolha Modo de design na caixa de diálogo Nova consulta e clique em OK. 3. Adicione a tabela Employees e a tabela Safety Test ao painel de projeto da consulta e então clique emFechar na caixa de diálogo Mostrar tabela. Se, como aqui, os campos de chave primária das tabelas tiverem o mesmo nome, o Access automaticamente cria uma join padrão entre as duas tabelas. (Se não, você criaria a join clicando em um campo e arrastando para o outro.) 4. Escolha Consulta, consulta Atualização. Isso muda a consulta ativa do tipo de consulta Seleção padrão para um tipo de consulta Atualização. Observe que a linha Classificação e a linha Mostrar são removidas e substituídas por uma linha Atualizar para. 5. Arraste o campo Department da tabela Safety Test para a primeira coluna da grade de projeto da consulta. Ou,clique na linha Campo e selecione Safety Test.Department da lista suspensa. 6. Na linha Atualizar para sob Department, digite [Employees].[Department]. Como há dois campos Department na consulta — um de cada tabela — você precisa qualificar aquele que quer atualizar. 7. Escolha Consulta, Executar ou clique no botão Executar na barra de ferramentas. Clique em Sim em resposta à mensagem de confirmação do Access.
A T O N
➪
A consulta utiliza uma inner join: ela retorna somente os registros que existem emambas as tabelas e, portanto, não tenta atualizar um registro localizado na tabela Safety Test que não esteja localizado na tabela Employees. Ela também poderia utilizar uma outer join que retorna todos os registros da tabela Safety Test e somente os registros da tabela Employees com um valor correspondente em EmpID. Suponha que em vez disso você utilize uma outer join que retorna todos os registros da tabela Employees e somente os registros da tabela Safety Test com um valor correspondente em EmpID. Então a consulta de atualização tentará atualizar os registros inexistentes na tabela Safety Test.Se estiver executando a consulta manualmente,você obterá uma mensagem de advertência de que alguns registros não foram atualizados por causa das violações de chave. Você pode pular o aviso, mas é um aborrecimento desnecessário. A menos que esteja certo de suas razões, utilize inner joins em consultas Atualização de múltiplas tabelas.
Para informações adicionais sobre inner e outer joins, consulte “Fazendo joins de registro-pai e registro-filho”, p. 111.
238
Gerenciando dados com o Microsoft Excel
Utilizando as consultas Exclusão e Acréscimo Não é muito difícil excluir um registro de uma tabela em um banco de dados: apenas abra a tabela, localize o registro, selecione-o e então pressione a tecla Delete. Mas quando tiver muitos registros para excluir e, por uma razão ou outra precisar excluir os mesmos registros repetidamente, você não gostará de fazer isso manualmente. Além do tempo que você desperdiça, há sempre a irritante dúvida de que talvez tenha excluído os registros errados. As consultas Exclusão podem ser úteis aqui. São mais rápidas do que excluir registros manualmente e você pode confiar nelas para agir da mesma maneira repetidas vezes — apenas certifique-se de que as configurou corretamente em primeiro lugar. O Capítulo 4, “Importando dados: uma visão geral”, mencionou a dificuldade que você encontra quando uma aplicação armazena dados internamente mas não oferece uma maneira simples e direta de acessá-los. Felizmente, tais aplicações geralmente fornecem ao usuário uma capacidade de geração de relatório, que exporta dados em um layout de relatório formatado ou em um formato de valores separados por vírgulas ( comma-separated values – CSV). Em alguns casos você deve acessar e reutilizar esses dados com freqüência. Então irá preferir utilizar consultas Exclusão em conjunto com consultas Acréscimo. Quando estiver nesse tipo de situação, normalmente ajuda utilizar código VBA para executar as consultas. A utilização de seu tempo é mais eficiente se você empregar um único procedimento em VBA que executa várias consultas do que se executar cada uma das consultas manualmente. ESTUDO DE CASO
Mas os pedidos do departamento de Recursos Humanos não pararam por aí. Esse departamento quer que você crie um formulário que os diretores possam utilizar para visualizar informações sobre empregados em seus departamentos: pessoas cujas avaliações anuais de desempenho vencem no próximo mês, datas que o exame médico anual é exigido, empregados retornando de licenças etc. Criar o formulário não é o verdadeiro problema. O problema é conseguir levar os dados do formulário a partir do banco de dados de empregados protegido do RH para um banco de dados que possa ser mais amplamente acessado por vários usuários — aqui, os diretores de departamento. Normalmente essa seria uma tarefa simples: apenas aponte uma consulta Seleção para a tabela apropriada no banco de dados do RH e utilize-a como a origem de dados para um formulário de usuário. O problema é que você não pode apontar uma consulta Seleção no banco de dados do RH: ele armazena seus dados em um formato proprietário que não é compatível com os padrões de hoje. O programa que o RH utiliza é antigo. Mas tem um gerador de relatórios que você utiliza periodicamente para criar um arquivo CSV. Com o arquivo CSV criado, você pode utilizá-lo para preencher uma tabela de banco de dados. Há mais um obstáculo: você precisa criar um novo campo como parte do processo. A aplicação de RH lhe fornecerá a data em que vence a próxima avaliação de cada um dos empregados. Mas os diretores têm um prazo para arquivamen to da avaliação anual. O prazo se estende da data de vencimento da avaliação por quatro semanas depois. Isto é, se a avaliação de Ms. Smith vence em 9 de agosto, sua avaliação não é considerada como vencida até 6 de setembro. Então,seu projeto terá de calcular essa data de vencimento do arquivamento além de atualizar os dados básicos do empregado. A seqüência de eventos será a seguinte:
Capítulo 9 – Gerenciando objetos de banco de dados
239
1. Utilizar o gerador de relatórios do RH para criar um novo arquivo CSV. 2. Importar os dados de texto do arquivo CSV para uma tabela de banco de dados. 3. Excluir os registros dos empregados antigos da sua tabela. 4. Acrescentar os novos registros de empregado à tabela. Ao mesmo tempo, você pode calcular a data de vencimento do arquiva-
mento da avaliação a partir da data de vencimento da avaliação. Cada um desses itens é discutido em detalhes nas seções a seguir .
Importando os dados de texto A primeira tarefa é se preparar para importar os dados. Suponha que o gerador de relatórios do RH gere uma saída de seus dados de texto na forma de um arquivo de texto chamado Employees.csv.É recomendável importar os dados em uma tabela do Access para que importações subseqüentes possam ser automatizadas.Você faz isso seguindo estes passos. 1. Com a janela de banco de dados do Access ativa, escolha Arquivo, Obter dados externos, Importar . A janela Importar aparece. 2. Na lista suspensa Arquivos do tipo, escolha Arquivos de texto (*.txt;*.csv;*.tab;*.asc). 3. Utilizando a lista suspensa Examinar em se necessário, navegue até a localização do arquivo Employees.csv. 4. Clique no arquivo Employees.csv para destacá-lo e clique no botão Importar . A janela Importar se fecha e é substituída pelo
primeiro passo do Assistente de importação de texto (veja a Figura 9.18).
Figura 9.18 Observe que a primeira linha do arquivo de dados inclui nomes de campo.
5. Nenhuma opção precisa de alteração no primeiro passo do assistente, então clique em Avançar para passar para o segundo passo
(veja a Figura 9.19).
240
Gerenciando dados com o Microsoft Excel
Figura 9.19 O Access observa a localização das vírgulas e separa os campos em colunas de acordo.
6. Seu arquivo separa campos com vírgulas, portanto você não precisa mexer no delimitador. Como mostrado, nomes de campo ocupam a primeira linha, então você deve marcar a caixa de seleção Primeira linha contém nomes de campo. Isso impede que o Access trate uma linha de nomes de campo como um registro. Além disso, o arquivo utiliza aspas duplas para configurar valores de texto, então utilize a lista suspensa Qualificador de texto para escolher a marca de aspas duplas. Clique em Avançar para seguir para o próximo passo (veja a Figura 9.20).
Figura 9.20 Especificar Primeira linha contém nomes de campo no passo anterior produz cabeçalhos de coluna dos nomes de campo.
7. Certifique-se de que Em uma nova tabela esteja selecionada e clique no botão Avançar (veja a Figura 9.21).
Capítulo 9 – Gerenciando objetos de banco de dados
241
Figura 9.21 Se necessário, selecione um campo clicando em sua coluna e altere suas propriedades quando necessário — por exemplo, seu Nome.
8. Se seu arquivo de dados não tem nomes de campo em sua primeira linha, o Access propõe nomes padrão ( Campo1, Campo2 e assim por diante). Utilize esse passo para mudar os nomes para algo mais descritivo, porque os nomes se tornarão os nomes de campo na nova tabela. Esse também é o lugar certo para indicar um tipo de dados específico para cada campo (em particular Data/Hora, porque o Access não reconhecerá automaticamente alguns formatos de data e hora). Clique em Avançar para seguir para o próximo passo (veja a Figura 9.22).
Figura 9.22 Se seus dados tiverem um ID de registro único, considere utilizar isso como a chave primária da tabela.
9. Neste exemplo, a tabela em que os dados são importados não precisa de uma chave primária, então você escolheria Sem chave primária. Em uma situação diferente, você talvez deixasse o Access adicionar uma chave primária (que normalmente se chamaria ID e seria digitado como um incremento Inteiro longo) ou escolhesse sua própria chave primária a partir da lista suspensa. Clique em Avançar (veja a Figura 9.23).
242
Gerenciando dados com o Microsoft Excel
Figura 9.23 Clique no botão Avançado para evitar perder todas as informações que você forneceu ao assistente.
10. A Figura 9.23 mostra que você pode fornecer um nome diferente para a nova tabela; caso contrário, ela será nomeada de acordo com o nome dado ao arquivo CSV. Nesse caso, é importante salvar as informações que você forneceu e você faz isso clicando no botão Avançado (veja a Figura 9.24). Figura 9.24 O sufixo ID é configurado por meio de Opções para indicar um índice, então o assistente propõe um índice de valor duplicado para o campo EmpID.
11. Faça quaisquer alterações necessárias utilizando os controles na janela Especificações de importação. Então se certifique de clicar no botão Salvar como. Isso salvará sua especificação de modo que possa ser reutilizada mais tarde e você não tenha de fornecer nomes de campo, tipos de campo, informações de indexação e assim por diante. A janela Salvar especificação de importação/ exportação aparece (veja a Figura 9.25). Figura 9.25 Raramente há uma razão para mudar o nome de especificação padrão fornecido pelo Access.
Capítulo 9 – Gerenciando objetos de banco de dados
243
12. Se necessário, altere o nome da especificação que o Access fornece e clique em OK. Você retorna à janela Especificação de importação. 13. Clique em OK para retornar ao passo final do Assistente de importação de texto e clique em Concluir. O Access o informa que importou os dados na tabela. Agora, desde que você não altere a estrutura do arquivo de dados — excluindo e adicionando campos, colocando dados de Texto onde a especificação espera por Data/Hora, utilizando um espaço em vez de uma vírgula como o delimitador de campo — você será capaz de reutilizar a especificação de importação que você salvou no passo 11. Como verá, você pode referenciá-la no código VBA para automatizar o processo de trazer novos dados ao banco de dados.
Excluindo os registros antigos A próxima tarefa é excluir os registros existentes. Então, você pode substituí-los pelos registros recém-impor tados. Naturalmente, a solução é executar uma consulta Exclusão seguida por uma consulta Acréscimo. O primeiro passo é criar as consultas. A consulta Exclusão completada é mostrada na Figura 9.26.
Figura 9.26 Em outra situação, você poderia excluir os registros seletivamente fornecendo um valor para um campo em particular na linha Critérios.
Para criar a consulta mostrada na Figura 9.26, siga estes passos:
1. Com a janela de banco de dados do Access ativa, clique na guia Consultas. Então clique no botão Novo. Com o modo de Design selecionado na caixa de listagem Nova consulta, clique em OK. 2. A janela Nova consulta desaparece e a janela Mostrar tabela aparece. Clique na guia Tabelas se necessário. 3. Clique na tabela EmployeeData na caixa de listagem Mostrar tabela e então clique em Adicionar para adicionar a tabela ao projeto de consulta. Essa é a única tabela envolvida na consulta, então você pode clicar em Fechar para remover a janela Mostrar tabela. 4. Escolha Consulta , Consulta exclusão . Isso altera a nova consulta de uma consulta Seleção padrão em uma consulta Exclusão. 5. Clique no asterisco na tabela EmployeeData mostrada no painel Tabela da consulta. Esse asterisco é um tipo de curinga, substituindo todos os campos da tabela. Arraste-o para a linha Campo da primeira coluna da grade de projeto. A janela de consulta agora aparece como na Figura 9.26. 6. Escolha Arquivo, Salvar. O Access pede para você substituir o nome padrão (Consulta1, Consulta2 e assim por diante) por um mais descritivo. Esse exemplo salva a consulta como Delete From Employee Data .
244
Gerenciando dados com o Microsoft Excel
Você executará a consulta Delete From Employee Data para remover registros preexistentes da tabela em preparação para adicionar novos registros obtidos da aplicação do RH.Você também desejará criar uma consulta Exclusão para dispor os registros antigos na tabela Employees, talvez denominada Delete From Employees.
Acrescentando os novos registros Você pode adicionar os registros mais eficientemente por meio de uma consulta Acréscimo, mostrada na Figura 9.27.
Figura 9.27 Uma consulta Acréscimo também pode calcular novos valores de campo para acrescentar à tabela-alvo.
Para criar a consulta mostrada na Figura 9.27, siga estes passos:
1. Com a janela de banco de dados do Access ativa, clique na guia Consultas. Então clique no botão Novo. Com o modo de Design selecionado na caixa de listagem Nova consulta, clique em OK. 2. A janela Nova consulta desaparece e a janela Mostrar tabela aparece. Clique na guia Tabelas se necessário. 3. Clique na tabela Employees na caixa de listagemMostrar tabela e então clique em Adicionar para adicionar a tabela ao projeto de consulta. Clique em Fechar para remover a janela Mostrar tabela. 4. Escolha Consulta, Consulta acréscimo. A janela Acrescentar aparece (veja a Figura 9.28). EscolhaEmployee Data na lista suspensa Nome da tabela e clique em OK. Isso muda a nova consulta de uma consulta Seleção padrão para uma consulta Acréscimo que obterá os registros da tabela Employees e os acrescentará à tabela Employee Data. Você retorna à janela de design de consulta. Figura 9.28 Ao projetar uma consulta Acréscimo, comece com a tabela de origem e especifique a tabela-alvo na janela Acréscimo.
5. Clique no asterisco na tabela Employees mostrada no Painel de tabela da consulta. Arraste-o para a linha Campo da primeira coluna da grade de projeto. 6. Na segunda coluna da grade de design, digite a expressão OverdueDate DateAdd("ww",4,DueDate) . Essa expressão utiliza a função DateAdd do Access para calcular um campo chamado OverdueDate. Nesse caso,ela adiciona quatro semanas (o argumento ww) ao campo DueDate. A janela de consulta agora aparece como na Figura 9.27. 7. Escolha Arquivo, Salvar. Substitua o nome padrão por um mais descritivo. Esse exemplo salva a consulta como Append To EmployeeData .
Capítulo 9 – Gerenciando objetos de banco de dados
245
Executando as consultas
Com essas duas consultas no banco de dados, você pode executar código VBA semelhante à seguinte listagem: Sub UpdateEmployeeData() Dim PathName As String CurrentDb.QueryDefs("Delete From Employee Data").Execute CurrentDb.QueryDefs("Delete From Employees").Execute PathName = "C:\Documents and Settings\Owner\Desktop\Employees.csv" DoCmd.TransferText acImportDelim, "Employees Import Specification", _ "Employees", PathName, True CurrentDb.QueryDefs("Append To Employee Data").Execute End Sub
Há alguns aspectos desse código que merecem uma referência: • O método TransferText do objeto DoCmd é utilizado para importar os dados do arquivo CSV. Seu argumento acImportDelim indica que o Access está para importar um arquivo delimitado. A especificação de importação, Especificação de importação de empregados, é identificada, tal como é a tabela Employees em que os dados serão inseridos. • O caminho e o nome do arquivo CSV são armazenados em uma variável PathName e passados ao método TransferText. Essa abordagem não é necessária, mas torna mais fácil alterar o caminho ou o nome de arquivo quando necessário. • Você pode executar uma consulta, assim como o código mostrado anteriormente executa três consultas, simplesmente nomeando a consulta e chamando o método Execute. A consulta pode ser de qualquer tipo — uma consulta Seleção ou de ação. • Anexando esse código a um botão de comando em um formulário de usuário no banco de dados, você torna muito fácil atualizar uma tabela quando necessário. • Você pode executar esse código diretamente em um módulo VBA do Access. Você também pode executá-lo (com alguns ajustes, como identificar o banco de dados) utilizando um módulo VBA do Excel. Você precisaria primeiro configurar uma referência a uma biblioteca de objeto DAO ou ADO, escolhendo Referências do menu Ferramentas do VBE e marcando a caixa de seleção da biblioteca apropriada. Você realmente não quer ter tanto trabalho apenas para obter os dados em um banco de dados. É muito mais sensato utilizar diretamente o banco de dados ou outra aplicação que é utilizada para manter o conjunto de dados original. Mas às vezes isso não é possível. Por exemplo, quando o pacote de manutenção de dados só pode ser utilizado em algumas estações de trabalho, por razões de licenciamento. Mesmo nessa situação, você esperaria que a aplicação suportasse uma vinculação a partir de uma aplicação mais amplamente disponível, como o Access ou o Excel. Mas muitas aplicações não fornecem esse grau de compatibilidade ou de conveniência. Então você tem de recorrer aos méto-
246
Gerenciando dados com o Microsoft Excel
dos discutidos neste capítulo, por mais precários que possam parecer. Em minha própria experiência, eles são mais freqüentemente necessários do que não.
Olhando para frente Este capítulo preocupou-se principalmente com estruturas e atividades mais apropriadas para utilização com um gerenciador de bancos de dados, como o Access ou o SQL Server, do que para uma aplicação de análise de dados como o Excel. A razão é que se estiver gerenciando dados em um banco de dados da plataforma Excel, é útil entender que efeito suas ações têm no banco de dados. O próximo capítulo retorna ao Excel, e os métodos que você vai utilizar para fazer o que este capítulo discutiu no contexto do Access: definir tabelas e campos, administrar joins de tabela e criar e executar consultas, tudo sem jamais utilizar o sistema de gerenciamento de bancos de dados.
10 Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects Criando bancos de dados a partir do Excel De certo modo, criar um banco de dados a partir da plataforma que o Excel fornece é muito simples. Com uma referência a Data Access Objects (DAO) estabelecida em um módulo do VBA (como descrito no Capítulo 8, “Abrindo bancos de dados”), você executa a seguinte instrução: CreateDatabase Name:= "NewDB.mdb", _ Locale:=dbLangGeneral
Isso é tudo que é necessário. Executar essa instrução no VBA cria um novo banco de dados do Access chamado NewDB.mdb no diretório padrão. Ele não tem tabelas ou consultas ou quaisquer outras estruturas que você normalmente encontra em um banco de dados do Access, mas está aí e pronto para armazená-las para você. Naturalmente, isso é falso. Você precisa preparar a base primeiro. Você vai querer verificar se já não existe um nome de arquivo NewDB.mdb no diretório padrão e, se houver, precisará decidir se o exclui antes de criar um novo. Você também precisará decidir o que fazer se o arquivo existir e outro usuário o tiver aberto. Este capítulo mostra como realizar isso. Ele faz isso utilizando DAO como o conjunto de ferramentas, em vez do ActiveX Data Objects (ADO). O ADO não foi inicialmente projetado para fornecer toda a funcionalidade necessária na criação de um novo banco de dados. Uma extensão do ADO — ADOX, abreviação de ADO Extensions — suporta a criação de novas tabelas e campos em um banco de dados preexistente e é discutido mais adiante neste capítulo. É um pouco incomum criar um novo banco de dados do Excel, mas de modo algum desconhecido. Em particular, se desenvolve aplicações baseadas em Office para sua empresa ou clientes, você com freqüência tem usuários que querem criar suas próprias pastas de trabalho do Excel para armazenar dados. Por razões de backup de dados, bem como por armazenamento e recuperação mais efetivos, esses usuários costumam querer armazenar dados colocados nas pastas de trabalho em um banco de dados verdadeiro. Você pode satisfazer suas necessidades fornecendo um modelo do Excel. Um modelo é uma pasta de trabalho na qual novas pastas de trabalho são baseadas. O modelo pode incluir o código VBA que estabelece um novo banco de dados do Access, completo com tabelas e campos que o permite sombrear dados que o usuário coloca na nova pasta de trabalho, como mostrado no próximo estudo de caso.
248
Gerenciando dados com o Microsoft Excel
Criando um novo banco de dados com o DAO Suponha que você queira automatizar a criação de um novo banco de dados do Access. Com uma nova pasta de trabalho do Excel aberta, comece estabelecendo um módulo do VBA. Escolha Ferramentas, Macro, Editor do Visual Basic. Com o VBE ativo, crie um novo módulo escolhendo Inserir, Módulo. Para criar um banco de dados utilizando DAO, você precisa estabelecer uma referência à biblioteca de objetos de DAO. Escolha Ferramentas, Referências e role para baixo até ver Microsoft DAO Object Library. A versão de número mais alto é a que deve ser utilizada e as versões disponíveis em seu sistema dependem de qual versão do Office você instalou. Para o Office 2003, é Microsoft DAO 3.6 Object Library; para o Office 1997, é DAO 3.5. Marque a caixa de seleção e clique em OK.
Fornecendo o código para criar o banco de dados Depois de estabelecer a referência necessária, digite este código no novo módulo: Sub CreateDatabaseWithDAO(DatabaseName As String) Dim GoAhead As Boolean GoAhead = True If Dir(DatabaseName) <> "" Then If MsgBox(Prompt:="Delete existing " & DatabaseName & "?", _ Buttons:=vbYesNo, Title:="Naming conflict.") = vbYes Then Kill DatabaseName GoAhead = True Else MsgBox Prompt:="Okay, leaving existing " & DatabaseName _ & " alone.", Title:="Taking no action." GoAhead = False End If End If If GoAhead Then CreateDatabase Name:=DatabaseName, Locale:=dbLangGeneral End If End Sub
Você chamaria essa sub-rotina por meio de outra instrução, uma que invoque a sub-rotina e que passe o nome do banco de dados para ela. Por exemplo, se quisesse que o banco de dados fosse chamado Crash Carts.mdb , a instrução poderia ser CreateDatabaseWithDAO "Crash Carts.mdb"
A lógica da sub-rotina é a seguinte: 1. Verifica se um banco de dados com o nome que foi passado já existe no caminho padrão. Essa
é a finalidade da função Dir. 2. Se
esse banco de dados já existir, exibe uma caixa de mensagem informando o usuário e perguntando se a versão existente deve ser excluída.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
249
3. Se
o usuário disser sim clicando no botão Sim da caixa de mensagem, a versão existente é excluída e um booleano, GoAhead, é configurado como VERDADEIRO.
4. Se o usuário disser não não clicando no botão Sim da caixa de mensagem, o banco de dados
não é excluído. Simplesmente exibe uma caixa de mensagem que diz que a versão existente será deixada como está e o booleano GoAhead configurado como FALSO. 5. Se GoAhead for VERDADEIRO, o banco de dados é criado.
A parte crítica da sub-rotina é a instrução que realmente cria o banco de dados. Sua sintaxe completa é Set DatabaseVariable = Workspace.CreateDatabase _ (Name, Locale, Options)
Você precisaria da porção Set DatabaseVariable somente se já tivesse declarado DatabaseVariable como uma variável de objeto; por exemplo: Dim DatabaseVariable As DAO.Database
A sub-rotina CreateDatabaseWithDAO não faz nenhuma utilização adicional do banco de dados além de criá-lo, então a sub-rotina não o atribui a uma variável de objeto. Você especificaria um espaço de trabalho na instrução CreateDatabase se já tivesse invocado um novo e quisesse utilizá-lo. Omitindo essa especificação a instrução utiliza o padrão, o espaço de trabalho ativo. O próprio método CreateDatabase leva três argumentos: • Name — Esse estabelece o nome e a extensão do banco de dados. Você pode, opcionalmente, fornecer um caminho: Name:=”C:\NewDatabase.mdb” . Se você não fornecer um caminho, o caminho padrão da pasta de trabalho do Excel é utilizado. Esse argumento é requerido. • Locale — Esse especifica a ordem em que as strings de texto serão classificadas. Você quase sempre utilizará dbLangGeneral, que agrupa a maioria dos idiomas europeus, incluindo o inglês. Esse argumento é requerido. • Options — Esse argumento é opcional. Omita-o para criar um novo banco de dados baseado em sua versão atual do Office. Você pode criar um banco de dados baseado em uma versão anterior se quiser. Por exemplo, suponha que você esteja executando o Access 2003, mas a maioria de seus usuários esteja executando o Access 97. Você poderia utilizar Option:=dbVersion30 para criar um banco de dados compatível com Jet 3.5, e assim compatível com o Access 97.
Criando uma tabela com o DAO Depois de utilizar seu código para criar um novo banco de dados, seus usuários seguramente precisarão colocar uma ou mais tabelas nele. A abordagem mostrada nesta seção funciona em qualquer banco de dados do Access existente, quer você tenha ou não acabado de criá-lo.
250
Gerenciando dados com o Microsoft Excel
A T O N
Como verá, você não pode salvar uma nova tabela em um banco de dados existente sem colocar pelo menos um campo na tabela. Mas você pode salvar um novo banco de dados sem colocar nenh uma tabela nele. Isso é coerente com o processo de criação de um banco de dados que utiliza a interface de Access: um banco de dados vazio é perfeitamente válido, mas uma tabela desprovida de quaisquer campos não é.
Como de costume, você precisará de um módulo que tenha um conjunto de referências para uma biblioteca DAO. Com o conjunto de referências, você digita o seguinte código em um módulo. O código mostrado aqui é o código mínimo necessário para criar uma tabela em um banco de dados existente utilizando o DAO: Sub MakeNewTableWithDAO(DatabaseName As String, _ TableName As String, FieldName As String) Dim dbBackupData As DAO.Database Dim tdBackupTable As DAO.TableDef Dim fldBackupField As DAO.Field Set dbBackupData = OpenDatabase(DatabaseName) Set tdBackupTable = dbBackupData.CreateTableDef(TableName) Set fldBackupField = tdBackupTable.CreateField(FieldName, _ dbText, 45) tdBackupTable.Fields.Append fldBackupField dbBackupData.TableDefs.Append tdBackupTable End Sub
Talvez você também queira integrar esse código com o código que cria seu próprio banco de dados. Por exemplo DatabaseName="SalesBackup.mdb" TableName="GiftShopSales" FieldName="SalesAmount" CreateDatabaseWithDAO DatabaseName MakeNewTableWithDAO DatabaseName, TableName, FieldName
Esse código passa o mesmo nome de banco de dados para ambos os procedimentos, assegurando que quando as tabelas forem criadas e os campos acrescentados, eles entram no banco de dados que foi criado com a sub-rotina CreateDatabaseWithDAO .
A T O N
Suponha que o banco de dados do Access esteja aberto quando você executar o código que cria uma nova tabela e que a guia Tabelas esteja ativa. Se você alternar agora para o Access, talvez não veja o nome da nova tabela. Isso ocorre porque você precisa atualizar a lista de tabelas no banco de dados. Clique em outra guia, como Consultas, e depois retorne à guia Tabelas para ver a nova tabela.
Obtendo nomes a partir do usuário Naturalmente, você evita a abordagem de nome estático na pasta de trabalho em desenvolvimento. Raramente é sensato atribuir valores estáticos a variáveis alfanuméricas ( string variables ) — ou a variá-
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
251
veis de qualquer outro tipo de dados, nesse sentido — no próprio código. Mas o código mostrado anteriormente faz exatamente isso aos nomes do banco de dados, da tabela e ao campo da tabela. Uma boa alternativa, que permite ao usuário alguma flexibilidade realista em nomear os objetos, é pegar os nomes da pasta de trabalho do usuário. Você poderia obter o nome do banco de dados do nome da pasta de trabalho, o nome de tabela do nome da planilha ativa e o nome do campo de um cabeçalho de coluna. Por fim o usuário irá inserir dados em uma planilha na pasta de trabalho que você está desenvolvendo. Suponha que a planilha se pareça com a mostrada na Figura 10.1. Figura 10.1 Observe que o comprimento máximo de um valor em A2:A20 é 6, e compare com o design de tabela na Figura 10.2.
Agora você poderia utilizar código como este para personalizar o banco de dados da maneira que o usuário estruturou a planilha: Option Explicit Option Base 1 Option Base 1 é uma maneira de assegurar que as matrizes do VBA referem-se a seu primeiro
elemento como 1. O padrão é 0, assim, a menos que organize de outro modo, o primeiro elemento de uma matriz é seu zero-ésimo. Se você escolher Option Base 1 na parte superior de um módulo do VBA, todas as matrizes declaradas nesse módulo referenciarão seu primeiro elemento como elemento número 1. Type FieldType FieldName As String FieldLength As Integer End Type
O VBA suporta tipos de dados definidos pelo usuário . Isto é, você pode declarar uma variável como algo diferente de String, Integer, Date ou Variant. Se já definiu seu próprio tipo, você pode declarar subseqüentemente uma variável para ser desse tipo. Em muitos casos, isso ocorre porque você quer misturar dois tipos de dados incompatíveis em uma matriz. No exemplo atual, será útil ter uma matriz que contém informações sobre os nomes de campos (dados de texto) e sobre o comprimento de cada campo (dados numéricos). Uma alternativa é declarar uma variável de tipo Variant, que pode armazenar qualquer mistura de tipos de dados. Masessa normalmente é uma abordagem preguiçosa e nega a você um dos benefícios de declarar um tipo: elementos identificados.
252
Gerenciando dados com o Microsoft Excel
Declarando um tipo — aqui, FieldType — no começo, o código mais tarde pode declarar uma matriz de tipo FieldType. Há várias vantagens em fazer isso. Nesse caso, as duas razões mais importantes são: • É possível ter uma única matriz que consiste em uma coluna de dados de texto e uma coluna de dados numéricos. • É fácil e autodocumentável referenciar um elemento chamado FieldName e um chamado FieldLength. Ao contrário, se declarasse a matriz como Variant, você teria de referenciar a primeira coluna e a segunda coluna por número em vez de por nome e teria de se lembrar qual é qual. Depois de um ano ou dois, outra pessoa talvez tenha de manter esse código. Considerando o peso que você estaria colocando nas costas dela, é recomendável ficar longe dos becos escuros. Depois de declarar as opções que estão em efeito e um tipo de dados definido pelo usuário, você continua desenvolvendo suas próprias sub-rotinas. Você utiliza uma sub-rotina chamada Driver para iniciar o processamento. Eis o código: Sub Driver() Dim Dim Dim Dim Dim Dim Dim
DatabaseName As String LockFileName As String TableName As String FieldCount As Integer i As Integer GoAhead As Boolean FieldArray() As FieldType
Observe que FieldArray é dimensionado com um par de parênteses vazios e declarado como FieldType. As implicações disso, junto com ReDim, são discutidas mais adiante nesta seção. Depois de declarar diversas variáveis, você se prepara para armazenar os nomes obtidos da pasta de trabalho do Excel nessas variáveis. Essa abordagem evita os problemas criados trabalhando com nomes constantes, estáticos. DatabaseName = Left(ThisWorkbook.Name, Len(ThisWorkbook.Name) - 4) DatabaseName = ThisWorkbook.Path & "\" & DatabaseName & ".mdb" LockFileName = ThisWorkbook.Path & "\" & DatabaseName & ".ldb"
Você armazena o nome da pasta de trabalho na variável DatabaseName. Observe que a função Left é utilizada junto com a função Len para eliminar os quatro caracteres mais à direita no nome da pasta de trabalho, e os caracteres restantes são armazenados em DatabaseName. Então, se o nome da pasta de trabalho for DBTemplate.xls, os caracteres “.xls” são removidos e a string restante, DBTemplate, é armazenada em DatabaseName. Além disso, o caminho da pasta de trabalho que contém o código, bem como uma barra invertida, são prefixados ao nome de banco de dados. Isso assegura que qualquer caminho que o Excel atualmente considera ser o padrão, o banco de dados será criado no mesmo caminho que a pasta de trabalho. Então LockFileName recebe o valor de DatabaseName mais os caracteres “.ldb”, e os caracteres “.mdb” são acrescentados ao final de DatabaseName. Um procedimento posterior utiliza esses valores para testar a existência de um banco de dados com o nome atual (por exemplo, DBTemplate.mdb).
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
253
TableName = ActiveSheet.Name
Você atribui o nome da planilha ativa à variável TableName. Se o nome da planilha ativa for Planilha1, a tabela de banco de dados criada pelo código também será Planilha1.
Contando os campos Agora é preciso determinar o número de campos a colocar na tabela. Comece com esta instrução: FieldCount = ActiveSheet.Cells(1, 256).End(xlToLeft).Column
O número de campos que será colocado na tabela de banco de dados é determinado pelo número da coluna mais à direita na linha 1 que tenha um valor.
A T O N
Essa instrução não é bem verdadeira. O método End(xlToLeft) emula o que acontece quando você seleciona uma célula, mantém pressionada a tecla Ctrl e pressiona a seta esquerda. Suponha que você inicie com a célula IV1. Se IV1 estiver vazia, o Excel vai para a esquerda até encontrar uma célula não vazia e pára ali. Se IV1 não estiver vazia mas IU1 estiver, o Excel vai para a esquerda até localizar uma célula não-vazia. Se nem IV1 nem IU1 estiverem vazias, o Excel vai para a esquerda até localizar a última célula não vazia contígua. O código faz a suposição (válida na maioria das situações da vida real) de que a célula IV1 está vazia; portanto, o Excel vai para a esquerda até localizar a primeira célula não-vazia — adotada aqui como a coluna mais à direita de uma lista.
Observe a instrução de atribuição para FieldCount. Essa instrui o VBA a iniciar na primeira linha, coluna 256 da planilha ativa e seguir para a esquerda até localizar uma célula utilizada. A suposição é de que o número da coluna contendo essa célula fornece o número de campos a criar. Idealmente, o usuário criou uma lista que ocupa, digamos, as colunas de A a G e um número indeterminado de linhas. Então o código descobre que a célula G1 não está vazia. Como G é a sétima coluna, o valor de 7 é atribuído a FieldCount.
Mantendo a flexibilidade com ReDim A próxima instrução redimensiona FieldArray de acordo com o valor de FieldCount: ReDim FieldArray(FieldCount) ReDim é um comando interessante e útil no VBA. Seu valor se origina em parte do fato de que
você não pode utilizar uma variável como um argumento para uma instrução Dim. Por exemplo, suponha — como é o caso — que o código tenha por este ponto determinado o valor de FieldCount, o número de campos a colocar na tabela de banco de dados. A próxima tarefa é colocar os nomes desses campos na matriz de memória FieldArray. Mas ele ainda não dimensionou FieldArray; isto é, o código ainda não declarou que FieldArray contém três ou seis linhas ou algum outro número de linhas. Se o código agora dimensionasse FieldArray com esta instrução: Dim FieldArray(FieldCount)
o código falharia com um erro: Constant expression required. Em outras palavras, é necessário um valor constante em vez da variável FieldCount.
254
Gerenciando dados com o Microsoft Excel
Mas ReDim é planejado especificamente para esse tipo de situação. Esta instrução ReDim FieldArray(FieldCount)
atribui o mesmo número de colunas à matriz FieldArray que atribui ao valor de FieldCount. A C I D
Antes de poder redimensionar uma matriz com ReDim, você já deve ter declarado a matriz com Dim. Além disso, na instrução Dim, o nome da matriz deve ser seguido por um par de parênteses vazios.
For i = 1 To FieldCount FieldArray(i).FieldName = ActiveSheet.Cells(1, i) Next i
Esse loop coloca cada valor na linha de cabeçalho da lista de planilha no elemento FieldName de FieldArray. (Lembre-se de que a primeira linha de uma lista em uma planilha contém os nomes das variáveis, ou campos, que quando tomados juntos produzem uma lista.)
Completando as definições de campo A próxima tarefa é determinar o comprimento de cada campo no banco de dados. Isso implica que cada campo é um campo Text. Seria possível incluir testes para determinar se um campo na lista é numérico e, se for, quer seja de precisão simples ou dupla, uma data, moeda e assim por diante. Assim, fazer isso introduziria complicações consideráveis e você decide assumir que todos os campos do usuário conterão texto. (Um campo Text em uma tabela do Access pode conter tipos e subtipos numéricos, embora ele os armazene como texto. Isso é análogo a digitar '4 em uma célula do Excel: parece um número, mas é armazenado como texto.) O seguinte loop testa o comprimento máximo de cada campo na lista do Excel e armazena-o no elemento FieldLength de FieldArray, onde é associado diretamente com o nome do campo. For i = 1 To FieldCount RecordCount = ActiveSheet.Cells(65536, i).End(xlUp).Row For j = 2 To RecordCount If Len(ActiveSheet.Cells(j, i)) > FieldArray(i).FieldLength Then FieldArray(i).FieldLength = Len(ActiveSheet.Cells(j, i)) End If Next j Next i
Você utiliza esse loop aninhado para obter o número de registros em cada campo da lista da planilha. Suponha que o registro final (ou mesmo os dois ou três registros finais) na lista está perdendo um valor em um ou mais campos. Nesse caso, verificar o número de linhas utilizadas apenas em uma coluna pode resultar em uma contagem menor no número de registros em outra coluna. Portanto, você organiza o loop para verificar o número de linhas utilizadas em cada coluna e armazena esse valor em RecordCount. O loop interno utiliza o valor atual de RecordCount para determinar quantas células verificar na coluna atual. O comprimento de cada célula é verificado por meio da função Len. Se o resultado for maior que o valor atual do elemento FieldLength desse campo, ele substitui esse valor atual. Quando o loop examinar a célula final na coluna atual, ele determinará o comprimento do valor mais longo na coluna e o armazenará no elemento FieldLength de FieldArray.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
255
CreateDatabaseWithDAO DatabaseName, LockFileName, GoAhead
Agora a sub-rotina Driver principal chama a sub-rotina nomeada CreateDatabaseWithDAO, discutida na próxima seção. Note o uso da variável booleana GoAhead como um argumento que é passado à sub-rotina. Até aqui, sua sub-rotina Driver não fez nada com a variável. Mas a variável é passada para CreateDatabaseWithDAO, o que talvez modifique seu valor. Quando o controle retornar à sub-rotina Driver, GoAhead pode ser utilizado para determinar se continua adicionando uma tabela a um banco de dados. O ponto a lembrar é que o valor de uma variável pode ser modificado por uma sub-rotina chamada. É esse valor modificado que está subseqüentemente disponível à sub-rotina chamadora.
A T O N
Esse comportamento é o padrão e é chamado de passagem por referência. Quando, como aqui, uma variável é passada por referência, seu valor pode ser modificado por um procedimento chamado. O valor modifi cado é retornado ao procedimento chamador.Se sobrescrever o comportamento padrão e passar a variável por valor , o procedimento chamado poderia modificar o valor da variável, mas o valor alterado não seria retornado ao procedimento chamador.
Depois que o novo banco de dados foi criado, a sub-rotina Driver conclui com as seguintes instruções: If GoAhead Then MakeNewTableWithDAO DatabaseName, TableName, FieldName() End If End Sub
Seu código primeiro verifica o valor de GoAhead. Se for TRUE, isso significa que um novo banco de dados foi criado e o código pode continuar colocando uma nova tabela nele. Depois que isso for realizado, você termina a sub-rotina Driver principal e o processamento terá sido concluído. A sub-rotina Driver é longa. Ela é mostrada aqui em sua totalidade para tornar mais fácil seguir sua estrutura visualizando tudo de uma vez, em vez de dividir por explicações. Sub Driver() Dim Dim Dim Dim Dim Dim Dim Dim Dim
DatabaseName As String x LockFileName As String x TableName As String x FieldArray() As FieldType x FieldCount As Integer x RecordCount As Long x i As Integer x j As Integer x GoAhead As Boolean x
DatabaseName = Left(ThisWorkbook.Name, Len(ThisWorkbook.Name) - 4) LockFileName = ThisWorkbook.Path & "\" & DatabaseName & ".ldb" DatabaseName = ThisWorkbook.Path & "\" & DatabaseName & ".mdb" TableName = ActiveSheet.Name FieldCount = ActiveSheet.Cells(1, 256).End(xlToLeft).Column ReDim FieldArray(FieldCount)
256
Gerenciando dados com o Microsoft Excel For i = 1 To FieldCount FieldArray(i).FieldName = ActiveSheet.Cells(1, i) Next i For i = 1 To FieldCount RecordCount = ActiveSheet.Cells(65536, i).End(xlUp).Row For j = 2 To RecordCount If Len(ActiveSheet.Cells(j, i)) > FieldArray(i).FieldLength Then FieldArray(i).FieldLength = Len(ActiveSheet.Cells(j, i)) End If Next j Next i CreateDatabaseWithDAO DatabaseName, LockFileName, GoAhead If GoAhead Then MakeNewTableWithDAO DatabaseName, TableName, FieldArray() End If End Sub
Verificando um banco de dados preexistente Sua sub-rotina Driver chama outra sub-rotina chamada
CreateDatabaseWithDAO.
Sub CreateDatabaseWithDAO(DatabaseName As String, _ LockFileName As String, GoAhead As Boolean)
Ela chama essa sub-rotina com três argumentos: •
DatabaseName — Este é o nome com o qual o banco de dados será salvo, incluindo a extensão .mdb. Como viu
na sub-rotina Driver, DatabaseName é baseado no nome da pasta de trabalho
do Excel ativa. •
LockFileName — Este é
o nome do banco de dados com a extensão .ldb em vez de .mdb. Na maioria dos casos, quando um banco de dados do Access estiver aberto, também existe um arquivo de bloqueio. O nome do arquivo de bloqueio é o nome do banco de dados e a extensão .ldb. Essa sub-rotina verifica a existência de um arquivo de bloqueio antes de tentar apagar um banco de dados existente.
•
GoAhead —
Esta variável booleana é utilizada para determinar se é necessário criar o novo banco de dados. Se GoAhead for FALSO, o banco de dados não poderá ser criado. O usuário pode configurar GoAhead como FALSO recusando-se a permitir que a sub-rotina crie o banco de dados; o código pode configurá-lo para FALSO se o banco de dados já existir e estiver aberto.
A T O N
A única circunstância em que o Access abre um banco de dados do Access sem também criar um arquivo de bloqueio (ou utilizar um arquivo de bloqueio existente) é quando o usuário abriu o banco de dados no modo somente leitura e no modo Exclusivo. Há uma situação em que ninguém está utilizando um banco de dados do Access e contudo o arquivo de bloqueio ainda existe. Isso ocorre quando o usuário final fechou o banco de dados e esse usuário não tem permissão de exclusão para arquivos em sua pasta. Nesse caso, o arquivo de bloqueio permanece no lugar até que algum outro usuário, um que tenha permissão de exclusão, seja o usuário final a fechar o banco de dados.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
257
As próximas várias linhas de código determinam o que fazer se um arquivo com um nome idêntico ao armazenado na variável DatabaseName já existir. Comece configurando a variável booleana GoAhead como TRUE. A menos que o código altere seu valor para FALSE, o banco de dados será criado no final da sub-rotina. GoAhead = True
A função Dir verifica se um arquivo que corresponde ao valor do seu argumento já existe no diretório atual. Se esse arquivo existir, Dir retorna o nome do arquivo; caso contrário, Dir retorna uma string nula (representada no código como um par vazio de aspas). Então, se o valor que Dir retorna for algo diferente de uma string nula, o arquivo já existe e tratamento adicional é necessário. If Dir(DatabaseName) <> "" Then If MsgBox(Prompt:="Delete existing " & DatabaseName & "?", _ Buttons:=vbYesNo, Title:="Naming conflict.") = vbYes Then
Se a função Dir localiza um arquivo chamado DatabaseName no diretório atual — isto é, se retorna um valor diferente de uma string nula — então o código exibe uma caixa de mensagem perguntando ao usuário se ele quer excluir o arquivo existente. Uma caixa de mensagem pode retornar um valor. Nesse caso, os botões exibidos na caixa de mensagem são um botão Sim e um botão Não. Se o usuário clicar no botão Sim, o valor retornado pela caixa de mensagem é vbYes; se o usuário clicar no botão Não, a caixa de mensagem retorna vbNo. Se o usuário clicar no botão Sim, o código continua verificando a existência de um arquivo de bloqueio. A razão é que se o arquivo existente chamado DatabaseName for um banco de dados do Access e se estiver aberto, o código terminará com um erro se tentar excluir o arquivo. Verificando a existência de um arquivo chamado LockFileName, é muito provável que o arquivo chamado DatabaseName seja excluído sem causar um erro. Mas “muito provável” não é bom o bastante e o código inclui um manipulador de erros. If Dir(LockFileName) <> "" Then MsgBox Prompt:=DatabaseName & " is already open and " _ & "cannot be erased. Taking no action.", _ Title:="File already open." GoAhead = False
O código descobre que o arquivo de bloqueio existe e então que o banco de dados não só existe mas está aberto. Ele notifica o usuário e configura GoAhead como FALSE. Else On Error GoTo Recover Kill DatabaseName On Error GoTo 0 GoAhead = True End If
Se o código não localizar o arquivo de bloqueio, ele se prepara para excluir o arquivo chamado DatabaseName. Entretanto, como observado anteriormente, é possível que o banco de dados esteja aberto mesmo que nenhum arquivo de bloqueio tenha sido localizado. Portanto, um manipulador de erros é estabelecido. O código é instruído, por meio da instrução On Error GoTo , a transferir o controle ao código que se segue ao rótulo Recover se um erro ocorrer. Isso ocorreria se o código devesse tentar em vão
258
Gerenciando dados com o Microsoft Excel
excluir o arquivo DatabaseName na instrução Kill. Nesse evento, o código mostrado no final da sub-rotina executa. Observe a instrução On Error GoTo 0 . Isso cancela a diretiva da instrução On Error precedente. O manipulador de erros que se segue ao rótulo Recover é planejado somente para um erro causado pela instrução Kill . Se o código terminar essa instrução sem um erro, o manipulador é irrelevante e assim o tratamento de erro é retornado ao seu status normal. Else MsgBox Prompt:="Okay, leaving existing " & DatabaseName _ & " alone.", Title:="Taking no action." GoAhead = False End If End If
As seis instruções anteriores completam a lógica do primeiro bloco If, que solicita o usuário o que fazer se uma instância de DatabaseName já existe. Se seu usuário clicar no botão Não na primeira caixa de mensagem, isso significa deixar o arquivo só e a variável GoAhead é configurada como FALSE. If GoAhead Then CreateDatabase Name:=DatabaseName, Locale:=dbLangGeneral End If Exit Sub
O código verifica o valor de GoAhead . Se ainda for TRUE, um banco de dados é criado com o nome DatabaseName. Por enquanto ele está vazio. Observe a instrução Exit Sub após o End If . Se a instrução for alcançada, o controle retorna imediatamente ao procedimento que chamou a subrotina (como você viu, esse procedimento é a sub-rotina Driver). A razão de sair da sub-rotina via a instrução Exit Sub é que o código restante é o manipulador de erros. Ele não deve executar a menos que um erro tenha ocorrido, então no curso normal de eventos, o controle retorna a Driver antes de o manipulador de erros executar. Mas se realmente ocorresse um erro, o seguinte código executaria: Recover: MsgBox Prompt:="Could not delete " & DatabaseName & _ ". It's likely that a user has opened it in exclusive " _ & "mode and read only. Taking no action.", Title:= _ "File already open." GoAhead = False End Sub
O manipulador de erros simplesmente informa o usuário de que o código não pode excluir a instância existente do arquivo chamado DatabaseName e então configura GoAhead como FALSE. Vale observar que Recover: não é uma instrução executável, mas meramente um rótulo de linha. Como tal, ele deve começar na primeira coluna (rótul os não podem ser recuados), deve ser a única instância desse rótulo em particular no procediment o e deve terminar com um dois-pontos.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
259
Eis o procedimento completo: Sub CreateDatabaseWithDAO(DatabaseName As String, _ LockFileName As String, GoAhead As Boolean) GoAhead = True If Dir(DatabaseName) <> "" Then If MsgBox(Prompt:="Delete existing " & DatabaseName & "?", _ Buttons:=vbYesNo, Title:="Naming conflict.") = vbYes Then If Dir(LockFileName) <> "" Then MsgBox Prompt:=DatabaseName & " is already open and " _ & "cannot be erased. Taking no action.", _ Title:="File already open." GoAhead = False Else On Error GoTo Recover Kill DatabaseName On Error GoTo 0 GoAhead = True End If Else MsgBox Prompt:="Okay, leaving existing " & DatabaseName _ & " alone.", Title:="Taking no action." GoAhead = False End If End If If GoAhead Then CreateDatabase Name:=DatabaseName, Locale:=dbLangGeneral End If Exit Sub Recover: MsgBox Prompt:="Could not delete " & DatabaseName & _ ". It's likely that a user has opened it in exclusive " _ & "mode and read only. Taking no action.", Title:= _ "File already open." GoAhead = False End Sub
Criando uma nova tabela no banco de dados com o DAO
Até agora seu código criou o novo banco de dados utilizando a sub-rotina CreateDatabaseWithDAO. Sua sub-rotina Driver coletou as informações sobre a tabela que o banco de dados conterá e os campos que a tabela conterá. É hora de realmente criar a tabela e acrescentar os novos campos. Como viu no final da seção “Completando as definições de campo”, quando a subrotina CreateDatabaseWithDAO é concluída, o controle retorna à sub-rotina Driver. Driver então verifica o valor de GoAhead e, se for TRUE, chama a seguinte sub-rotina: Sub MakeNewTableWithDAO(DatabaseName As String, _ TableName As String, FieldArray() As FieldType)
Esse procedimento aceita três argumentos: uma string que fornece o nome do banco de dados, o nome da tabela a ser inserida no banco de dados e uma matriz de nomes de campo a ser inserida na tabela.
260
Gerenciando dados com o Microsoft Excel
Dim Dim Dim Dim Dim
dbDataFile As DAO.Database tdDataTable As DAO.TableDef fldDataField As DAO.Field FieldCount As Integer i As Integer
Ao contrário da sub-rotina CreateDatabaseWithDAO, a sub-rotina MakeNewTableWithDAO utiliza diversas variáveis de objeto. Elas são declaradas nas instruções Dim. Observe que as primeiras três variáveis são declaradas como objetos cujos tipos (Database, TableDef e Field) são qualificados como objetos DAO. Isso é para proteger contra a possibilidade de outra biblioteca de objetos, referenciada pelo módulo, com o seu próprio tipo Database, TableDef ou Field. Set dbDataFile = OpenDatabase(DatabaseName) Set tdDataTable = dbDataFile.CreateTableDef(TableName)
O banco de dados foi criado pela sub-rotina CreateDatabaseWithDAO , mas não foi aberto. Abrindo-o com OpenDatabase , ele é estabelecido como um objeto e atribuído à variável de objeto dbDataFile. Então o método CreateTableDef é utilizado em conjunção com a variável alfanumérica TableName para criar a tabela e atribuí-la à variável de objeto tdDataTable . Com a tabela estabelecida, é hora de inserir os campos. Comece determinando quantos nomes de campo a sub-rotina Driver coloca na matriz. O código determina utilizando a função UBound na matriz. UBound retorna o número do elemento final em uma matriz. É por essa razão que Option Base 1 foi utilizado no início do módulo. Caso contrário, o desenvolvedor teria de se lembrar de adicionar 1 a qualquer número UBound retornado. FieldCount = UBound(FieldArray) For i = 1 To FieldCount Set fldDataField = tdDataTable.CreateField(FieldArray(i).FieldName, _ dbText, FieldArray(i).FieldLength) tdDataTable.Fields.Append fldDataField Next i
Um loop executa de 1 a FieldCount . Para cada registro em FieldArray , o método CreateField é utilizado para criar um novo campo na tabela de dados. Cada campo é nomeado de acordo com o valor no elemento FieldName. O campo é representado como Text por meio do argumento dbText. O comprimento do campo é configurado de acordo com o valor no elemento FieldLength. Observe que é necessário acrescentar explicitamente cada campo à tabela utilizando o método Append da coleção Fields. Simplesmente criar o campo não é suficiente. dbDataFile.TableDefs.Append tdDataTable End Sub
Quando todos os campos tiverem sido criados, nomeados e recebido um tipo e comprimento, seu código acrescenta a própria tabela à coleção de tabelas no banco de dados. Novamente, o método Append é utilizado, mas desta vez na coleção TableDefs em vez de na coleção Fields . Eis procedimento completo:
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
261
Sub MakeNewTableWithDAO(DatabaseName As String, _ TableName As String, FieldArray() As FieldType) Dim Dim Dim Dim Dim
dbDataFile As DAO.Database tdDataTable As DAO.TableDef fldDataField As DAO.Field FieldCount As Integer i As Integer
Set dbDataFile = OpenDatabase(DatabaseName) Set tdDataTable = dbDataFile.CreateTableDef(TableName) FieldCount = UBound(FieldArray) For i = 1 To FieldCount Set fldDataField = tdDataTable.CreateField _ (FieldArray(i).FieldName, _ dbText, FieldArray(i).FieldLength) tdDataTable.Fields.Append fldDataField Next i dbDataFile.TableDefs.Append tdDataTable End Sub
Se o banco de dados criado pelo código discutido nesta seção fosse baseado na planilha mostrada na Figura 10.1, seria semelhante ao mostrado na Figura 10.2. Figura 10.2 Observe que o comprimento de campo do primeiro campo é 6 e compare-o com a Figura 10.1.
Resta preencher a tabela com os registros na planilha do Excel. Esse processo é tratado na seção “Declarando e utilizando recordsets”. Primeiro, porém, é útil examinar uma abordagem ligeiramente diferente para criar as tabelas e campos em um banco de dados existente.
Criando uma tabela e campos com o ADO Este capítulo observou no começo que o ADO não é muito adequado para criar novos bancos de dados; sua força reside em mover dados de um lado para o outro entre um banco de dados e alguma outra aplicação como o Excel. Portanto, se estiver utilizando o Access e seu mecanismo de bancos de dados Jet como o banco de dados, faz muito sentido criar bancos de dados utilizando o DAO (como mostrado nas
262
Gerenciando dados com o Microsoft Excel
seções anteriores deste capítulo) ou diretamente com a interface de usuário do banco de dados. Se estiver utilizando outro DBMS, como SQL Server, é muito mais fácil utilizar sua interface para criar o banco de dados. Entretanto, depois que o banco de dados existe, o ADO oferece ferramentas que são inteiramente apropriadas para criar novas tabelas, campos e outras estruturas. O que se segue é um exemplo de como configurar uma tabela utilizando ADO. ESTUDO DE CASO Você foi solicitado a preparar um banco de dados chamado ShortStay, que conterá as informações sobre pacientes de hospital que gastam somente um curto período de tempo como pacientes internados. O banco de dados precisa manter as informações sobre os pacientes, os procedimentos médicos e cirúrgicos utilizados por cada um, o custo de estoques, o pessoal do hospital envolvido e assim por diante. Tendo configurado o banco de dados ShortStay utilizando DAO, em certo ponto mais tarde você descobre que precisa criar uma tabela que contenha um campo com os nomes dos procedimentos médicos e uma que contenha o ID de um procedimento, que será a chave primária da tabela. Você utilizará a tabela subseqüentemente como uma tabela de pesquisa: você armazenará o ID de um procedimento na tabela de dados principal e exibirá seu nome ao trazer um registro de volta ao Excel a partir do banco de dados. Você começa inserindo um novo módulo em sua pasta de trabalho e configurando referências a estas bibliotecas: • Microsoft ActiveX Data Objects 2.x Library • Microsoft ADO Ext. 2.x for DDL and Security • As referências padrão ao VBA, a biblioteca de objetos do Excel, Automação OLE e a biblioteca de objetos do Office A T O N
Em ambos os casos, 2.x refere-se à versão de nível mais alto em seu sistema. (Se estiver executando o Office 97, você provavelmente tem a versão 2.1; se estiver executando o Office 2003, você provavelmente tem a versão 2.7.)
Você introduz o seguinte código no módulo: Sub NewShortStayTable() Dim cnConnectToShortStay As ADODB.Connection Dim SourceName As String Dim rsProcs As ADODB.Recordset Dim i As Integer Dim LastProc As Long Dim catDatabaseFile As ADOX.Catalog Dim tdfProcs As ADOX.Table
Há dois novos itens nessa lista de declarações: um catálogo ADOX e uma tabela ADOX. Ambos pertencem às ADO Extensions mencionadas no começo do capítulo. Um catálogo, como utilizado aqui, é idêntico a um banco de dados. O ADO utiliza-o como um contêiner dos objetos que você normalmente encontra em um banco de dados: tabelas, consultas e assim por diante. A tabela ADOX não é diferente das tabelas que foram discutidas e utilizadas até agora neste livro. Set cnConnectToShortStay = New ADODB.Connection Set catDatabaseFile = New ADOX.Catalog Set tdfProcs = New ADOX.Table
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
263
Três variáveis de objeto são configuradas: uma variável de conexão que será apontada no banco de dados ShortStay, uma variável de catálogo que representará o próprio banco de dados e uma variável de tabela que representará a nova tabela a ser criada. SourceName = ThisWorkbook.Path & "\ShortStay.mdb" cnConnectToShortStay.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & SourceName
A variável SourceName é utilizada para armazenar o caminho e o nome do banco de dados. A conexão especifica o provedor e o caminho e nome, exatamente como no Capítulo 8. Set catDatabaseFile.ActiveConnection = cnConnectToShortStay
O código conecta-se ao banco de dados por meio da conexão. tdfProcs.Name = "Procedures"
A nova tabela é nomeada Procedures. Então dois novos campos são acrescentados à tabela. Note a diferença de DAO, onde um novo campo é primeiro estabelecido e depois acrescentado à coleção de campos da tabela. Em ADO, você nomeia o campo, especifica seu tipo e acrescenta à tabela em um passo, como mostrado nas duas próximas instruções: tdfProcs.Columns.Append "ProcedureID", adInteger tdfProcs.Columns.Append "ProcedureName", adVarWChar, 50
A primeira instrução acrescenta um novo campo chamado ProcedureID à tabela Procedures. A segunda instrução acrescenta um novo campo chamado ProcedureName. O tipo adVarWChar resulta em um campo Text no Access. O argumento final, 50, especifica um comprimento máximo de 50 caracteres para o campo. tdfProcs.Keys.Append "PrimaryKey", adKeyPrimary, "ProcedureID"
Uma chave primária é estabelecida para a tabela. A partir da discussão no Capítulo 9, “Gerenciando objetos de banco de dados”, lembre-se de que uma tabela pode ter mais de um índice. Se a tabela tiver uma chave primária, esse é apenas um índice em particular, distinguido pelo fato de que ele não permite valores duplicados e que somente um índice por vez pode ser designado como a chave primária da tabela. A chave primária é estabelecida aqui simplesmente acrescentando uma chave à coleção de chaves da tabela (o ADO utiliza o termo chaves enquanto o Access utiliza o termo índices; eles são em grande parte sinônimos). A chave é nomeada PrimaryKey, seu tipo é adKeyPrimary (outras opções são adKeyUnique para criar um índice único que não seja a chave primária e adKeyForeign para vincular à outra chave primária da tabela). A instrução especifica ProcedureID como a base da chave. catDatabaseFile.Tables.Append tdfProcs
A tabela recém-criada é acrescentada à coleção de tabelas do catálogo e é hora de colocar registros na tabela. Seu código começa contando o número de registros na planilha (veja a Figura 10.3). LastProc = ThisWorkbook.Worksheets("Procedures") _ .Cells(65536, 1).End(xlUp).Row
Então estabeleça e abra um novo recordset baseado na tabela Procedures.
264
Gerenciando dados com o Microsoft Excel
Figura 10.3 Se as procedures forem posteriormente utilizadas em uma lista suspensa, é útil começar classificando-as alfabeticamente.
Set rsProcs = New ADODB.Recordset rsProcs.Open "Procedures", cnConnectToShortStay, _ adOpenStatic, adLockOptimistic, adCmdTable
Estabeleça um bloco With e execute um loop For-Next para preencher a tabela Procedures. Durante cada ciclo pelo loop, seu contador i é utilizado para fornecer um valor único ao campo de chave primária, e o nome da procedure é colocado em seu campo. With rsProcs For i = 2 To LastProc .AddNew .Fields("ProcedureID") = i - 1 .Fields("ProcedureName") = ThisWorkbook.Sheets("Procedures").Cells(i, 1) .Update Next i End With
Codifique configurando as variáveis de objeto como Nothing (assim liberando as variáveis) e fechando a conexão. Set tdfProcs = Nothing Set catDatabaseFile = Nothing cnConnectToShortStay.Close Set cnConnectToShortStay = Nothing End Sub
Você agora tem uma nova tabela no banco de dados, com uma chave primária e um campo de texto, como mostrado na Figura 10.4.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
Figura 10.4 Pode ser conveniente, mas não é descritivo nomear uma chave primária da tabela como PrimaryKey .
Eis a sub-rotina completa: Sub Dim Dim Dim Dim Dim Dim Dim
NewShortStayTable() cnConnectToShortStay As ADODB.Connection catDatabaseFile As ADOX.Catalog tdfProcs As ADOX.Table SourceName As String rsProcs As ADODB.Recordset i As Integer LastProc As Long
Set cnConnectToShortStay = New ADODB.Connection Set catDatabaseFile = New ADOX.Catalog Set tdfProcs = New ADOX.Table SourceName = ThisWorkbook.Path & "\ShortStay.mdb" cnConnectToShortStay.Open _ "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & SourceName Set catDatabaseFile.ActiveConnection = cnConnectToShortStay tdfProcs.Name = "Procedures" tdfProcs.Columns.Append "ProcedureID", adInteger tdfProcs.Columns.Append "ProcedureName", adVarWChar, 50 tdfProcs.Keys.Append "PrimaryKey", adKeyPrimary, "ProcedureID" catDatabaseFile.Tables.Append tdfProcs Set rsProcs = New ADODB.Recordset LastProc = ThisWorkbook.Worksheets("Procedures") _ .Cells(65536, 1).End(xlUp).Row
265
266
Gerenciando dados com o Microsoft Excel
rsProcs.Open "Procedures", cnConnectToShortStay, _ adOpenForwardOnly, adLockOptimistic With rsProcs For i = 2 To LastProc .AddNew .Fields("ProcedureID") = i - 1 .Fields("ProcedureName") = ThisWorkbook _ .Sheets("Procedures").Cells(i, 1) .Update Next i End With Set tdfProcs = Nothing Set catDatabaseFile = Nothing cnConnectToShortStay.Close Set cnConnectToShortStay = Nothing End Sub
Declarando e utilizando recordsets É quando você começa a utilizar recordsets que você começa a impor cargas significativas sobre os recursos do sistema. Como o Capítulo 9 mencionou, as consultas de SQL são poderosas e fazem uso relativamente eficiente de processadores e memória. Sempre que você puder utilizar razoavelmente uma consulta de SQL para acrescentar dados ou atualizar dados ou selecionar e exibir dados, certamente faça isso. Mas as consultas são ardilosas. Elas não executam bem em situações que exigem desvios e loops, por exemplo. Quanto mais complicada a lógica que você deve pôr em ação nos dados, menos atraente é a aparência da consulta de SQL. Os recordsets , por outro lado, permitem utilizar ferramentas mais delicadas. Você pode utilizar as capacidades extensas do VBA ao trabalhar com recordsets — e isso inclui o rico conjunto de funções de planilha que acompanha o Excel. Por exemplo, se quisesse adicionar registros a uma tabela dependendo de onde caem em relação a algum valor mediano, você quase certamente optaria por VBA em conjunção com um recordset, em vez de uma consulta de SQL. O SQL em geral não oferece uma função que retorna uma média. (Não é difícil de escrever por conta própria, mas por que se aborrecer quando já está disponível?) Um recordset é semelhante a uma lista do Excel ou a uma tabela de banco de dados. Também é semelhante à matriz chamada FieldArray que foi utilizada na seção chamada “Criando uma nova tabela no banco de dados com o DAO”: uma matriz de registros com elementos que podem ter tipos diferentes. Um recordset consiste em um ou mais registros e de um ou mais campos. Você declara um recordset no VBA depois de ter estabelecido uma referência a uma biblioteca DAO ou ADO. Então você pode declarar que um recordset existe, nomeá-lo e atribuí-lo a uma tabela de banco de dados ou consulta. Depois de estabelecer o recordset, você utiliza comandos do VBA para manipular seus dados adicionando a ele, excluindo dele, modificando os valores em seus campos, importando seus registros para outra localização como uma planilha do Excel ou um documento do Word e assim por diante.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
267
Como uma introdução a recordsets, considere o seguinte código, que estabelece um recordset utilizando o DAO. Você poderia utilizá-lo em conjunção com o código discutido na seção anterior que criou um novo banco de dados, uma tabela para possuir os registros e campos nessa tabela para conter os valores reais. Um bom lugar para colocá-lo seria no final da sub-rotina Driver, no final do bloco If: If GoAhead Then MakeNewTableWithDAO DatabaseName, TableName, FieldName() AddRecordsWithDAO DatabaseName, TableName, FieldCount End If
Eis o código. Estabelece um recordset com base na nova tabela de banco de dados e adiciona registros a ele. Os registros são obtidos da planilha. Sub AddRecordsWithDAO(DatabaseName As String, _ TableName As String, FieldCount As Integer) Dim i As Integer, j As Integer Dim RecordCount As Long, LastRowInColumn As Long Dim dbDataFile As DAO.Database Dim rsDataRecords As DAO.Recordset
Depois de declarar algumas variáveis, o código prossegue para configurar o banco de dados utilizando a variável DatabaseName (lembre-se que DatabaseName inclui o caminho ao banco de dados). Então o recordset nomeado rsDataRecords é estabelecido. Set dbDataFile = OpenDatabase(DatabaseName) Set rsDataRecords = dbDataFile.OpenRecordset(TableName, _ ➥ dbOpenTable)
A atribuição do recordset precisa de uma pequena explicação. A variável de objeto rsDataRecords é atribuída ao resultado do método OpenRecordset. Esse método utilizado aqui leva dois argumentos: •
Source —
A fonte do recordset é o nome de uma tabela ou consulta em que os registros se encontram ou em que os registros serão colocados. A fonte também pode ser uma consulta de SQL em formato de texto, embora esse uso seja raro na prática. No exemplo atual, a fonte do recordset é o valor de TableName.
•
Type — No DAO,
há vários tipos de recordsets, como dynaset, tipo de tabela e instantâneo. Os diferentes tipos e a implicação de escolher um tipo em particular são discutidos mais adiante neste capítulo, em “Entendendo os tipos de recordset do DAO”. O exemplo atual especifica um tipo de tabela. For i = 1 To FieldCount LastRowInColumn = ActiveSheet.Cells(65536, 1). _ End(xlUp).Row If RecordCount < LastRowInColumn Then RecordCount = LastRowInColumn End If Next i
Depois que o recordset foi estabelecido, o código determina o número de registros a ser obtido da planilha e colocado na tabela de banco de dados. Ele realiza isso fazendo loop pelo número de colunas (determinado anteriormente, na sub-rotina Driver) e localizando a célula final
268
Gerenciando dados com o Microsoft Excel
utilizada em cada uma delas. A variável RecordCount é utilizada para determinar o maior número de registros em qualquer coluna. O núcleo da sub-rotina está nas seguintes nove instruções. Essas instruções são incluídas dentro de um bloco With, que aceita o recordset como seu objeto. With rsDataRecords
Utilizando o bloco With, o código evita repetitivamente nomear o recordset e faz com que o código navegue pelo recordset a seus métodos e propriedades. O código então introduz um loop aninhado. O loop externo itera pelos registros. Observe que começa em 2, não em 1, porque na planilha os registros começam na linha 2, utilizando a linha 1 para os cabeçalhos. For j = 2 To RecordCount
A primeira instrução no loop externo adiciona um novo registro ao recordset. Um dos efeitos de adicionar um novo registro é torná-lo o registro atual; quaisquer operações de registro realizadas ocorrem no registro atual, até que um outro se torne o atual. Observe o uso do ponto antes da palavra-chave AddNew. O ponto significa que AddNew pertence ao objeto da instrução With; nesse caso, o recordset. O código está adicionando um novo registro ao recordset. .AddNew
Com um novo registro atual e no começo vazio, o loop interno executa. Ele itera pelos campos no recordset. Seu propósito é colocar o valor de cada coluna na linha atual da planilha dentro do campo correspondente no registro atual. Enquanto o loop interno está executando, o código permanece na mesma linha de planilha e coloca um valor em um campo no mesmo registro. For i = 1 To FieldCount .Fields(i - 1) = ActiveSheet.Cells(j, i) Next i
Há três itens de nota sobre esse loop interno. Primeiro, o contador j permanece constante. Não há nada no loop que o altere e portanto a referência Cells aponta para a mesma linha enquanto o loop executa. É o loop externo, aquele que itera pelas linhas da lista, que incrementa j. Segundo, observe o uso do ponto antes da palavra-chave Fields. Isso significa que os campos pertencem ao objeto chamado no With — novamente, esse é o recordset. No exemplo, como no método AddNew, a coleção Fields pertence a rsDataRecords. Observe também que os campos do recordset estão indexados por i–1 em vez de por i. O primeiro campo em um recordset é o número de campo 0, o segundo campo é o número de campo 1 e assim por diante. Isso é um pouco desconcertante até que você se acostume. ( Option Base 1 não tem nenhum efeito nos índices de campo, a propósito, apenas nas matrizes de memória.) Mas você provavelmente se acostumará com isso relativamente rápido porque, sem dúvida, a utilização mais comum de loops e índices de campo no VBA é mover dados de um lado a outro entre recordsets e planilhas — e as planilhas não têm nenhuma coluna de número zero. .Update
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
269
Ao adicionar um novo registro, como aqui, você o edita. Nesse exemplo, o processo de edição acontece no loop interno, onde o conteúdo das células da planilha é colocado no registro ativo do recordset. A edição não acontece no próprio recordset, mas em um buffer de cópia. Isto é, quanto o código pega os valores da planilha e aparentemente os armazena em campos no recordset, ele de fato coloca os valores no buffer de cópia temporariamente, uma posição da memória que o Access gerencia. Não é até que o código executa uma instrução Update que os valores são movidos do buffer de cópia e colocados no recordset. A qualquer momento que seu código utiliza uma instrução com AddNew (ou, se estiver editando um registro que já existe, uma instrução com Edit) você tem de segui-lo com uma Update. Caso contrário, os valores no buffer de cópia são perdidos. A T O N
O requisito Update é verdadeiro somente no código utilizando DAO. Uma instrução Update não é necessária se você estiver utilizando o ADO. Mas veja o Capítulo 12, na seção intitulada“Utilizando o ADO para adicionar registros”, as razões pelas quais você deve utilizá-la de qualquer jeito.
Next j End With rsDataRecords.Close dbDataFile.Close End Sub
Eis o código completo para AddRecordsWithDAO : Sub AddRecordsWithDAO(DatabaseName As String, _ TableName As String, FieldCount As Integer) Dim i As Integer, j As Integer Dim RecordCount As Long, LastRowInColumn As Long Dim dbDataFile As DAO.Database Dim rsDataRecords As DAO.Recordset Set dbDataFile = OpenDatabase(DatabaseName) Set rsDataRecords = dbDataFile.OpenRecordset(TableName, dbOpenTable) For i = 1 To FieldCount LastRowInColumn = ActiveSheet.Cells(65536, 1).End(xlUp).Row If RecordCount < LastRowInColumn Then RecordCount = LastRowInColumn End If Next i With rsDataRecords For j = 2 To RecordCount .AddNew For i = 1 To FieldCount .Fields(i - 1) = ActiveSheet.Cells(j, i) Next i .Update Next j End With rsDataRecords.Close dbDataFile.Close End Sub
270
Gerenciando dados com o Microsoft Excel
A Figura 10.5 mostra a aparência da tabela Admits na visualização de folha de dados depois que esse código colocou os registros na planilha na tabela. Figura 10.5 As horas mostradas na planilha foram salvas como números seriais de data/hora.
As horas da planilha foram salvas em um formato inconveniente. Talvez a maneira mais simples e direta de tratar isso seja depois do fato. Como observado anteriormente neste capítulo, seria possível determinar tipos de dados escrevendo código para examinar os dados da planilha, mas se não fizer isso, você pode converter os dados utilizando uma consulta de atualização. A Figura 10.6 mostra a consulta no modo de Design. Figura 10.6 A função CData no Access converte um número serial uma representação de data/ hora.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
271
Como mostrado na Figura 10.6, dois novos campos foram adicionados à tabela Admits: ConvertedAdmitTime e ConvertedDischargeTime. A consulta de atualização utiliza a função CData para converter as horas, representadas como String, da planilha para horas, representadas como Date/Time, no banco de dados. O resultado da execução da consulta é mostrado na Figura 10.7. Figura 10.7 A parte fracionária de um número serial especifica a hora do dia; a parte de inteiro especifica uma data.
Observe que as horas convertidas mostradas na Figura 10.7 correspondem às horas originais como mostradas na planilha na Figura 10.1.
Entendendo tipos de recordset DAO A biblioteca DAO oferece vários tipos diferentes de recordsets. Ao atribuir uma tabela ou uma consulta a um recordset por meio de uma instrução Set, você especifica o tipo de recordset que você quer utilizar. Sua escolha tem implicações nas demandas em recursos do sistema que seu código produzirá, bem como na maneira que seu código pode utilizar o recordset. Sua instrução Set pode assumir algum de vários formatos. Em cada caso, o método OpenRecordset é utilizado e aceita vários argumentos: • Source — Esse é o único argumento requerido. É uma string e é o nome do objeto que contém os registros. A fonte normalmente é uma tabela ou uma consulta existente, mas também poderia ser uma instrução de SQL que define uma consulta. • Type — Há cinco tipos de recordsets DAO. Os requisitos para cada tipo são especificados nas seções a seguir. Esse é um argumento opcional. Se não especificar um tipo, um recordset do tipo tabela é aberto. • Options — Esse é um argumento opcional. Há 11 opções disponíveis. Elas são em grande parte envolvidas com a autorização ou não de permissões de leitura e gravação ou são providas por questão de retrocompatibilidade com versões anteriores. Como uma questão prática, você normalmente não precisa configurar essas opções em seu código e este livro não as discutirá. Elas são descritas na documentação de Ajuda do Access do método OpenRecordset.
272 •
Gerenciando dados com o Microsoft Excel
LockEdits — Esse
é um argumento opcional. As possíveis configurações incluem especificar atualizações otimistas ou pessimistas. Quando um recordset é bloqueado pessimistamente, um registro que um usuário estiver editando está indisponível para edição por qualquer outro usuário enquanto o método Edit do recordset executa e disponibilizado novamente depois que o método Update executa. Se o bloqueio otimista estiver configurado, o registro é bloqueado somente enquanto o método Update estiver executando. O padrão é o bloqueio pessimista. Novamente, em geral, seu código não precisa especificar LockEdits .
Você pode executar o método OpenRecordset contra um banco de dados ou uma conexão a um banco de dados, utilizando qualquer um destes dois formatos: Set Recordset = Database .OpenRecordset _ (Fonte , Tipo , Opções , LockEdits ) Set Recordset = Connection.OpenRecordset _ (Fonte , Tipo , Opções , LockEdits )
Você também pode executar o método OpenRecordset contra uma tabela, uma consulta ou outro recordset. Nesse caso, você utilizaria este formato: , opções Set Recordset = Objeto.OpenRecordset (tipo , lockedits )
onde Objeto é uma tabela, consulta ou recordset. Por exemplo Set rsProcedures = dbDataFile.TableDefs("Procedures") _ .OpenRecordset (dbOpenDynaset)
ou Set tblProcedures = dbDataFile.TableDefs("Procedures") Set rsProcedures = tblProcedures.OpenRecordset (dbOpenDynaset)
Os tipos de recordset DAO mais comumente utilizados são discutidos nas próximas três seções.
Configurando um recordset do tipo tabela Você configura um recordset do tipo tabela com uma instrução como a seguinte: Set rsProcedures = dbDataFile.OpenRecordset("Procedures", dbOpenTable)
Nessa instrução, Procedures é a fonte do recordset e dbOpenTable é seu tipo. Procedures deve ser uma tabela, não uma consulta, e a tabela deve estar fisicamente dentro do banco de dados, não um link para uma tabela em outro banco de dados. Um recordset do tipo tabela é o padrão, então se Procedures é uma tabela, esta é uma instrução equivalente: Set rsProcedures = dbDataFile.OpenRecordset("Procedures")
Se Procedures for uma consulta ou uma tabela vinculada, recordset dynaset.
rsProcedures é
por padrão um
Se a tabela for muito grande, você poderia ser capaz de tornar seu código mais eficiente acessando registros utilizando o método Seek. Por exemplo
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
273
Sub SeekARecord() Dim Dim Dim Dim
dbDatabaseFile As DAO.Database rsProcs As DAO.Recordset SourceName As String WhichID As Integer
WhichID = InputBox("Enter a procedure ID") SourceName = ThisWorkbook.Path & "\ShortStay.mdb" Set dbDatabaseFile = OpenDatabase(SourceName) Set rsProcs = dbDatabaseFile.OpenRecordset _ ("Procedures", dbOpenTable) rsProcs.Index = "PrimaryKey" rsProcs.Seek "=", WhichID MsgBox rsProcs.Fields("ProcedureName") End Sub
Essa sub-rotina solicita ao usuário o valor da chave primária de uma tabela. Ela então abre o banco de dados e estabelece um recordset baseado na tabela Procedures do banco de dados. Ela configura o índice da tabela como o índice chamado PrimaryKey. Então executa o método Seek do recorset. É assim, utilizando DAO, que você acessa um registro muito rapidamente. O VBA pode utilizar uma pesquisa de árvore B para localizar o ID fornecido pelo usuário no índice da tabela e retornar o valor do registro no campo ProcedureName. Isso pode acelerar o processamento com tabelas muito grandes. Mas há uma troca: antes de poder utilizar o método Seek, você deve configurar o índice atual da tabela. Com recordsets menores, talvez leve mais tempo para configurar o índice e executar a busca do que procurar diretamente o registro, como no seguinte código: Sub FindARecord() Dim Dim Dim Dim
dbDatabaseFile As DAO.Database rsProcs As DAO.Recordset SourceName As String WhichID As Integer
WhichID = InputBox("Enter a procedure ID") SourceName = ThisWorkbook.Path & "\ShortStay.mdb" Set dbDatabaseFile = OpenDatabase(SourceName) Set rsProcs = dbDatabaseFile.OpenRecordset("Procedures", dbOpenSnapshot) rsProcs.FindFirst "ProcedureID = " & WhichID MsgBox rsProcs.Fields("ProcedureName") End Sub
A sub-rotina nomeada FindARecord não envolve o índice da tabela. Ela apenas procura o campo ProcedureID da tabela Procedure até localizar o ID que o usuário fornece. Em seguida, ela informa o valor desse registro no campo ProcedureName.
274
Gerenciando dados com o Microsoft Excel
Observe que o recordset é declarado não como dbOpenTable mas como dbOpenSnapshot, um tipo de recordset DAO discutido a seguir. Para utilizar o método FindFirst, o recordset deve ser dbOpenSnapshot ou dbOpenDynaset. A T O N
Você não pode utilizar o método Seek do DAO com uma tabela vinculada.Ele está disponível somente com recordsets tipificados como dbOpenTable e, como observado no começo desta seção, uma tabela vinculada não pode ser tipificada como dbOpenTable.
Configurando um recordset do tipo instantâneo Você configura um recordset instantâneo com o tipo dbOpenSnapshot: Set rsProcs = dbDatabaseFile.OpenRecordset _ ("Procedures", dbOpenSnapshot)
Um recordset do tipo instantâneo ( snapshot ), diferente de outros tipos de recordset, é somente de leitura. Isso ocorre porque todos os registros em sua fonte são trazidos na memória quando a instrução Set é executada. Quaisquer alterações nos registros da fonte que ocorram subseqüentemente não serão refletidas no recordset. Além disso, você não pode editar, adicionar ou excluir registros do recordset. Um recordset do tipo instantâneo pode ser útil se você estiver apenas acessando campos e registros e seu código não for planejado para modificá-los. Como é menos flexível que outros tipos de recordset, ele pode tornar mais eficiente a utilização de recursos do sistema que outros tipos. Entretanto, ele pode executar ligeiramente mais lento que outros tipos porque traz todos os registros e campos da fonte para a memória. Isso se contrapõe, por exemplo, ao recordset do tipo dynaset, que traz somente indicadores na memória quando o recordset é estabelecido. O termo instantâneo ( snapshot ) pode ser uma fonte de confusão. Esta seção discute recordsets DAO, e um recordset instantâneo DAO não é atualizável (outro termo para editável). Há um tipo de recordset ADO, um recordset estático, ao qual projetos no Access se referem como um instantâneo. Mas um recordset instantâneo ADO pode se tornar atualizável.
Configurando um recordset do tipo dynaset Um recordset dynaset é o mais flexível dos tipos de recordset DAO. Ele pode ser baseado em uma tabela ou em uma consulta e permite ao código editar, acrescentar ou excluir os registros da fonte. Você indica um dynaset, como de costume, na instrução Set do recordset. Set rsProcs = dbDatabaseFile.OpenRecordset _ ("Procedures", dbOpenDynaset)
Quando essa instrução executa, os registros são trazidos para a memória, mas somente seus indicadores. (Um indicador é uma propriedade do recordset. É semelhante a uma chave primária no sentido de que identifica unicamente cada um dos registros do recordset.) É somente quando o código modifica os registros que os campos são disponibilizados. Essa é a razão de dynasets inicialmente poderem se abrir mais rapidamente que outros tipos de recordset.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
275
Entendendo tipos de recordset ADO O ADO também utiliza diferentes tipos de recordsets, mas são definidos diferentemente dos tipos de recordset DAO. As duas principais formas de especificar as propriedades do recordset ADO são configurando-os diretamente ou como parte do método Open do recordset. Por exemplo Set rsProcs = New ADODB.Recordset With rsProcs .Source = "Procedures" .ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & SourceFile .CursorType = adOpenDynamic .LockType = adLockOptimistic .CursorLocation = adUserServer End With
Utilizando o método Open do recordset, você poderia utilizar esta instrução: rsProcs.Open rsProcs.Open "Procedures", strConnectToShortStay, _ adOpenStatic, adLockOptimistic, adCmdTable
Esse exemplo passa os parâmetros de recordset para o método Open do recordset; em ordem, sua fonte, uma string que define a conexão, o tipo de cursor, o tipo de bloqueio e a posição de cursor. (Consulte as discussões sobre os três últimos parâmetros nas seções a seguir.) Com um recordset ADO, você não precisa se preocupar como se preocuparia com o DAO em relação à fonte do recordset. Você pode utilizar uma tabela, uma consulta, uma instrução de SQL e outras fontes sem ter de se certificar de que escolheu o tipo correto de recordset. A string de conexão é um parâmetro que vimos antes quando discutíamos as estruturas do ADO. Em geral, ela especifica um provedor como Microsoft.Jet.OLEDB.4.0 para um banco de dados Access Jet ou SQLOLEDB.1 para o SQL Server. Além disso, ela fornece as informações sobre o caminho e nome de um banco de dados ou o nome de um servidor e um catálogo para o SQL Server. Outros parâmetros são introduzidos nas três próximas seções.
Utilizando a propriedade CursorType A propriedade CursorType é de alguma forma semelhante a uma propriedade Type do recordset DAO: ela determina se alterações que outros usuários poderiam fazer nos registros da fonte aparecem em seu recordset e se todos os campos e registros são trazidos para o recordset quando for aberto. Eis os valores que a propriedade CursorType pode assumir: •
adOpenDynamic —
Esse tipo de cursor inicialmente recupera valores de chave somente. Ao editar um registro ou, de outro modo, acessar seus campos, os dados restantes para esse registro são recuperados. As alterações feitas na fonte por outros usuários são refletidas no recordset, inclusive a adição e exclusão de registros. Esse tipo de cursor produz uma demanda maior de recursos do que outros tipos.
•
adOpenForwardOnly —
Essa é a configuração padrão. Você não tem acesso às alterações que outros usuários poderiam fazer depois de abrir o recordset. Você deve utilizá-la somente para avançar no recordset; dependendo das condições, você poderia encontrar erros se tentasse se
276
Gerenciando dados com o Microsoft Excel
mover, por exemplo, do registro final ao primeiro. Esse tipo permite uso mais eficiente dos recursos do sistema que outros tipos de cursor. • adOpenKeyset — Como adOpenDynamic, o tipo keyset inicialmente recupera chaves apenas. Se outros usuários editam valores de campo em registros, essas modificações aparecem quando você recupera um registro completo. Os registros recém-adicionados à fonte por outros usuários não são adicionados ao recordset. • adOpenStatic — As alterações feitas por outros usuários não aparecem no recordset. O Access denomina esse tipo de cursor como um instantâneo, mas não é o mesmo que um instantâneo DAO porque você pode torná-lo atualizável configurando seu tipo de bloqueio (veja a próxima seção).
Utilizando a propriedade LockType A propriedade LockType permite controlar se e como é possível ao código alterar os dados na fonte do recordset. Há quatro valores: • adLockBatchOptimistic — Todas as edições que seu código poderia fazer no recordset são salvas até que um método UpdateBatch seja encontrado. Nesse ponto, as edições são salvas na fonte do recordset. Isso não é feito necessariamente registro por registro. Você poderia, por exemplo, editar cada registro no recordset antes de uma atualização fazer com que as alterações fossem salvas. • adLockOptimistic — Esse é semelhante a configurar um bloqueio otimista em um recordset DAO. Durante o processo de edição, o registro não é bloqueado e pode ser editado por outros usuários. Ele é bloqueado durante a atualização de recordset e então liberado novamente. • adLockPessimistic — Esse é semelhante a um bloqueio pessimista de DAO. Outro usuário não pode editar um registro que você já começou a editar até depois de você ter atualizado o recordset. • adLockReadOnly — Essa é a configuração padrão. Você não pode atualizar o recordset. Essa configuração, porém, não impede que outro usuário edite os registros da fonte enquanto você a tem aberta. Se estiver fazendo nada além de visualizar os dados ou copiando-os para um destino como uma planilha, essa é a escolha mais eficiente.
Utilizando a propriedade CursorLocation Você poderia esperar que uma posição de cursor referenciasse o registro atual, mas na realidade ela referencia o servidor ou o cliente. Se o mecanismo de bancos de dados, como Jet ou SQL Server, gerencia o cursor, e portanto gerencia o recordset, essa propriedade foi configurada como adUseServer. Essa é a configuração padrão e significa que o mesmo mecanismo que está gerenciando todos os outros usuários está cuidando de você. Quando houver muitos usuários, isso pode tornar as coisas lentas.
Capítulo 10 – Definindo campos e registros com o ActiveX Data Objects e o Data Access Objects
277
Você pode amenizar a carga sobre o mecanismo de bancos de dados e deixar o ADO gerenciar o cursor configurando a propriedade CursorLocation como adUseClient. Você não pode configurar essa propriedade por meio do método Open do recordset. Em vez disso, utilize uma instrução do seguinte tipo antes de invocar Open : rsProcs.CursorLocation = adUseClient
Olhando para frente Este capítulo discutiu maneiras de utilizar as duas principais bibliotecas de objeto de dados, DAO e ADO, para manipular objetos em bancos de dados. Você viu como utilizar o DAO para criar um banco de dados do Access a partir do zero. Além disso, viu como utilizar tanto o DAO como ADO para criar tabelas e campos. A última parte deste capítulo descreveu como criar e representar recordsets DAO e ADO. Essa é base necessária para o trabalho real de gerenciamento de dados a partir da plataforma Excel. O Capítulo 11, “Obtendo dados do Access para o Excel com o ADO e DAO”, entra nos detalhes de como mover dados para tabelas de banco de dados, e recuperá-los no Excel a partir de recordsets baseados em tabelas e consultas.
11 Obtendo dados do Access para o Excel com o ADO e o DAO Utilizando CopyFromRecordset CopyFromRecordset Este capítulo focaliza a utilização de bibliotecas de objeto ADO e DAO para trazer dados de um banco de dados para uma pasta de trabalho do Excel. Grande parte do material está relacionada com o ajuste fino de atividades, por exemplo, utilizar o código VBA para encontrar o local exato que os dados precisam ocupar em uma planilha. Muitas vezes, porém, você está menos interessado em selecionar e escolher entre registros e colocar um valor de campo em uma localização particular e mais interessado em copiar uma grande quantidade de dados muito rapidamente. Você poderia utilizar um intervalo de dados externos, se quisesse, como descrito nos Capítulos 4, “Importando dados: uma visão geral”, e 5, “Utilizando o Microsoft Query”. Atualizar uma fonte de dados existente é um processo rápido. Mas em geral você configura um intervalo de dados externos manualmente, começando com Dados, Importar dados externos. (Naturalmente, você pode configurar um utilizando o VBA. Para ver como isso é realizado, apenas ligue o programa de gravação de macros antes de você iniciar o processo.) Independentemente de como você configura um intervalo de dados externos, um intervalo identificado é necessário. Você poderia não querer adicionar um intervalo identificado à pasta de trabalho. Nesse caso, considere utilizar o método CopyFromRecordset do Excel. Observe que CopyFromRecordset não faz parte das bibliotecas de objeto DAO ou ADO. É um método que pertence aos intervalos de planilha do Excel. Contudo, você deve estabelecer uma referência a ADO ou a DAO no código porque o argumento principal utilizado por CopyFromRecordset é o objeto de recordset a ser copiado para a planilha. Eis um exemplo que utiliza um recordset DAO. (Segue-se um exemplo utilizando um recordset de ADO.) Naturalmente, antes de você poder executá-lo, você precisaria estabelecer uma referência a uma biblioteca DAO utilizando Ferramentas, Referências a partir do VBE. ➪
Para informações adicionais sobre como estabelecer referências de biblioteca consulte “Conectando-se utilizando o ADO”,p. 185. Sub Dim Dim Dim Dim
CopyFromRecordsetWithDAO() dbNorthWind As DAO.Database tdfOrders As DAO.TableDef rsOrders As DAO.Recordset i As Integer
Set dbNorthWind = OpenDatabase("C:\Documents and Settings" & _ "\Owner\My Documents\Northwind.mdb")
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
279
Set tdfOrders = dbNorthWind.TableDefs("Orders") Set rsOrders = tdfOrders.OpenRecordset(dbOpenTable) With ActiveSheet For i = 0 To rsOrders.Fields.Count - 1 .Cells(1, i + 1) = rsOrders.Fields(i).Name Next i .Cells(2, 1).CopyFromRecordset rsOrders End With End Sub
O código estabelece um recordset, rsOrders, que representa a tabela Orders no banco de dados de exemplo do Access, Northwind.mdb. Ao executar o método CopyFromRecordset, o conteúdo dessa tabela é copiado para a planilha ativa. A cópia ocorre muito rapidamente. Nesse exemplo, 830 registros com 14 campos cada foram copiados para uma planilha em menos de um segundo, utilizando um Pentium 4 com processador de 1,80 GHz. Observe que o código faz um loop pela lista de campos que pertence ao recordset e grava o nome de cada campo na primeira linha da planilha ativa. O método CopyFromRecordset não fornece nomes de campo. Ao contrário, se estabelecer um intervalo de dados externos, você obtém os nomes de campo por padrão. Como a primeira linha contém os nomes de campo, os registros e campos do recordset são copiados para a planilha ativa começando na linha 2, coluna A. O seguinte código passa um recordset ADO ao método CopyFromRecordset. Observe o uso do objeto Recordset do ADO, e também sua utilização de um arquivo do SQL Server em vez de um banco de dados Jet. Sub CopyFromRecordsetWithADO() Dim rsOrders As ADODB.Recordset Dim i As Integer Dim cnnConnectSpec As String cnnConnectSpec = "Provider=SQLOLEDB.1;Data Source=(local);" & _ "Initial Catalog=NorthwindCS;Integrated Security=SSPI" Set rsOrders = New ADODB.Recordset rsOrders.Open "Orders", cnnConnectSpec, adOpenStatic, _ adLockReadOnly, adCmdTable With ActiveSheet For i = 0 To rsOrders.Fields.Count - 1 .Cells(1, i + 1) = rsOrders.Fields(i).Name Next i .Cells(2, 1).CopyFromRecordset rsOrders End With End Sub
A lógica de retornar nomes e valores de campo à planilha é idêntica se você estiver utilizando DAO ou ADO. Observe a string que contém as informações de conexão para o recordset ADO. Ela especifica SQLOLEDB.1 como o provedor e (local) como a fonte de dados. Em um ambiente em rede, você utilizaria o nome do servidor armazenando o catálogo como a fonte de dados. O uso de ( local) indica que um servidor foi instalado na estação de trabalho local.
280
Gerenciando dados com o Microsoft Excel
A Microsoft oferece uma versão de desktop do SQL Server desde o Office 2000, chamada Microsoft Data Engine ou MSDE . (O SQL Server refere-se a essa versão como o SQL Server Desktop Engine.) Os passos para instalá-lo dependem da versão que você está executando. No Office 2000 Premium, por exemplo, você instala o Microsoft Office Server Extensions a partir do Disco 3 do produto Office 2000. No Office 2003, uma maneira é localizar uma pasta chamada MSDE2000 nos discos de instalação. Essa pasta contém um arquivo de instalação executável; dê um duplo clique em seu ícone para executá-lo. Com o servidor instalado, você pode utilizar a versão compatível com SQL Server do banco de dados de exemplo Northwind. Novamente, obtê-lo depende da versão do Office que você está executando. No Access 2000, você utiliza Arquivo, Abrir e navega até a subpasta Exemplos dentro da pasta do Office. No Access 2003, escolha Bancos de dados de exemplo a partir do menu Ajuda. Se tudo que estiver fazendo é trazendo um conjunto de dados — baseado em uma consulta ou em uma tabela — de um banco de dados para uma planilha do Excel, o método CopyFromRecordset talvez seja tudo de que você precisa. Quando os dados estiverem na planilha, você pode utilizar o conjunto completo de ferramentas do Excel, como gráficos e tabelas dinâmicas e funções de planilha, para analisar os dados. Entretanto, se precisar retornar os dados em um formato específico, você precisará recorrer às técnicas discutidas no restante deste capítulo.
Criando uma aplicação Não faz muito tempo uma empresa me procurou com um problema. A empresa precisava de um sistema melhor para permitir que seus empregados reservassem salas de reuniões, conferências e outras funções. A empresa tinha cerca de 2.200 empregados em quatro prédios, e não tinha salas de reunião suficientes, então era essencial um sistema de reserva de sala que operasse com precisão e de maneira uniforme. Durante algum tempo, se quisesse reservar uma sala, você telefonava à administração e os informava para quando sua reunião estava agendada e onde queria que acontecesse. Um assistente administrativo verificava um calendário de parede para ver se a sala estava disponível em sua data e hora desejada e se estivesse, você dava os demais detalhes — que bebidas deveriam ser servidas, se o equipamento audiovisual era necessário, de que conta deveria ser cobrada e assim por diante. Uma vez por semana, esse mesmo assistente reuniria as informações e as introduziria outra vez em um formulário de duas páginas, mostrando cada dia durante a semana seguinte e listando cada reunião agendada em cada dia. Cada reunião também mostrava a sala concedida e o horário de início agendado. Um dia típico tinha cerca de 20 reuniões. O formulário era duplicado e postado em vários lugares por todo o prédio a cada sexta-feira à noite. Havia problemas com esse sistema. A abordagem de “papel e lápis” para coletar, armazenar e recuperar informações sobre as reuniões causava o problema padrão de informações incompletas e ilegíveis, overbooking de salas, reservas perdidas e assim por diante — sem falar de uma pessoa o dia inteiro, toda semana, dedicada a compilar as informações e digitar o formulário. Então, quando uma rede de dados foi instalada, a empresa pensou que era hora de modernizar seu sistema de reserva de salas. Colocando-o na rede, seria mais fácil para o pessoal verificar o
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
281
status das salas que eles tinham reservado e possivelmente descentralizar a entrada de reservas. Compraram um programa organizador de salas pronto por US$ 6.000. Um de seus funcionários mais tecnicamente bem informado, que entendia tanto os problemas reais da empresa como a natureza do novo software, fez sua instalação. Mas a empresa não tinha levado em conta que seus empregados estavam acostumados aos formulários que durante anos eram postados fora das salas de reunião. O pessoal gostava deles, mesmo que fossem notoriamente imprecisos, e gostavam da maneira como eram organizados. Estavam acostumados com esses formulários. O novo software, embora incluísse um gerador de relatórios, provou-se incapaz de simular o antigo e familiar layout do formulário de agendamento. E tinha alguns outros obstáculos com que a empresa não havia se preocupado. Então, seus representantes procuraram minha empresa e pediram um sistema personalizado. Aceitamos, então pensamos e iniciamos o trabalho de projeto. Mas cada vez que chegávamos perto de fixar o modelo, nosso representante junto ao cliente aparecia como Colombo, usando uma capa de chuva velha esfarrapada, fumando um desagradável charuto, acenando e indicando “Só mais uma coisa…”. Até que concluirmos o sofware, nosso cliente já havia gasto quatro vezes o valor do software pronto. Mas tinha um sistema quase à prova de falhas (nenhum sistema é completamente à prova de falhas) que fazia muito mais do que originalmente haviamos antecipado. O sistema se baseava na interface do Excel para entrada do usuário devido à flexibilidade da planilha e a riqueza de maneiras como ela pode ser formatada. Para armazenar os dados, o sistema se baseava em um banco de dados externo. Tentar manter mais de 7.000 reservas por ano, com 55 campos cada, em uma pasta de trabalho do Excel nos levaria direto de volta a uma das situações que foram discutidas no Capítulo 1, “Utilizando incorretamente o Excel como uma ferramenta de gerenciamento de banco de dados”. Então, temos uma aplicação que conta com uma biblioteca de objetos, uma interface baseada noVBA entre o Excel e um banco de dados. As informações sobre uma nova reserva são introduzidas no Excel e armazenadas no banco de dados. O valor de um dia cheio de reservas está visível em uma planilha Excel, tornando fácil ao usuário ver quais salas estão disponíveis em quais horários em qualquer determinada data do calendário. Se as informações sobre a reserva precisassem ser alteradas, elas seriam facilmente chamadas de volta do banco de dados para a pasta de trabalho do Excel para modificação, e as alterações armazenadas de volta no banco de dados. E, naturalmente, o relatório, aquele que era postado em todas as portas das salas de reunião, é gerado automaticamente toda sexta-feira à tarde, no antigo layout tão estimado pelo pessoal da empresa. Você está convidado a espiar por trás do ombro do desenvolvedor no estudo de caso a seguir para ver exatamente como as informações armazenadas no banco de dados são retornadas à pasta de trabalho do Excel de algumas maneiras muito particulares — maneiras que se fazem necessárias para personalizar o processo por meio de recordsets, em vez de por meio de SQL. Você também verá como os dados são utilizados para preencher um formulário de usuário, de modo que o usuário possa editar os dados existentes. No Capítulo 12, “Controlando um banco de dados a partir do Excel com o ADO e o DAO”, você verá como os dados são movidos automaticamente de uma pasta de trabalho para um banco de dados.
282
Gerenciando dados com o Microsoft Excel
Trazendo dados de volta de uma consulta parametrizada Esta seção o orienta no processo de preencher uma planilha com dados retornados de um banco de dados utilizando uma consulta parametrizada.
Configurando os requisitos de layout O layout e a aparência da planilha são importantes aos propósitos da programação de salas descritos anteriormente. Os dados devem ser colocados em intervalos de células específicos. Esta é a razão de o código recorrer a recordsets: é insuficiente meramente retornar registros do banco de dados escolhendo, por exemplo, Importar dados externos a partir do menu Dados. Essa abordagem importa dados como uma lista, mas o layout requerido é como mostrado na Figura 11.1.
Figura 11.1 Os nomes das salas de reunião são mostrados na coluna A e os horários são mostrados na linha 1 em incrementos de 15 minutos.
A planilha mostrada na Figura 11.1 tem vários aspectos que o código deve atender: • A legenda na guia da planilha é a data para as reuniões mostradas na planilha. • Cada reunião ocupa múltiplas colunas e normalmente apenas uma linha. Ela se estende pelo número de colunas necessário para capturar seu horário de início e horário de término, e ocupa a linha que representa uma sala de reunião em particular. (Em um caso mostrado na Figura 11.1, uma reunião ocupa as salas 1 e 2, que aparece nas linhas 2 e 3.) • O propósito de uma reunião é mostrado em cada célula que representa a reunião. Assim, YOGA aparece nas células T17:W17. • Antes de uma reunião, uma equipe deve arrumar a sala: providenciar mesas e cadeiras, servir as bebidas e assim por diante. Depois de uma reunião, uma equipe precisa fazer a limpeza: esvaziar cestos de lixo, remover cafeteiras e assim por diante. Normalmente leva meia hora para arrumar e fazer a limpeza, então há um período antes e depois de cada horário de reunião reservado para arrumação e limpeza. Esses períodos são mostrados em uma cor
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
283
diferente na planilha. Os exemplos na Figura 11.1 são as células V13:W13 (arrumação para a reunião CASE MGMT TRAINING) e X17:Y17 (limpeza depois de YOGA). • Observe as células V13 e Y17 na Figura 11.1. V13 tem uma borda preta escura à sua esquerda e Y17 tem uma à sua direita. Essas bordas ajudam a distinguir entre período de limpeza e o de arrumação de uma reunião para outra, se os dois períodos se coincidirem. Para obter as reservas de sala para uma data em particular na planilha, o usuário escolhe Saltar para uma data a partir do menu personalizado Calendário. Ele seleciona uma data e essa data é gravada na legenda da guia da planilha. Então a seguinte sub-rotina é chamada: Option Base 1 Option Explicit
As duas instruções de opção, colocadas na parte superior do módulo, se aplicam a todos procedimentos localizados no módulo. Option Explicit é incluído como boa prática de programação. Option Base 1 é incluído porque simplifica o manuseio da matriz, discutido mais tarde nesta seção. Sub GetSingleDayFromDB() Dim dbReservation As DAO.Database Dim qdfRetrieveCurrent As DAO.QueryDef Dim rsRecordsToRetrieve As DAO.Recordset
Uma referência à biblioteca DAO foi configurada como o módulo da sub-rotina. Com essa referência configurada, é possível declarar três variáveis de objeto: uma representando o banco de dados que contém as informações sobre as reservas de salas, uma representando uma definição de consulta nesse banco de dados e uma representando um recordset que conterá os registros retornados pela consulta. Dim StartCol As Integer, StopCol As Integer, _ WhichRow As Integer Dim ReservationRange As Range
As variáveis StartCol, StopCol e WhichRow são utilizadas para localizar a coluna na qual uma reserva começa, a coluna onde termina e sua linha. Juntas, são utilizadas para definir um intervalo de planilha, ReservationRange. Dim SetupPeriods As Integer, CleanupPeriods As Integer
Como mencionado anteriormente, uma reserva precisa permitir tempo de arrumação de sala antes da reunião e, tempo de limpeza, depois. A variável SetupPeriods armazena o número de períodos de 15 minutos necessários para a arrumação e CleanupPeriods faz o mesmo para a limpeza. Dim TimeAsText As String
As aplicações do Microsoft Office armazenam horas como um número serial, com a parte do inteiro representando uma data e a parte decimal representando hora. O Excel tem várias maneiras de exibir as informações de data e hora. Ele pode mostrar o horário como horas e minutos, como horas, minutos e segundos, em um mostrador de 24 horas ou utilizando notação de AM/PM e assim por diante. É possível armazenar as informações de horário como texto. Por exemplo, o
284
Gerenciando dados com o Microsoft Excel
horário representado numericamente como 0,4688 pode ser exibido, utilizando um formato de hora, como 11:15 AM . Esse é o formato, h:mm AM/PM , utilizado na primeira linha na planilha. A fim de comparar um horário de reserva com os valores da linha 1, é útil armazenar o horário como um valor de texto em vez de como um número serial. A variável TimeAsText é utilizada com esse propósito. Dim TimeArray(71) As String, RoomArray() As String
Duas matrizes são declaradas. TimeArray armazenará todos os horários na linha 1 da planilha. Esse array tem exatamente 71 elementos. Os períodos de 15 minutos estão na planilha no intervalo B1:BT1. Variam de 6:30 até 12:00, inclusive, e isso representa 71 períodos. A matriz RoomArray é declarada como uma matriz dinâmica, cujo número de elementos pode ser alterado no código enquanto ele executa. De vez em quando, o número de salas disponíveis para reuniões muda. Tornando o array dinâmico em vez de estático, o código pode contar o número de salas na planilha e redimensionar RoomArray de maneira correspondente. Dim i As Integer Dim ResourceCount As Integer
Duas variáveis do tipo inteiro são declaradas. A variável i é utilizada como um contador em um loop For-Next, e ResourceCount é utilizada tanto para redimensionar RoomArray como para ajudar a definir um intervalo na planilha. Application.ScreenUpdating = False DatabaseName = ThisWorkbook.Sheets("UserNames").Cells(1, 3)
Para acelerar o processamento porque a planilha ativa será completamente atualizada, a atualização de tela é desativada (isso “congela” o monitor momentaneamente). O caminho e o nome do banco de dados que armazena as informações de reserva são mantidos na célula C1 de uma planilha chamada UserNames. Armazená-los aí torna desnecessário armazená-los em algum lugar no código VBA, onde pode ser difícil de localizar, dado que o projeto inclui sete módulos. O valor nessa célula é atribuído a DatabaseName. Set dbReservation = OpenDatabase _ (DatabaseName, False, False, "MS Access")
O banco de dados é aberto e atribuído à variável de objeto dbReservation. Set qdfRetrieveCurrent = _ dbReservation.QueryDefs("RetrieveSingleDay")
A variável de objeto qdfRetrieveCurrent é configurada como a consulta, localizada no banco de dados Recursos, chamada RetrieveSingleDay. qdfRetrieveCurrent.Parameters("ThisDate") = ActiveSheet.Name
A consulta nomeada RetrieveSingleDay, representada nesse código pela variável de objeto qdfRetrieveCurrent, tem um parâmetro. As bibliotecas DAO e ADO incluem esse objeto. É um tipo de critério que você redefine toda vez que a consulta executa. Veja a Figura 11.2, que mostra a consulta no modo de Design.
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
285
Figura 11.2 Observe que o nome do parâmetro é fornecido entre colchetes.
É por meio da consulta RetrieveSingleDay e seu parâmetro, ThisDate, que o código obtém os dados sobre as reservas que foram feitas num dia particular. Se abrisse a consulta diretamente, a partir de dentro do Access, você seria solicitado a fornecer um valor ao parâmetro. Então o Access responderia exibindo todos os registros que têm o valor fornecido no campo ReservationDate. A T O N
Além do parâmetro de data, a consulta RetrieveSingleDay utiliza outro critério. Observe na Figura 11.2 que o nome de campo Cancelled tem o critério Falso. A consulta não retorna nenhum registro que tem o valor Verdadeiro no campo Cancelled. Utilizando esse campo, é possível impedir que as reservas apareçam na planilha sem excluí-las fisicamente do banco de dados. O campo Cancelled é utilizado mais adiante neste Capítulo, em“Excluindo registros do banco de dados e da planilha”, em que a sub-rotina RemoveReservation() é discutida.
Em vez disso, o código obtém a legenda mostrada na guia da planilha ativa e passa esse valor como um parâmetro à consulta. A consulta trata esse valor exatamente como trataria um fornecido quando uma consulta é aberta diretamente. Tanto o ADO como o DAO têm um objeto chamado Parameter e ao qual você pode atribuir um valor. Mas o Excel também tem um objeto chamado Parameter. Se declarar uma variável de objeto para representar um parâmetro, não deixe de qualificá-la com a biblioteca de objeto que quiser. Por exemplo, Dim prmTheDate As Parameter Set prmTheDate = qdfRetrieveCurrent.Parameters("ThisDate") A C I D
resulta em uma não-correspondência de tipo porque o VBA assume que prmTheDate é um parâmetro do Excel, não um parâmetro DAO ou ADO. Em vez disso, não deixe de utilizar Dim prmTheDate As DAO.Parameter
ou Dim prmTheDate As ADO.Parameter
286
Gerenciando dados com o Microsoft Excel
Agora o código executa a consulta e coloca os resultados em um recordset que será retornado à planilha do Excel. Novamente, a razão de colocar os resultados da consulta em um recordset, em vez de retorná-los diretamente à planilha, é que a última abordagem traz os registros de volta organizados como uma lista, em vez de no layout mostrado na Figura 11.1. O recordset é aberto só como lista. Esse é um tipo de recordset eficiente e é apropriado porque o código não rolará de um lado a outro pelos registros e não os editará. Set rsRecordsToRetrieve = _ qdfRetrieveCurrent.OpenRecordset(dbOpenForwardOnly)
Limpando a planilha Agora é hora de pegar as informações sobre a planilha. O código começa contando os recursos listados na coluna A e utilizando esses para dimensionar RoomArray. ResourceCount = ActiveSheet.Cells(600, 1).End(xlUp).Row - 1 ReDim RoomArray(ResourceCount)
O código assume que não há nenhuma entrada na coluna A além da linha 600. Utiliza o método End(xlUp) para localizar a entrada final na coluna A e anota sua linha (na Figura 11.1 é a linha 19). Subtrai 1 desse número de linha para considerar a célula vazia A1. Então, utilizando o layout mostrado na Figura 11.1, ResourceCount é igual a 18 e ResourceArray é redimensionado para conter 18 elementos. ActiveSheet.Range(Cells(2, 2), _ Cells(ResourceCount + 1, 72)).Clear
O código limpa todas as células na planilha que poderiam conter dados, removendo as informações sobre reservas para a data que a planilha atualmente representa. O intervalo que começa com a célula B2 e que se estende até BT19 (19 porque ResourceCount é igual a 18 nesse exemplo) tem seu conteúdo limpo, removendo assim os valores de célula que identificam o propósito de uma reserva. O método Clear também remove comentários de célula e as cores de célula que identificam as reservas, períodos de arrumação e de limpeza.
Preenchendo as matrizes de memória Com a planilha vazia de dados de reserva, o código obtém os nomes dos recursos disponíveis e os horários do dia e os coloca em matrizes de memória. Essas matrizes serão úteis na identificação da localização exata em que as informações sobre cada reserva devem ser colocadas na planilha. Primeiro, porém, é melhor verificar se a data que o usuário solicitou tem quaisquer reservas estabelecidas. If Not rsRecordsToRetrieve.BOF Then
O código testa para ver se a consulta retornou um recordset vazio. Isso pode ocorrer se o usuário especificou uma data que não tem nenhuma reserva no banco de dados. Nesse evento, o código subseqüente que faz referência a supostos registros causará erros de tempo de execução; portanto, a sub-rotina executa esse código somente se houver pelo menos um registro no recordset. Uma maneira de testar um recordset vazio é verificar se a posição atual, imediatamente depois
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
287
que o recordset foi preenchido, é BOF ou Começo De Arquivo. Se essa não for a posição atual, o recordset tem pelo menos um registro e o código pode prosseguir normalmente. For i = 1 To 71 TimeArray(i) = Application.Text _ (ActiveSheet.Cells(1, i + 1), "h:mm AM/PM") Next i
Preencha a matriz de memória TimeArray com os valores localizados nas células B1:BT1 na planilha. Embora sejam exibidos no formato h:mm AM/PM, o conteúdo real das células são números seriais de data/hora. Então, o código utiliza a função Text do Excel para armazenar os valores de hora em TimeArray como strings e utiliza o mesmo formato que a planilha utiliza. A razão para isso é que o código subseqüentemente pesquisará a matriz por valores de hora em formato de string para determinar qual coluna deve utilizar para introduzir as informações de reserva. For i = 1 To ResourceCount RoomArray(i) = ActiveSheet.Cells(i + 1, 1) Next i
De maneira semelhante, a matriz RoomArray é preenchida com os nomes das salas localizados na coluna A. Observe o uso de ResourceCount como o valor final utilizado pelo contador do loop. Note também que os elementos do array RoomArray iriam de 1 até 18 por causa da instrução Option Explicit no início do módulo. Em sua ausência, os elementos da matriz iriam de 0 até 17.
Encontrando a localização da reserva A seguir , um bloco With é estabelecido. As instruções subseqüentes que referenciam objetos, propriedades ou métodos qualificados somente com um ponto (por exemplo, .Fields) são consideradas como pertencentes ao objeto do bloco With — aqui, o recordset. With rsRecordsToRetrieve
Com o bloco With estabelecido, o código entra em um loop que itera pelo recordset, registro por registro. Ele termina quando o EOF do recordset (seu Fim De Arquivo) for alcançado. Do Until .EOF
Dentro do loop Do, cada registro de reserva é verificado para determinar seu horário de início, seu horário de término e a sala que utiliza. Isso é feito obtendo o valor no campo StartTime do registro, seu campo StopTime e seu campo ResourceName. Esses valores são comparados com os valores em TimeArray e RoomArray para localizar suas posições dentro das matrizes. Os resultados dessas comparações informam ao código quais colunas de planilha utilizar como os horários de início e término da reserva e qual linha da planilha utilizar como a sala de reunião da reserva. Comece obtendo o valor de StartTime e convertendo-o no formato h:mm AM/PM, mais uma vez utilizando a função Text do Excel. TimeAsText = Application.WorksheetFunction.Text _ (.Fields("StartTime"), "h:mm AM/PM")
288
Gerenciando dados com o Microsoft Excel
Então utilize a função Match do Excel para localizar a posição do horário de início da reserva dentro de TimeArray. Considerando a discussão do Capítulo 2, “Recursos de gerenciamento de dados do Excel”, lembre-se de que quando utilizada em uma planilha, a função Match retorna a posição de um valor dentro de um intervalo de células. Utilizada no código VBA, ela pode — como aqui — retornar a posição de um valor dentro de uma matriz. Então, se o horário armazenado no campo StartTime fosse (em formato de número serial) 0,3125, a função Text o converteria para 7:30 AM. A função Match, pesquisando TimeArray por 7:30 AM retornaria 5, que é o quinto valor no array. Como essa é maneira como a matriz foi preenchida, 7:30 AM também é o quinto horário mostrado na linha 1 da planilha. Mas como os horários disponíveis começam na coluna B, não na coluna A, 1 é adicionado ao resultado da função Match. StartCol = Application.Match _ (TimeAsText, TimeArray, 0) + 1
Lógica semelhante é utilizada para obter a coluna do horário de término da reserva: TimeAsText = Application.WorksheetFunction.Text _ (.Fields("StopTime"), "h:mm AM/PM") StopCol = Application.Match(TimeAsText, TimeArray, 0)
Mas dessa vez, o número 1 não é adicionado ao resultado da função Match. Suponha que o horário de término da reserva seja 12:00 da tarde, como é o caso com a reserva de YOGA na linha 17 da Figura 11.1. Então a célula W17, correspondente ao período de 15 minutos começando às 11:45 da manhã, deve ser a célula final da reserva. Ao meio dia, a reunião está acabando e está em seu período de limpeza. Portanto, 1 não é adicionado, como acontece com o resultado do horário de início Match. WhichRow = Application.Match(.Fields("ResourceName"), _ RoomArray, 0) + 1
A linha que a reserva ocupa na planilha é localizada de uma maneira semelhante. O campo ResourceName no recordset contém o nome da sala reservada. Esse valor é correspondido a RoomArray para encontrar sua posição na matriz, correspondendo (quase) à linha da planilha. Esse “quase” é tratado adicionando 1 ao resultado, contabilizando a célula A1 em branco da planilha. Set ReservationRange = ActiveSheet.Range _ (Cells(WhichRow, StartCol), Cells(WhichRow, StopCol))
A variável de objeto ReservationRange é configurada para representar as colunas começando com StartCol e terminando com StopCol e ocupa a linha cujo número é WhichRow. Agora é possível referenciar repetida e convenientemente esse intervalo em instruções subseqüentes.
Colocando os dados na planilha Agora que o código sabe quais células a reserva deve ocupar, ele coloca os dados necessários nessas células e fornece a formatação correta. ReservationRange = UCase(.Fields("Purpose"))
Cada célula em ReservationRange é preenchida com o valor armazenado no campo Propósito do registro. A função Ucase do VBA é utilizada para converter letras minúsculas no campo em letras maiúsculas (por exemplo, Yoga torna-se YOGA).
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
289
If .Fields("ReserveHold") = "Reserve" Then ReservationRange.Interior.ColorIndex = 3 Else ReservationRange.Interior.ColorIndex = 6 End If
A aplicação permite que o usuário preencha um intervalo de células temporariamente, não com uma reserva firme mas segurando uma sala até os planos se consolidarem. Se o registro representa uma reserva firme, as células no intervalo são coloridas de vermelho (seu ColorIndex é 3). Caso contrário, o registro representa uma retenção provisória e as células são coloridas de amarelo (seu ColorIndex é 6). SetupPeriods = .Fields("SetupPeriods") CleanupPeriods = .Fields("CleanupPeriods")
O recordset tem dois campos, SetupPeriods e CleanupPeriods, que armazenam o número de períodos de 15 minutos que devem ser reservados para a arrumação de uma sala antes da reunião e a limpeza depois. Esses valores de inteiro (por exemplo, dois períodos de arrumação permitirão 30 minutos de preparação) são atribuídos a duas variáveis para utilização posterior. If SetupPeriods > 0 Then ReservationRange.Offset(0, –SetupPeriods) _ .Resize(1, SetupPeriods).Interior.ColorIndex = 48 End If
As células que representam o período de arrumação da reunião são coloridas de cinza. Isso é feito utilizando a função Offset do Excel. O uso de Offset na planilha é descrito no Capítulo 2. Utilizada aqui, essa função identifica um intervalo que é o deslocamento de ReservationRange por zero linhas e pelo mesmo número de colunas daquele do valor de SetupPeriods . Observe o sinal de subtração antes de SetupPeriods como um argumento para Offset. Isso significa que se SetupPeriods contém 2, o intervalo é deslocado a partir de ReservationRange duas colunas para a esquerda do início de ReservationRange. Além disso, o método Resize é utilizado para definir o intervalo de deslocamento com uma linha de altura e as SetupPeriods colunas de largura. Então seu ColorIndex é configurado como 48 ou cinza. With ReservationRange.Offset(0, –SetupPeriods) _ .Resize(1, 1).Borders(xlEdgeLeft) .LineStyle = xlContinuous .Weight = xlThick .ColorIndex = 1 End With
A primeira célula no intervalo que representa os períodos de arrumação recebe uma borda preta no seu lado esquerdo. Essa célula é localizada da mesma maneira como o próprio intervalo de arrumação é localizado: por meio de um deslocamento por um número negativo de períodos de arrumação. A única diferença é que o método Resize é utilizado para especificar uma única célula, a primeira no intervalo de períodos de arrumação. If CleanupPeriods > 0 Then ReservationRange.Offset(0, ReservationRange.Columns _ .Count).Resize(1, CleanupPeriods).Interior. _ ColorIndex = 48
290
Gerenciando dados com o Microsoft Excel
End If With ReservationRange.Offset(0, ReservationRange.Columns. _ Count + CleanupPeriods - 1).Resize(1, 1) _ .Borders(xlEdgeRight) .LineStyle = xlContinuous .Weight = xlThick .ColorIndex = 1 End With
O mesmo procedimento é utilizado para estabelecer um intervalo de períodos de limpeza e uma borda preta. As diferenças em relação à especificação dos períodos de arrumação são as seguintes: • O deslocamento para ReservationRange considera o número de colunas no intervalo, de modo que os períodos de arrumação começam à direita da coluna final no intervalo de células reservadas. • O método Resize utiliza o número positivo em CleanupPeriods para fazer o intervalo se estender a partir do lado direito de ReservationRange em vez de pelo lado esquerdo, como é feito com SetupPeriods. • A borda direita da célula, em vez da borda esquerda, do período de arrumação final recebe uma borda preta. Um comentário de célula é adicionado à primeira célula em ReservationRange. Esse comentário mostra o nome do usuário que fez a reserva, o nome da pessoa para quem a reserva foi feita e a data em que as informações sobre a reserva foram mais recentemente modificadas. Esses valores são obtidos de seus respectivos campos no recordset e concatenados em um comentário, junto com rótulos e caracteres de retorno de carro. O código faz isso invocando o método AddComment na primeira célula de ReservationRange. ReservationRange.Resize(1, 1).AddComment _ ("Reserved By: " & .Fields("ReserverName") & _ Chr(10) & "Reserved For: " & .Fields("ReservedFor") _ & Chr(10) & "Last Modified: " & Format _ (.Fields("MostRecentlyModified"), "m/d/yy")) Chr(10) identifica
um retorno de carro. Suponha que a pessoa que fez a reserva seja Joe, a pessoa para quem a reserva foi feita seja Mary e que a data em que a reserva foi modificada pela última vez seja 9/1/2004. Nesse caso, o comentário de célula aparece como segue: Reserved By: Joe Reserved By: Mary Last Modified: 9/1/2004 If .Fields("Participants") = "External" Then _ ReservationRange.Font.Bold = True End If
A tarefa final para o registro atual no recordset é formatar ReservationRange em fonte negrito se os participantes da reunião forem externos à organização, e deixar a fonte normal caso contrário.
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
291
.MoveNext Loop
Continuando pelo recordset O código move-se para o próximo registro no recordset. (Ele não pode mover-se de volta, mesmo que se ajuste a lógica do código, porque o recordset foi aberto como lista somente [ forward only].) O final do loop é alcançado e o controle retorna à parte superior do loop, onde o teste para o EOF do recordset é feito. Se estiver agora em EOF, a execução continua com a seguinte instrução End With , que termina o bloco With rsRecordsToRetrieve . End With
Então o bloco If conclui. Esse If é testado quer ou não o recordset estivesse em BOF no começo, o que indicaria um recordset vazio. End If
Por fim, o código libera as variáveis de objeto configurando-as como igual a Nothing e a subrotina em si termina. Set qdfRetrieveCurrent = Nothing Set rsRecordsToRetrieve = Nothing Set ReservationRange = Nothing End Sub
Retornando dados de um banco de dados para um formulário de usuário Nem sempre é desejável retornar os dados a uma planilha de um banco de dados. Particularmente quando houver muitos campos que você quer que o usuário veja, edite ou de outro modo responda, você deve considerar colocar os dados em um formulário de usuário. Você estabelece um formulário de usuário alternando para o Visual Basic Editor e escolhendo UserForm do menu Inserir. Com um formulário vazio ativo, você pode utilizar a caixa de ferramentas de controles para colocar controles — caixas de texto, caixas de combinação, botões de opção, controles multipágina, botões de comando e assim por diante — no formulário. Um controle de multipágina é útil quando você tiver muitos campos para ajustar convenientemente em um formulário de usuário normal. Ao colocar um controle de multipágina em um formulário, você estabelece duas ou mais guias. Cada guia tem um conjunto diferente de controles, muito parecido com a caixa de diálogo que aparece ao escolher Opções do menu Ferramentas do Excel. A guia principal do formulário de usuário utilizada em conjunção com a aplicação Reservation aparece na Figura 11.3. O usuário emprega o formulário para fornecer os dados sobre uma nova reserva ou alterar os dados sobre uma reserva existente.
292
Gerenciando dados com o Microsoft Excel Caixa de listagem Room
Caixa de combinação Date
Caixa de combinação Start Time
Figura 11.3 Incluindo os dois botões de opção Hold e Reserve no mesmo quadro, rotulado Action, você os torna mutuamente exclusivos.
Caixa de combinação Stop Time
Quando o usuário estiver fazendo uma nova reserva, ele escolhe as opções e digita as informações em um formulário de usuário em branco. Quando ele clica no botão OK, o código obtém os dados do formulário de usuário e os armazena no banco de dados. Quando o usuário estiver editando uma reserva preexistente, as informações sobre a reserva são primeiro obtidas do banco de dados e gravadas no formulário de usuário. O usuário então pode modificar o valor de quaisquer controles que quiser. Quando ele clica em OK, o código mais uma vez obtém os dados do formulário de usuário e os coloca no banco de dados.
Identificando o registro de reserva O código seguinte executa quando o usuário tiver indicado que quer editar uma reserva existente. Ele move os dados do banco de dados para o formulário de usuário. O usuário começa clicando em qualquer célula na planilha que representa uma reserva e então escolhe um item do menu personalizado que faz com que esse procedimento execute. Sub FromDBtoForm(rsRecordsToEdit As Recordset) Dim Dim Dim Dim Dim Dim Dim
i As Integer ResourceCount As Integer WhichDate As Date, StartTime As Date WhichRoom As String WhichColumn As Integer qdfEditDetails As QueryDef rsRecordsToEdit As Recordset
DatabaseName = ThisWorkbook.Sheets("UserNames").Cells(1, 3) Set dbReservation = OpenDatabase(DatabaseName, False, False, "MS Access")
Depois de identificar a localização e o nome do banco de dados de reservas, ele é aberto e atribuído à variável de objeto dbReservation. Agora o código precisa recuperar, do banco de dados, todos os campos que descrevem a reserva que o usuário escolheu editar. Comece coletando da planilha as informações necessárias para identificar unicamente uma reserva no banco de dados: a data, a sala e o horário de início. Só pode existir uma reserva em uma determinada data, em uma determinada sala e começando em um determinado horário de início.
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
293
WhichDate = ActiveSheet.Name WhichRoom = ActiveSheet.Cells(ActiveCell.Row, 1).Value
A data da reserva é armazenada com o nome da planilha ativa, que também é mostrado na guia da planilha. Como o usuário inicia o processo clicando em uma das células que representa a reserva, a sala reservada é mostrada na coluna A da linha da célula ativa. O nome da planilha é armazenado em WhichDate e o nome da sala é armazenado em WhichRoom. Resta determinar o horário de início da reserva. Ao usuário é permitido começar esse processo clicando em qualquer célula no intervalo que representa uma reserva. Lembre-se de que cada célula no intervalo de reserva contém uma string que nomeia a reunião — CASE MGMT TRAINING , por exemplo. Para localizar a célula mais à esquerda para a reserva, tudo que é preciso fazer é continuar movendo-se à esquerda da célula ativa até localizar uma célula que não contenha o nome da reunião. A célula mais à esquerda que contém o nome da reunião está na coluna que representa o horário de início da reserva. Então, identifique o número da coluna à esquerda da célula ativa. WhichColumn = ActiveCell.Column - 1
Utilize um loop Do para decrementar o valor de WhichColumn até que o valor da célula em WhichColumn não mais seja igual ao valor na célula ativa. Do While Cells(ActiveCell.Row, WhichColumn) = _ Cells(ActiveCell.Row, ActiveCell.Column) WhichColumn = WhichColumn - 1 Loop
O horário de início da reserva é localizado na linha 1 do valor anterior de WhichColumn . Armazene o horário de início na variável StartTime. StartTime = ActiveSheet.Cells(1, WhichColumn + 1).Value
Agora configure a consulta que retornará todas as informações sobre a reserva selecionada do banco de dados e passe os valores da data de reserva, a sala reservada e o horário de início como parâmetros à consulta. Set qdfEditDetails = dbReservation.QueryDefs("DetailRecords") With qdfEditDetails .Parameters("WhichDate") = WhichDate .Parameters("WhichRoom") = WhichRoom .Parameters("WhichTime") = StartTime
Então configure a variável de objeto rsRecordsToEdit como o resultado da execução da consulta com seus parâmetros. Set rsRecordsToEdit = .OpenRecordset(dbOpenForwardOnly) End With
A Figura 11.4 mostra a consulta no modo de Design.
294
Gerenciando dados com o Microsoft Excel
Figura 11.4 Incluindo a tabela Resources, o código pode utilizar o nome da sala em vez de seu ID como um parâmetro.
Revisar: o usuário clicou em uma célula de uma reserva existente na planilha do Excel e selecionou um item do menu personalizado que chama o presente procedimento. O código anota o nome da planilha (para obter a data da reserva), o nome da sala reservada e a coluna em que a reserva inicia (para obter seu horário de início). Esses valores são passados como parâmetros a uma consulta de banco de dados. Juntos especificam uma única reserva. Esse registro da reserva é atribuído à variável de objeto rsRecordsToEdit e o próximo passo geral é preencher o formulário de usuário com as informações da reserva.
Preenchendo o formulário de usuário Os valores recuperados do banco de dados agora são atribuídos aos controles no formulário de usuário. Como o formulário de usuário é envolvido em muitas das instruções de atribuição, um bloco With é iniciado. Subseqüentemente, os objetos utilizados e que começam com um ponto são considerados como pertencentes ao objeto da instrução With, o próprio formulário de usuário. With ReservationForm
Agora você precisa preencher a caixa de listagem que mostra as salas disponíveis. Ela é tratada um pouco diferentemente das caixas de combinação de data, horário de início e horário de término. A razão é que a lista de salas disponíveis pode alterar de vez em quando se acaso uma sala tornar-se indisponível para reuniões ou se uma nova sala for adicionada. Ao contrário, o número de períodos de 15 minutos disponível e suas designações são constantes, como são os dias no ano. Como datas e horas são constantes, suas caixas de combinação podem utilizar intervalos de planilha estáticos como suas fontes de dados. Mas como a lista de salas disponíveis pode alterar inesperadamente, é desejável reconstruir o conteúdo da caixa de listagem toda vez que o formulário for exibido. Comece obtendo o número de salas disponíveis a partir da coluna A. ResourceCount = ActiveSheet.Cells(600, 1).End(xlUp).Row - 1
Limpe as entradas atuais na caixa de listagem de salas. Então faça um loop pela lista de salas na planilha ativa e preencha a caixa de listagem.
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
295
.lbResources.Clear For i = 2 To ResourceCount .lbResources.AddItem (ActiveSheet.Cells(i, 1)) Next i
O banco de dados armazena a sala reservada como um ID numérico, não como o nome da sala. Então, o código pega o nome da sala reservada da planilha e utiliza-o para configurar a seleção atual da caixa de listagem de salas. .lbResources = ActiveSheet.Cells(ActiveCell.Row, 1)
A C I D
Uma caixa de listagem pode ter sua propriedade MultiSelectconfigurada como uma seleção múltipla. Dessa maneira, um usuário pode selecionar mais de um dos itens na lista. Se configurar essa propriedade como fmMultiSelectMulti, você deve trabalhar com o índice de lista da caixa de listagem. Por exemplo, se fosse possível selecionar mais de uma sala na caixa de listagem, você mostraria que essas salas foram selecionadas configurando a propriedade Selected para esse item como True. Por exemplo, para selecionar tanto o 11° quanto o 13° itens em uma caixa de listagem de múltipla seleção, utilize isto: .lbResources.Selected(10) = True .lbResources.Selected(12) = True
A T O N
Em cada caso, 1 é subtraído do valor calculado do ListIndex porque o primeiro elemento da lista da caixa de combinação é o elemento número 0. Isso não é afetado pelo uso de Option Base 1 . O mesmo se aplica à caixa de listagem de salas, embora seja a propriedade Selected em vez da propriedade LastIndex que está configurada.
Agora configure os caixas de combinação (informalmente conhecidas como drop-down) com seus valores adequados. As caixas de combinação exibem a data de reserva e o horário de início e de término. O código configura o valor das caixas de combinação diretamente a partir dos valore s dos campos no recordset. .cbDate = rsRecordsToEdit.Fields("ReservationDate") .cbStartTime = rsRecordsToEdit.Fields("StartTime") .cbStopTime = rsRecordsToEdit.Fields("StopTime")
Entendendo a caixa de combinação Uma caixa de combinação talvez seja mais conhecida como lista suspensa ( dropdown). A caixa de combinação combina uma caixa de texto com uma caixa de listagem. A caixa de texto normalmente é visível e mostra o item que foi selecionado. A caixa de listagem aparece, ou se abre, ao se clicar em sua seta. Utilizando o código VBA, você pode configurar o valor de uma caixa de combinação de duas formas. Atribuir o valor diretamente à caixa de combinação (a abordagem utilizada no código descrito aqui) ou configurar sua propriedade ListIndex . A ListIndex é o número do item na caixa de listagem (começando com 0, não 1). Se você atribuir um valor diretamente, não precisa ser membro da lista e o valor de ListIndex é configurado como –1. O primeiro elemento da lista da caixa de combinação é o elemento número 0. Portanto, se quiser configurar o valor da caixa de combinação como seu elemento 15, você utilizaria algo como ReservationForm.cbDate.ListIndex = 14
Isso não é afetado pelo uso de Option
Base 1 . O mesmo se aplica a uma caixa de listagem pura.
296
Gerenciando dados com o Microsoft Excel
Suponha que queira que uma caixa de combinação exiba o valor utilizar uma instrução como esta:
. Você poderia
7:00 AM
cbStartTime = "7:00 AM"
Alternativamente, você poderia configurar a propriedade ListIndex da caixa de combinação. Assumindo que 7:00 AM é o terceiro valor na lista, você utilizaria isto (lembre-se, o primeiro item em uma caixa de listagem é o item número 0) : ReservationForm.cbStartTime.ListIndex = 2
Os possíveis valores para as três caixas de combinação são armazenados em uma planilha oculta, UserNames. (É a mesma planilha que armazena o caminho e o nome do banco de dados de reservas.) A planilha é mostrada na Figura 11.5. Figura 11.5 Se você armazena valores utilizados pelo código em uma planilha, é uma boa idéia configurar a propriedade Visible da planilha como xlVeryHidden.
Comparada às escolhas envolvidas na configuração dos valores de caixas de listagem e caixas de combinação, é muito simples e direta a configuração do valor de caixas de texto como os que exibem o número de períodos de arrumação e de limpeza. Simplesmente configure a caixa de texto como igual ao valor que você quer exibir. .tbSetupPeriods = rsRecordsToEdit.Fields("SetupPeriods") .tbCleanupPeriods = rsRecordsToEdit.Fields("CleanupPeriods") .tbPurposeBox.Value = rsRecordsToEdit.Fields("Purpose") .tbReservedForBox.Value = rsRecordsToEdit.Fields("ReservedFor")
Naturalmente, você pode configurar valores no formulário do usuário condicionalmente. Aqui, um botão de opção ou outro obtém a configuração como True dependendo do valor de um campo no recordset.
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
297
If rsRecordsToEdit.Fields("Participants") = "Internal" Then .obInternal.Value = True ElseIf rsRecordsToEdit.Fields("Participants") = "External" Then .obExternal.Value = True End If
Cerca de 300 linhas de código são omitidas nesse ponto. Seu propósito é configurar os valores de outros controles no formulário de usuário, controles que não aparecem na Figura 11.5 porque estão localizados em guias diferentes. A abordagem utilizada é a mesma que esta seção ilustrou: atribua a um controle o valor localizado no recordset, no campo que está associado ao controle. Só para terminar os controles na guia principal do formulário, o departamento responsável e a conta a ser cobrada são configurados: .cbDept.Value = rsRecordsToEdit.Fields("Department") .tbAccount.Value = rsRecordsToEdit.Fields("Account") End With Set dbReservatioons = Nothing Set rsRecordsToEdit = Nothing End Sub
O bloco With é terminado, as variáveis de objeto são liberadas e a sub-rotina em si é concluída.
Permitindo reservas recorrentes Um aspecto particularmente útil do design dessa aplicação de reservas é sua capacidade de lidar com reservas recorrentes. Do ponto de vista do usuário, uma reserva recorrente representa uma reunião que ocorre mais de uma vez, normalmente de forma regular, na mesma sala e com os mesmos requisitos de arrumação de sala. Por exemplo, o usuário poderia fazer todos os arranjos necessários para uma reserva de reunião e, além disso, solicitar que esses mesmos arranjos se apliquem a outra data. A outra data poderia ser todas as quartas-feiras ou o terceiro dia de semana a cada mês ou em cada dia por cinco dias consecutivos. Do ponto de vista da aplicação, uma reserva recorrente consiste em pelo menos dois registros que compartilham o mesmo MasterID. Em resumo, eis como as reservas individuais são agrupadas em uma configuração de reservas recorrentes. ➪
Para informações adicionais sobre como mover dados de uma pasta de trabalho para um banco de dados, consulte “Utilizando recordsets DAO para mover dados do Excel para um banco de dados Jet”, p. 324.
O banco de dados contém uma tabela, nomeada Masters, cujo único propósito é fornecer um novo ID mestre para cada reserva. A tabela tem um campo AutoNumeração chamado MasterID. Quando o usuário estabelece uma nova reserva, recorrente ou não, estas instruções estão entre o código que executa: Sub GetMasterID(dbReservation As Database, _ MasterResID As Long) Dim rsMasterTable As Recordset Set rsMasterTable = dbReservation.TableDefs("Masters") _ .OpenRecordset(dbOpenDynaset)
298
Gerenciando dados com o Microsoft Excel
With rsMasterTable .AddNew .Update .MoveLast MasterResID = .Fields("MasterID") .Close End With Set rsMasterTable = Nothing End Sub
Um novo registro é adicionado à tabela chamada Masters. Quando isso é feito, um novo e único valor é automaticamente colocado no campo AutoNumeração MasterID. Esse valor é atribuído à variável MasterResID. A T O N
Note que o MasterResID é passado à sub-rotina GetMasterID como um Longo — isto é, como um inteiro longo. Os campos AutoNumeração no Access são por padrão inteiros longos, então é melho r dar a MasterResID o mesmo tipo de variável.
Milissegundos mais tarde, quando as informações sobre a reserva estiverem armazenadas no banco de dados, o novo registro de reserva armazena o valor de MasterResID: rsReservation.Fields("MasterID") = MasterResID
Utilizando esse arranjo, quando um usuário quiser editar uma reserva — ou mesmo excluí-la — o código pode obter todas as reservas recorrentes e relacionadas e, à vontade do usuário, aplicar as alterações a todas elas ou somente àquelas que o usuário começou selecionando.
Excluindo registros do banco de dados e da planilha Eis como funciona na prática. Suponha que o usuário clicou em uma célula em uma reserva existente na planilha do Excel e escolheu um item do menu personalizado que inicia o cancelamento da reserva. É necessário cuidar de duas tarefas amplas: indicar ao banco de dados que a reserva (ou, se recorrente, as reservas) foi cancelada e remover a reserva da planilha a fim de liberar a sala para alguma outra utilização naquele dia e hora. O seguinte código gerencia essas tarefas. Gerenciando as preliminares
Como de costume, a procedure começa com uma instrução Sub e a declaração das variáveis necessárias. Sub RemoveReservation()
É bom verificar para certificar-se de que o usuário selecionou uma célula no intervalo de reserva antes de tentar removê-la. A tal célula foi colorida de vermelho para representar uma reserva firme ou de amarelo para representar uma retenção experimental. A variável CellColor é utilizada para determinar se o usuário começou selecionando uma célula nesse intervalo. Dim CellColor As Integer
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
299
Três variáveis são declaradas para identificar unicamente a reserva em questão. Como observado na seção anterior, somente um registro pode ter uma data, horário de início e sala em particular. Determinando seus valores para a reserva que o usuário quer cancelar, o código pode localizar essa reserva específica no banco de dados e tomar a ação apropriada. Elas são declaradas como WhichDate, WhichRoom e StartTime. Dim WhichDate As Date, WhichRoom As String, StartTime As Date WhichColumn
da reserva.
é utilizada como na seção anterior para ajudar a determinar o horário de início
Dim WhichColumn As Integer
É melhor, depois que o registro da reserva foi localizado mas antes de realmente cancelálo, perguntar ao usuário se ele está certo de quer prosseguir. A variável Confirm é utilizada para capturar essa informação. Dim Confirm As Integer
Dois recordsets são declarados: um para representar a tabela Reservations e um para associar os IDs que identificam salas com os nomes das salas. Dim rsReservation As Recordset, rsRooms As Recordset
Dois inteiros Longos são declarados, um para conter o ID mestre da reserva — aquele que ele compartilha com outros registros que fazem parte do mesmo grupo de recorrência — e um ID de registro que identifica unicamente uma reserva particular. Dim MasterResID As Long, RecordID As Long
Duas variáveis de objeto são utilizadas para representar objetos de banco de dados. A consulta que retorna o registro de reserva (ou registros) é qdfEditDetails, e o recordset que possui o registro (ou registros) é rsRecordsToDelete. Dim qdfRecordDetails As QueryDef Dim rsRecordsToDelete As Recordset
As duas próximas variáveis requeridas conterão o número de registros de reserva no recordset: um se a reserva for não-recorrente e um número inicialmente desconhecido de registros caso contrário. Por último, ResourceID é utilizado para armazenar o número de ID que identifica uma sala em particular. Dim ReservationCount As Long Dim ResourceID As Long
Quatro variáveis são declaradas para suportar a comunicação com o usuário via uma caixa de mensagem. A variável Msg armazena a própria mensagem, a variável Style contém um inteiro que determina a mistura de botões na caixa de mensagem (por exemplo, só OK, Sim/Não/Cancelar e assim por diante), Title para conter a string que é mostrada na barra de título da caixa de mensagem e Response para capturar a resposta do usuário à caixa de mensagem. Dim Msg As String, Style As Integer, Title As String, _ Response As Integer
300
Gerenciando dados com o Microsoft Excel
Verificando e confirmando a solicitação do usuário O código então se certifica de que o usuário começou selecionando uma célula dentro de uma reserva existente. Uma célula que representa uma reserva tem sua cor configurada como amarela (para uma retenção temporária) ou vermelha (para uma reserva firme). O código não funcionaria adequadamente se o usuário não tivesse começado identificando uma reserva, então o código verifica se a cor da célula é amarela ou vermelha. O código determina o índice numérico que identifica a cor da célula ativa — 3 para vermelha, 6 para amarela. Se não for 3 nem 6, ele exibe uma caixa de mensagem, queixando-se de que o usuário não selecionou uma célula de reserva. Então a sub-rotina é terminada de modo que nada mais de seu código executa. CellColor = ActiveCell.Interior.ColorIndex If CellColor <> 6 And CellColor <> 3 Then MsgBox "To remove a reservation, please begin by selecting a cell " _ & "that's part of an existing reservation -- that is, a red " _ & "cell or a yellow cell." Exit Sub End If
Os argumentos para essa caixa de mensagem não indicam um conjunto particular de botões de comando. Portanto, o VBA exibe o padrão, que é um único botão OK. Independentemente do que o usuário fizer para dispensar a caixa de mensagem, o código depois interrompe o processamento. Assumindo que o usuário começou selecionando uma reserva, o código em seguida confirma que o usuário realmente quer excluí-la. O VBA exibe uma caixa de mensagem que pergunta se usuário tem certeza. O argumento vbOKCancel faz com que a caixa de mensagem tenha um botão OK e um botão Cancelar. Confirm = MsgBox("Are you sure you want to " _ & "delete this reservation?", vbOKCancel)
Se o usuário clicar no botão Sim, indicando que ele quer prosseguir e excluir a reserva, a caixa de mensagem retorna um 1. Portanto, se ela retornar qualquer outra coisa, o código interrompe o processamento por meio da instrução Exit Sub. If Confirm <> 1 Then Exit Sub End If
Caso contrário, o código continua e vai procurar o caminho e o nome do banco de dados de reservas e o abre. DatabaseName = ThisWorkbook.Sheets("UserNames").Cells(1, 3) Set dbReservation = OpenDatabase(DatabaseName, False, False, "MS Access")
Estabelecendo os recordsets Dois recordsets são estabelecidos. A tabela Reservations do banco de dados é necessária porque o registro de reserva precisa ser excluído dessa tabela. A tabela Resources é necessária para que o código possa determinar o ID da sala que está sendo reservada. (A planilha mostra os nomes das salas, mas a tabela Reservations armazena seus IDs em vez de seus nomes.)
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
301
Set rsReservation = dbReservation.TableDefs _ ("Reservations").OpenRecordset(dbOpenDynaset) Set rsRooms = dbReservation.TableDefs _ ("Resources").OpenRecordset(dbOpenDynaset)
Localizando a reserva no banco de dados
É hora de obter as informações da planilha que identificarão unicamente a reserva que o usuário selecionou. Como observado na seção “Identificando o registro de reserva”, essas informações incluem a data da reserva, sua sala e seu horário de início. A data e o horário são obtidos precisamente da mesma maneira como em “Identificando o registro de reserva”. Obtém-se o nome da planilha ativa para a data. Obtém-se o horário de início retrocedendo ao começo do intervalo de reserva para localizar sua coluna inicial — o horário de início está na linha 1 dessa coluna. WhichDate = ActiveSheet.Name WhichColumn = ActiveCell.Column - 1 Do While Cells(ActiveCell.Row, WhichColumn) = _ Cells(ActiveCell.Row, ActiveCell.Column) WhichColumn = WhichColumn - 1 Loop StartTime = ActiveSheet.Cells(1, WhichColumn + 1).Value
Nesse caso, porém, é mais conveniente utilizar o ID da sala em vez de seu nome. Mas esse ID tem de ser obtido utilizando o nome da sala e outra sub-rotina, uma breve, é utilizada para fazer isso. O nome da sala é obtido como antes da coluna A da linha da reserva. WhichRoom = ActiveSheet.Cells(ActiveCell.Row, 1).Value ConvertResource WhichRoom, ResourceID, rsRooms
Então a sub-rotina ConvertResource é chamada, com o nome da sala ( WhichRoom), uma variável para possuir seu ID (ResourceID) e o recordset que contém os nomes e IDs de salas ( rsRooms) como argumentos. Até agora, ResourceID não tem nenhum valor, mas ao retornar de ConvertResource conterá o ID da sala selecionada. Eis o código dessa procedure: Sub ConvertResource(RoomName As String, _ ResourceID As Long, rsRooms As Recordset) Dim Criterion As String Criterion = "ResourceName = '" & RoomName & "'" With rsRooms .FindFirst Criterion ResourceID = .Fields("ResourceID") End With End Sub
O método FindFirst é utilizado para localizar o nome da sala selecionada na tabela Resources, representado pelo recordset rsRooms. O código armazena em Criterion uma string constituída pelo nome do campo a ser pesquisado, ResourceName, e o nome da sala a ser localizada, armazenado em RoomName. Suponha que o nome Cafeteria esteja em RoomName. A string de pesquisa, Criterion, seria ResourceName = 'Cafeteria'
302
Gerenciando dados com o Microsoft Excel
O registro com o valor de RoomName no campo ResourceName é localizado, e seu valor no campo ResourceID é armazenado na variável ResourceID. Essa variável então é retornada à procedure chamadora, RemoveReservation. Esse código não utiliza a propriedade NoMatch porque o restante da aplicação garante que um registro será localizado. Se você utiliza um método de pesquisa como FindFirst ou Seek, considere utilizar NoMatch. Essa propriedade é True se o método de pesquisa não conseguir localizar um registro correspondente aos critério s de pesquisa. Por exemplo, A C I D
rsRooms.FindFirst Criterion If rsRooms.NoMatch Then [Código para se recuperar de uma falha ao tentar localizar um registro] End If
Você também poderia utilizar algo como If Not rsRooms.NoMatch para permitir que seu código continue o processamento. (Demora um pouco para se acostumar com a dupla negativa.) Verificando reservas recorrentes
Agora que ResourceID da sala foi localizado, o código na sub-rotina RemoveReservation continua. Ele primeiro verifica para ver se o MasterID do registro é compartilhado por quaisquer outros registros — isto é, verifica se a reserva selecionada é recorrente. Ele faz isso chamando a função FindReservationMasterIDInDB, utilizando o banco de dados de reservas, a data da reserva, o ID da sala e o horário de início como argumentos. MasterResID = FindReservationMasterIDInDB _ (dbReservation, WhichDate, ResourceID, StartTime)
A função não é predefinida, mas é uma função definida pelo usuário ( user-defined function – UDF). Eis seu código. Function FindReservationMasterIDInDB(dbReservation As Database, _ WhichDate As Date, WhichRoom As Long, StartTime As Date) As Long
Observe que a declaração da procedure começa com Function em vez de Sub ( procedure é um termo genérico para uma função ou uma sub-rotina). Também observe que a função tem um tipo; aqui, é Long, então o valor que ele retorna é representado como um Inteiro longo. Duas variáveis de objeto são declaradas na função, uma consulta que retornará todos os registros que compartilham o mesmo MasterID e um recordset que conterá esses registros. Dim qdfDetail As QueryDef Dim rsDetail As Recordset Set qdfDetail = dbReservation.QueryDefs("FindSingleReservation")
A consulta chamada FindSingleReservation aparece no modo de Design na Figura 11.6.
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
303
Figura 11.6 Note o campo calculado, que formata StartTime como Data normal (hh:mm AM/PM).
O código passa a sala, a data e o horário de início (formatado como Data normal) para a consulta e seus resultados são atribuídos ao recordset. With qdfDetail .Parameters("WhichRoom") = WhichRoom .Parameters("WhichDate") = WhichDate .Parameters("WhichTime") = Format(StartTime, "Medium Time") Set rsDetail = .OpenRecordset(dbOpenDynaset) End With
Então a própria função é configurada como o valor do campo MasterID no recordset. Com esse valor disponível, será possível descobrir se há outros registros no banco de dados que compartilham o mesmo MasterID. Se houver, o código perguntará ao usuário se todos devem ser excluídos ou somente aquele que foi selecionado quando o usuário começou o processo. A T O N
A atribuição de um valor à função é típica de UDFs e de funções em geral. Esse aspecto é o que permite a você escrever suas próprias funções no VBA e fazê-las retornar valores diretamente à planilha.
FindReservationMasterIDInDB = rsDetail.Fields("MasterID") Set qdfDetail = Nothing Set rsDetail = Nothing End Function
Agora a variável MasterResID foi configurada como igual ao MasterID da reserva selecionada. Esse valor é passado como um parâmetro à consulta chamada DetailRecords. A consulta retornará somente um registro — o escolhido pelo usuário — se não fizer parte de um grupo de reservas recorrentes. Se a reserva ocorre periodicamente, a consulta selecionará todas elas e o código perguntará se o usuário quer excluir todas as reservas ou somente aquelas que selecionou. A consulta é atribuída a uma variável de objeto e seu parâmetro é passado. Então os resultados da consulta são atribuídos a um recordset. Set qdfRecordDetails = dbReservation.QueryDefs("DetailRecords") qdfRecordDetails.Parameters("WhichID") = MasterResID Set rsRecordsToDelete = qdfRecordDetails.OpenRecordset(dbOpenDynaset)
304
Gerenciando dados com o Microsoft Excel
Com o recordset estabelecido, o código segue para seu registro final e então obtém a contagem de registro. É necessário primeiro mover-se para o registro final: em um recordset DAO, a contagem de registro não está disponível até que o registro final tenha sido alcançado. Portanto, o código emprega o método MoveLast no recordset e então obtém a contagem do número de registros. With rsRecordsToDelete .MoveLast ReservationCount = .RecordCount End With
Agora o código determina o que fazer se houver mais de uma reserva no recordset. If ReservationCount > 1 Then
Se houver múltiplos registros que compartilham o mesmo MasterID, uma caixa de mensagem é preparada. A pergunta a ser imposta ao usuário é montada na variável de string Msg, que pergunta ao usuário se é para excluir todas as reservas recorrentes ou somente a reserva selecionada: Msg = "This reservation is one of a recurring group " & _ "or a multi-room group. " & vbLf & _ "Do you want to delete all the records as a group? " & _ vbLf & "(If you click No, you will delete only " & _ "the reservation you selected.)"
A vbLf é uma constante que representa um caractere de quebra de linha. É utilizada aqui para dividir a mensagem em três linhas separadas. A caixa de mensagem oferecerá um botão Sim, um Não e um Cancelar. Essa combinação é especificada pela palavra-chave vbYesNoCancel e armazenada na variável Style. Um título é estabelecido e a caixa de mensagem exibida. Style = vbYesNoCancel Title = "Delete multiple reservations"
A variável Response captura o botão em que o usuário clica. Response = MsgBox(Msg, Style, Title)
Se o usuário clicar no botão Cancelar, o fluxo do código sai dessa sub-rotina. If Response = vbCancel Then Exit Sub
Se o usuário clicar no botão Não, significa que ele quer excluir somente a reserva selecionada e nenhum de seus registros relacionados. Nesse caso, feche o recordset que contém múltiplos registros e o restabeleça apenas com o registro selecionado. ElseIf Response = vbNo Then rsRecordsToDelete.Close RecordID = FindReservationDetailIDInDB _ (dbReservation, WhichDate, ResourceID, StartTime)
A função FindReservationDetailIDInDB é idêntica à função FindReservationMasterIDInDB , discutida anteriormente nesta seção, exceto que é configurada igual ao ID da reserva selecionada, não a seu MasterID. Esse ID de reserva é passado como um parâmetro para uma consulta que
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
305
retorna o registro único de reserva e os resultados dessa consulta são atribuídos ao recordset rsRecordsToDelete .
Set qdfRecordDetails = dbReservation.QueryDefs _ ("FindOneRecord") qdfRecordDetails.Parameters("WhichID") = RecordID Set rsRecordsToDelete = qdfRecordDetails _ .OpenRecordset(dbOpenDynaset) End If
End If
Recapitulando: se o usuário quiser excluir todo um grupo de reservas recorrentes, ele indica isso clicando no botão Sim na caixa de mensagem e o recordset rsRecordsToDelete permanece como era. Se quiser excluir somente a reserva selecionada, ele clica no botão Não na caixa de mensagem. Nesse caso, o ID de registro único da reserva é localizado utilizando sua data, sala e horário de início, e esse registro é utilizado para preencher o recordset rsRecordsToDelete. O código então faz um loop pelo recordset e configura o valor do campo Cancelar como Verdadeiro para cada registro. Com esse campo configurado, um registro não será subseqüentemente retornado à pasta de trabalho. Observe o uso dos métodos Edit e Update: With rsRecordsToDelete .MoveFirst Do While Not .EOF .Edit .Fields("Cancelled") = True .Update .MoveNext Loop End With
Retoque final Resta remover a reserva que foi originalmente selecionada da planilha. Isso pode ser feito facilmente chamando a sub-rotina GetSingleDayFromDB , discutida anteriormente neste capítulo na seção “Trazendo dados de volta de uma consulta parametrizada”. Lembre-se de que a sub-rotina limpa a planilha e então recupera todas as reservas para a data mostrada na guia da planilha. Mas ela não recupera nenhum registro em que o campo Cancelar é Verdadeiro, então a reserva excluída não reaparece na planilha. GetSingleDayFromDB
Por fim, ocorre a limpeza que libera a variável de objeto. Set Set Set Set
rsReservation = Nothing rsRooms = Nothing qdfRecordDetails = Nothing rsRecordsToDelete = Nothing
End Sub
Essa sub-rotina emprega uma variedade de técnicas que você achará útil para retornar informações de um banco de dados via um recordset. Ela chama outras sub-rotinas que modificam o valor de variáveis, bem como um UDF que localiza IDs de registro. Ela utiliza consultas para
306
Gerenciando dados com o Microsoft Excel
retornar registros específicos por meio de parâmetros passados às consultas bem como critérios fixados (como o campo Cancelar na sub-rotina GetSingleDayFromDB). Ela mostra como utilizar informações na pasta de trabalho — nesse caso, a data a partir da guia da planilha, os nomes de sala a partir da coluna A, e horários do dia a partir da linha 1 — para determinar os registros que são retornados a partir do banco de dados. Ela também demonstra como revisar o conteúdo de recordsets com base em respostas do usuário a perguntas colocadas em caixas de mensagem. Esta seção conclui fornecendo todo o código discutido, com os comentários removidos.
O código completo Sub GetSingleDayFromDB(Optional DateLastModified As Date) Dim dbReservation As DAO.Database Dim qdfRetrieveCurrent As DAO.QueryDef Dim rsRecordsToRetrieve As DAO.Recordset Dim Dim Dim Dim Dim Dim Dim
StartCol As Integer, StopCol As Integer, WhichRow As Integer ReservationRange As Range SetupPeriods As Integer, CleanupPeriods As Integer TimeAsText As String TimeArray(71) As String, RoomArray() As String i As Integer ResourceCount As Integer
ResourceCount = ActiveSheet.Cells(600, 1).End(xlUp).Row - 1 ReDim RoomArray(ResourceCount) DatabaseName = ThisWorkbook.Sheets("UserNames").Cells(1, 3) Set dbReservation = OpenDatabase(DatabaseName, False, _ False, "MS Access;PWD=Nirmac") Set qdfRetrieveCurrent = dbReservation.QueryDefs("RetrieveSingleDay") Application.ScreenUpdating = False qdfRetrieveCurrent.Parameters("ThisDate") = ActiveSheet.Name Set rsRecordsToRetrieve = qdfRetrieveCurrent.OpenRecordset(dbOpenForwardOnly) ActiveSheet.Range(Cells(2, 2), Cells(ResourceCount + 1, 73)).Clear ActiveSheet.Range(Cells(2, 2), Cells(ResourceCount + 1, 73)) _ .Interior.ColorIndex = xlNone If Not rsRecordsToRetrieve.BOF Then For i = 1 To 71 TimeArray(i) = Application.Text(ActiveSheet.Cells(1, i + 1), "h:mm AM/PM") Next i For i = 1 To ResourceCount RoomArray(i) = ActiveSheet.Cells(i + 1, 1) Next i With rsRecordsToRetrieve Do While Not .EOF TimeAsText = Application.WorksheetFunction.Text _
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
(.Fields("StartTime"), "h:mm AM/PM") StartCol = Application.Match(TimeAsText, TimeArray, 0) + 1 TimeAsText = Application.WorksheetFunction.Text _ (.Fields("StopTime"), "h:mm AM/PM") StopCol = Application.Match(TimeAsText, TimeArray, 0)
WhichRow = Application.Match(.Fields("ResourceName"), _ RoomArray, 0) + 1 Set ReservationRange = ActiveSheet.Range(Cells(WhichRow, _ StartCol), Cells(WhichRow, StopCol)) ReservationRange.FormulaR1C1 = UCase(.Fields("Purpose")) If .Fields("ReserveHold") = "Reserve" Then ReservationRange.Interior.ColorIndex = 3 Else ReservationRange.Interior.ColorIndex = 6 End If
SetupPeriods = .Fields("SetupPeriods") CleanupPeriods = .Fields("CleanupPeriods") If SetupPeriods > 0 Then ReservationRange.Offset(0, -SetupPeriods).Resize _ (1, SetupPeriods).Interior.ColorIndex = 48 End If With ReservationRange.Offset(0, -SetupPeriods) _ .Resize(1, 1).Borders(xlEdgeLeft) .LineStyle = xlContinuous .Weight = xlThick .ColorIndex = 1 End With If CleanupPeriods > 0 Then ReservationRange.Offset(0, ReservationRange.Columns.Count) _ .Resize(1, CleanupPeriods).Interior.ColorIndex = 48 End If With ReservationRange.Offset(0, ReservationRange.Columns.Count _ + CleanupPeriods - 1).Resize(1, 1).Borders(xlEdgeRight) .LineStyle = xlContinuous .Weight = xlThick .ColorIndex = 1 End With DateLastModified = .Fields("MostRecentlyModified") ReservationRange.Resize(1, 1).ClearComments ReservationRange.Resize(1, 1).AddComment ("Reserved By: " & _ .Fields("ReserverName") & Chr(10) & "Reserved For: " & _ .Fields("ReservedFor") & Chr(10) & "Last Modified: " & _ Format(.Fields("MostRecentlyModified"), "m/d/yy")) If .Fields("Participants") = "External" Then ReservationRange.Font.Bold = True End If .MoveNext Loop End With End If Application.StatusBar = False Set qdfRetrieveCurrent = Nothing Set rsRecordsToRetrieve = Nothing Set ReservationRange = Nothing End Sub Dim Dim
Sub FromDBtoForm(rsRecordsToEdit As Recordset, WhichRoom As String) StopTime As Date, StartTime As Date RoomListIndex As Integer, StartTimeListIndex As Integer, i As Integer
307
308
Gerenciando dados com o Microsoft Excel
Dim StopTimeListIndex As Integer, DateListIndex As Integer Dim WhichDate As Date Dim ResourceCount As Integer ResourceCount = ActiveSheet.Cells(600, 1).End(xlUp).Row - 1 StartTime = rsRecordsToEdit.Fields("StartTime") StopTime = rsRecordsToEdit.Fields("StopTime") WhichDate = rsRecordsToEdit.Fields("ReservationDate") GetDropdownIndices RoomListIndex, WhichRoom, StartTime, StartTimeListIndex, _ StopTime, StopTimeListIndex, WhichDate, DateListIndex With ReservationForm For i = 1 To ResourceCount .ResourceListBox.AddItem (Sheets("UserNames").Cells(i, 4).Value) Next i .DateDropDown.Value = Sheets("UserNames").Cells(DateListIndex, 5) .ResourceListBox.Selected(RoomListIndex - 1) = True .ddStartTime.ListIndex = StartTimeListIndex - 1 .ddStopTime.ListIndex = StopTimeListIndex - 1 .SetupPeriods = rsRecordsToEdit.Fields("SetupPeriods") .CleanupPeriods = rsRecordsToEdit.Fields("CleanupPeriods")
If rsRecordsToEdit.Fields("RoundTables") = "Round Tables" Then .tbChairsPerTable.Visible = True .tbChairsPerTable.Text = rsRecordsToEdit.Fields("ChairsPerTable") .lblChairsPerTable.Visible = True Else .tbChairsPerTable.Visible = False .lblChairsPerTable.Visible = False End If .PurposeBox.Value = rsRecordsToEdit.Fields("Purpose") .ReservedForBox.Value = rsRecordsToEdit.Fields("ReservedFor") If rsRecordsToEdit.Fields("Participants") = "Internal" Then .obInternal.Value = True ElseIf rsRecordsToEdit.Fields("Participants") = "External" Then .obExternal.Value = True End If
'Código semelhante e repetitivo omitido aqui .cbDept.Value = rsRecordsToEdit.Fields("Department") .tbAccount.Value = rsRecordsToEdit.Fields("Account") End With Set dbReservatioons = Nothing Set rsRecordsToEdit = Nothing End Sub Sub GetMasterID(MasterResID As Long) Dim rsMasterTable As Recordset Set rsMasterTable = dbReservation.TableDefs("Masters") _ .OpenRecordset(dbOpenDynaset) With rsMasterTable .AddNew .Update .MoveLast MasterResID = .Fields("MasterID")
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
.Close End With End Sub Sub Dim Dim Dim Dim Dim Dim
RemoveReservation() MeetingRange As Range, SetUpRange As Range, CleanUpRange As Range ICI As Integer, ReservationCount As Long, i As Long WhichDate As Date, WhichRoom As String, StartTime As Date, StopTime As Date WhichColumn As Integer Confirm As Integer rsReservation As Recordset, rsReserver As Recordset, _ rsResource As Recordset Dim rsTableForSeek As Recordset Dim MasterResID As Long, ResourceID As Long Dim qdfEditDetails As QueryDef Dim rsRecordsToEdit As Recordset Dim RecordID As Long Dim Msg As String, Style As Integer, Title As String, Response As Integer Dim CanRemove As Boolean Dim CountSheets As Integer BookCheck = ActiveWorkbook Is ThisWorkbook If Not BookCheck Then MsgBox "Please use this command only with the Resources workbook active." End End If If Not SuppressWarning Then Confirm = MsgBox("Are you sure you want to delete this reservation?", _ vbOKCancel) If Confirm <> 1 Then Exit Sub End If End If DatabaseName = ThisWorkbook.Sheets("UserNames").Cells(1, 3) Set dbReservation = OpenDatabase(DatabaseName, False, False, "MS Access") Set rsReservation = dbReservation.TableDefs("Reservations") _ .OpenRecordset(dbOpenDynaset) Set rsReserver = dbReservation.TableDefs("Reservers") _ .OpenRecordset(dbOpenDynaset) Set rsResource = dbReservation.TableDefs("Resources") _ .OpenRecordset(dbOpenDynaset) ICI = ActiveCell.Interior.ColorIndex If ICI <> 6 And ICI <> 3 Then MsgBox "To remove a reservation, please begin by selecting a cell " _ & "that's part of an existing reservation -- that is, a red " _ & "cell or a yellow cell." Exit Sub End If WhichDate = ActiveSheet.Name WhichRoom = ActiveSheet.Cells(ActiveCell.Row, 1).Value ConvertResource WhichRoom, ResourceID, rsResource WhichColumn = ActiveCell.Column - 1 Do While Cells(ActiveCell.Row, WhichColumn) = _ Cells(ActiveCell.Row, ActiveCell.Column) WhichColumn = WhichColumn - 1 Loop StartTime = ActiveSheet.Cells(1, WhichColumn + 1).Value MasterResID = FindReservationMasterIDInDB _
309
310
Gerenciando dados com o Microsoft Excel
(dbReservation, WhichDate, ResourceID, StartTime) Set qdfEditDetails = dbReservation.QueryDefs("DetailRecords") qdfEditDetails.Parameters("WhichID") = MasterResID Set rsRecordsToEdit = qdfEditDetails.OpenRecordset(dbOpenDynaset) With rsRecordsToEdit .MoveLast ReservationCount = .RecordCount .MoveFirst End With If ReservationCount > 1 Then Msg = "This reservation is one of a recurring group or a multi-room " & _ "group. " & Chr(10) & "Do you want to delete all the records " & _ "as a group?" & Chr(10) & "(If you click No, you will delete " & _ "only the reservation you selected.)" Style = vbYesNoCancel Title = "Delete multiple reservations" Response = MsgBox(Msg, Style, Title)
If Response = vbCancel Then End ElseIf Response = vbNo Then rsRecordsToEdit.Close RecordID = FindReservationDetailIDInDB _ (dbReservation, WhichDate, ResourceID, StartTime) Set qdfEditDetails = dbReservation.QueryDefs("EditOneRecord") qdfEditDetails.Parameters("WhichID") = RecordID Set rsRecordsToEdit = qdfEditDetails.OpenRecordset(dbOpenDynaset) End If
End If Set MeetingRange = Selection GetFullReservationRange SetUpRange, MeetingRange, CleanUpRange rsRecordsToEdit.MoveFirst CanRemove = CheckEditPermits(MeetingRange) If Not CanRemove Then MsgBox "Only the person who made the reservation can delete it." End End If Sheets(MeetingRange.Parent.Name).Activate MeetingRange.Select SuppressWarning = True RemoveReservationFromWorksheet SetUpRange, MeetingRange, CleanUpRange SuppressWarning = False With rsRecordsToEdit Do While Not .EOF .Edit .Fields("Cancelled") = True .Update .MoveNext Loop End With GetSingleDayFromDB Set rsReservation = Nothing
Capítulo 11 – Obtendo dados do Access para o Excel com o ADO e o DAO
311
Set rsRooms = Nothing Set qdfRecordDetails = Nothing Set rsRecordsToDelete = Nothing End Sub Sub ConvertResource(ResourceName As String, ResourceID As Long, _ rsResource As Recordset) Dim Criterion As String Criterion = "ResourceName = '" & ResourceName & "'" With rsResource .FindFirst Criterion ResourceID = .Fields("ResourceID") End With End Sub Function FindReservationMasterIDInDB(dbReservation As Database, _ WhichDate As Date, WhichRoom As Long, StartTime As Date) As Long Dim qdfDetail As QueryDef Dim rsDetail As Recordset Set qdfDetail = dbReservation.QueryDefs("FindSingleReservation") qdfDetail.Parameters("WhichRoom") = WhichRoom qdfDetail.Parameters("WhichDate") = WhichDate qdfDetail.Parameters("WhichTime") = Format(StartTime, "Medium Time") Set rsDetail = qdfDetail.OpenRecordset(dbOpenDynaset) FindReservationMasterIDInDB = rsDetail.Fields("MasterID") Set qdfDetail = Nothing Set rsDetail = Nothing End Function
Olhando para frente Este capítulo focalizou como trazer dados para uma pasta de trabalho utilizando recordsets. Você viu como utilizar o método CopyFromRecordset para adquirir dados em massa e viu como utilizar consultas e parâmetros para definir recordsets que podem ser gerenciados registro por registro. As consultas discutidas neste capítulo são consultas seleção. Seu único propósito é extrair registros e campos de tabelas e tornar os registros e campos disponíveis para você editar através de recordsets. Uma consulta seleção não faz alterações nos dados subjacentes, nem nas tabelas que contêm os dados. Há outra grande classe de consultas, chamadas consultas de ação. Essas consultas atuam sobre os dados, inserindo ou excluindo registros ou modificando-os. (Consultas de ação são chamadas de procedures armazenadas em SQL Server.) O capítulo final neste livro, “Controlando um banco de dados a partir do Excel com o ADO e DAO”, focaliza como mover dados na direção oposta daquela discutida neste capítulo. Isso é feito utilizando consultas de ação, DAO e o objeto ADO Command.
12 Controlando um banco de dados a partir do Excel com o ADO e o DAO Utilizando o DAO para executar consultas de ação As consultas de ação foram discutidas em algum detalhe no Capítulo 9, “Gerenciando objetos de banco de dados”, mas de um ponto de vista interativo. Lá, em “Criando consultas”, você viu como utilizar as consultas Atualização, Exclusão e Acréscimo para alterar dados, remover registros e adicioná-los às tabelas subjacentes. O contexto era de o usuário empregar as consultas diretamente a partir da interface com o usuário do banco de dados. Mas assim como pode combinar o VBA com recordsets DAO ou ADO para retornar dados via consultas Seleção, você pode fazer com que o VBA execute consultas de ação a partir da plataforma Excel e assim atualizar, excluir e acrescentar registros em um banco de dados. Você pode fazer isso com consultas que foram salvas no banco de dados ou pode submeter a SQL das consultas junto com os comandos para executá-las. A razão habitual de executar uma consulta de ação a partir do Excel é que o usuário fez algo em uma pasta de trabalho que exige que algo ocorra no banco de dados. Por exemplo, suponha que você queira substituir um conjunto de registros em uma tabela por novas informações. Particularmente se outros utilizarem essa tabela, você precisa ter cuidado em como fazer isso. Você não pode simplesmente excluir a tabela e reconstruí-la. Nenhum banco de dados digno do nome permitiria excluir uma tabela que outro usuário tivesse aberto, direta ou indiretamente por meio de uma consulta ou formulário. Você tem duas opções. Uma é comparar os dados antigos com os novos, registro por registro e campo por campo, atualizando quaisquer registros cujos dados tenham sido alterado. Uma abordagem muito mais simples e direta é simplesmente excluir todos os registros antigos da tabela e então substituí-los por novos registros. A melhor maneira de fazer isso normalmente é colocar os novos registros em uma tabela de banco de dados temporária e executar uma consulta Exclusão para remover os registros antigos da tabela permanente. Por fim, você executa uma consulta Acréscimo para mover os novos registros na tabela permanente da tabela temporária.
ATENÇÃO Um banco de dados permitirá a exclusão de registros de uma tabela aberta, a menos que passos específicos tenham sido realizados para impedir essa ação.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
313
Utilizando o DAO para executar uma consulta existente Eis um exemplo de como o DAO pode ser utilizado para executar uma consulta Exclusão. Nesse caso, a consulta já existe no banco de dados e aceita um parâmetro em seu campo ReservationDate. A Figura 12.1 mostra a consulta no modo de Design. Figura 12.1 Se executar a consulta interativamente, você será solicitado a fornecer um valor de data. Nenhuma solicitação aparece se você executá-la a partir do código.
Você colocaria um código semelhante ao seguinte em um módulo VBE do Excel e estabeleceria uma referência a uma biblioteca DAO selecionando Referências a partir do menu Ferramentas do VBE. O código passa um valor ao parâmetro da consulta Exclusão. Ao executar a consulta, o código exclui todos os registros com valores em ReservationDate anteriores a 1º de janeiro de 2004. Sub DeleteOldRecords(FilePath As String) Dim dbReservations As DAO.Database Dim qdfDeleteRecords As DAO.QueryDef Set dbReservations = OpenDatabase("I:\RM_RES\Resources.mdb") Set qdfDeleteRecords = dbReservations.QueryDefs("DeleteOldReservations") With qdfDeleteRecords .Parameters("WhichDate") = DateValue("1/1/2004") .Execute End With Set qdfDeleteRecords = Nothing Set dbReservations = Nothing End Sub
Observe o uso da função DateValue do VBA para converter o valor de parâmetro de um valor de texto que especifica uma data em um valor de data verdadeiro. Isso não é requerido. A instrução poderia ser esta: .Parameters("WhichDate") = "1/1/2004"
porque o Access restringirá o valor do parâmetro ao tipo utilizado pelo campo de critério, ReservationDate. Você também poderia utilizar esta: .Parameters("WhichDate") = #1/1/2004#
porque o Access reconhece um valor cercado por sinais de cerquilha (#) como uma data.
314
Gerenciando dados com o Microsoft Excel
Utilizando o DAO para definir e executar uma consulta não-persistente Às vezes você quer utilizar uma consulta que não existe no banco de dados. Isso pode ocorrer quando o usuário realiza uma ação que só ocorre ocasionalmente — por exemplo, limpar periodicamente os registros de uma tabela porque não são mais necessários. Em um caso como esse, você poderia preferir criar a consulta no código VBA, em vez de armazenar uma consulta no banco de dados que é raramente necessária. Uma das fontes mais geradoras de confusão em um banco de dados é a tendência dos usuários de criarem consultas que são utilizadas apenas uma vez ou uma vez ao ano. Você acaba com uma enorme lista de consultas com nomes como Consulta do Carlos e Consulta de Teste e Consulta de Jane de Fev 2004 e Consulta1 e Consulta2 e assim por diante. Você não faz nenhuma idéia, ao examinar os nomes das consultas, para que servem e torna-se muito difícil localizar consultas úteis. Então você às vezes cria novas consultas no código, em vez de depender da existência de uma consulta no banco de dados. (Se as coisas se tornaram muito ingerenciáveis, alguém poderia ter excluído todas as consultas existentes impacientemente.) Para fazer isso, você precisa fornecer a SQL no código, como no seguinte exemplo: Sub DeleteOldRecordsWithString1(FilePath As String, _ FirstDate As String) Dim dbReservations As DAO.Database Dim qdfTempQuery As DAO.QueryDef Dim strQuery As String strQuery = "DELETE Reservations.*, " & _ "Reservations.ReservationDate " & _ "FROM Reservations " & _ "WHERE ((Reservations.ReservationDate) < " & _ FirstDate & ");" Set dbReservations = OpenDatabase(FilePath & "Resources.mdb") Set qdfTempQuery = dbReservations.CreateQueryDef("", strQuery) qdfTempQuery.Execute End Sub
Há vários pontos a observar nesse código: • A SQL necessária para executar a consulta é fornecida no código. Nesse exemplo, ela é armazenada na variável de string strQuery. • Ela utiliza o método CreateQueryDef do objeto de banco de dados para criar uma nova consulta. Esse método aceita dois argumentos: o nome da consulta e sua SQL. • A consulta recebe um nome nulo, indicado pelas aspas vazias. Isso evita que a consulta seja salva no banco de dados. Só dura enquanto o código estiver executando. O objeto é não persistente. • A procedure que chama a sub-rotina deve passar o valor de FirstDate entre os sinais de cerquilha: por exemplo, #1/1/2004#. Isso assegura que o valor será interpretado como uma data.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
315
Especificando parâmetros em SQL Não é necessário declarar parâmetros da consulta em SQL, mas não é uma má idéia se para fins de documentação. Eis um exemplo: Sub Dim Dim Dim
DeleteOldRecords(FilePath As String) dbReservations As DAO.Database qdfDeleteRecs As DAO.QueryDef strQuery As String
strQuery = "PARAMETERS [WhichDate] Date;" & _ "DELETE Reservations.*, Reservations.ReservationDate " & _ "FROM Reservations " & _ "WHERE (((Reservations.ReservationDate)<[WhichDate]));" Set dbReservations = OpenDatabase(FilePath & "Resources.mdb") Set qdfDeleteRecs = dbReservations.CreateQueryDef _ ("DeleteOldRecs", strQuery) With qdfDeleteRecs .Parameters("WhichDate") = #1/1/2004# .Execute End With End Sub
Observe que a primeira cláusula na SQL nomeia o parâmetro e especifica seu tipo como Date. Observe também que essa consulta é salva no banco de dados, com o nome DeleteOldRecs. Haveria um pequeno problema em criar uma consulta não-persistente — uma pesquisa que não é salva no banco de dados — que aceita um ou mais parâmetros. O raciocínio por trás dos parâmetros de consulta é que eles permitem que os usuários executem a consulta repetidamente, com valores diferentes fornecidos ao parâmetro. Por exemplo, no início de 2005, você poderia passar 1/1/2005 ao parâmetro WhichDate, indicando que todos os registros com um valor antes dessa data sejam excluídos. No início de 2006, você poderia executar a mesma consulta, passando 1/1/2006 ao parâmetro WhichDate. O problema é que depois de ter estabelecido a consulta, você pode executá-la repetidamente, com valores de parâmetro diferentes para produzir resultados diferentes. Se a consulta for não-persistente, porém, ela não será salva e, portanto, ela não será colocada no banco de dados, pacientemente esperando você executá-la novamente com um valor de parâmetro diferente. Então, embora seja certamente válido criar uma consulta temporária e não-persistente que tenha um parâmetro, há um pequeno problema em fazer isso. Em vez disso, você poderia apenas dispensar o parâmetro e colocar o valor de critério diretamente na cláusula WHERE, como mostrado aqui: "WHERE ((Reservations.ReservationDate) < #1/1/2004);"
e aqui: "WHERE ((Reservations.ReservationDate) < FirstDate);"
316
Gerenciando dados com o Microsoft Excel
Utilizando o DAO para criar e executar uma consulta Acréscimo Talvez a maneira mais rápida de mover dados de uma planilha do Excel para uma tabela do Access seja por meio de uma consulta Acréscimo escrita em SQL. Sendo todas as outras coisas iguais, o SQL fornece a utilização mais eficiente e rápida de recursos do sistema para manipulação de dados. Ao trabalhar diretamente no Access e projetar um nova consulta Acréscimo, o Access quer conhecer a tabela que contém os dados originais. A suposição é a de que você tem dados em uma tabela chamada, digamos, RecordsFrom2004, e que quer acresecentar seu conteúdo à tabela chamada, digamos, AllRecords . Isso não ajuda muito se você tiver dados em uma planilha do Excel e quiser acrescentá-los a uma tabela do Access. Para isso você precisa encontrar outro caminho. Os recordsets DAO (e ADO) são um método excelente, mas não tão eficientes quanto SQL. Quando não estiver fazendo nenhum ajuste fino, quando tudo que você quer fazer é acumular os dados da planilha em uma tabela de banco de dados do modo mais rápido e eficiente possível, você vai querer utilizar a SQL. A solução é estabelecer uma consulta Acréscimo temporária e incluir os valores desejados na SQL da consulta. Para fazer isso, você precisa utilizar uma versão da sintaxe da consulta Acréscimo que,em geral, o Access não gera.
ESTUDO DE CASO Como diretor de um departamento médico de empregados, uma de suas responsabilidades é certificar-se de que a data em que um empregado tem um exame médico fique registrada no banco de dados Employees do RH. Você arranja as coisas de modo que a recepcionista registre as informações necessárias em uma planilha do Excel depois que o exame médico foi completado. Essas informações incluem o número ID único do empregado, bem como a data e hora em que o exame médico foi completado (veja a Figura 12.2).
Figura 12.2 Como os registros entram no banco de dados sem tratamento especial, uma consulta de ação trata essa situação eficientemente.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
317
Você utiliza o evento BeforeClose da pasta de trabalho para executar o seguinte código quando a recepcionista fechar a pasta ao fim de cada dia útil. Private Sub Workbook_BeforeClose(Cancel As Boolean) Dim dbEmpPhys As DAO.Database Dim qdfTempQuery As DAO.QueryDef Dim strQuery As String Dim i As Integer Dim EmpID As String, PhysDate As Date, PhysTime As Date Dim LastEmployee As Long Set dbEmpPhys = OpenDatabase _ (ThisWorkbook.Path & "\Employees.mdb") Set qdfTempQuery = dbEmpPhys.CreateQueryDef("")
Depois de declarar as variáveis necessárias, seu código configura o banco de dados e, em seguida, configura uma consulta nova e temporária (observe a string vazia dada como seu nome). LastEmployee = ThisWorkbook.Sheets("Appointments") _ .Cells(65536,1).End(xlUp).Row
O número de registros de empregado a ser processado é obtido subindo da última linha da coluna A ao valor mais inferior nessa coluna e atribuindo seu número de linha a LastEmployee. Um loop agora inicia, começando com 2 (porque os dados reais começam na linha 2 da planilha) e finalizando com LastEmployee. Os dados nas colunas A, B e C da linha atual do loop são atribuídos às variáveis EmpID, PhysData e PhysTime. For i = 2 To LastEmployee EmpID = ThisWorkbook.Sheets _ ("Appointments").Cells(i, 1) PhysDate = ThisWorkbook.Sheets _ ("Appointments").Cells(i, 2) PhysTime = ThisWorkbook.Sheets _ ("Appointments").Cells(i, 3)
Agora a SQL da consulta é construída (na primeira passagem pelo loop) e reconstruída (em ciclos subseqüentes) utilizando os valores atuais do ID de empregado e a data e hora do exame médico. A consulta insere um novo registro na tabela chamada Physicals e especifica os campos StaffID, DateOfPhysical e TimeOfPhysical. A cláusula VALUES da SQL fornece os valores que são inseridos nesses campos. strQuery = "INSERT INTO Physicals" & _ "(StaffID, DateOfPhysical, TimeOfPhysical) " & _ "VALUES ('" & EmpID & "', '" & PhysDate & "', '" & _ PhysTime & "');"
Para um registro no qual o ID do empregado é 314, a data é 2/5/2005 e o horário é 10:00 AM, o valor que é atribuído a strQuery é INSERT INTO Physicals (StaffID, DateOfPhysical, TimeOfPhysical) _ VALUES ('314', '2/5/2005', '10:00 AM');
Então, toda vez que o loop executa, valores diferentes são fornecidos à SQL. A própria consulta não-persistente já foi configurada e somente sua SQL altera. A alteração de sua SQL é realizada no seguinte bloco With, que também faz com que a consulta execute:
318
Gerenciando dados com o Microsoft Excel
With qdfTempQuery .Sql = strQuery .Execute End With
Next i ThisWorkbook.Sheets("Appointments") _ .Range(Cells(2, 1), Cells(LastEmployee,3)).Clear End Sub
Depois da última viagem pelo loop, o código limpa o intervalo que continha os dados em preparação para os compromissos do próximo dia, e a sub-rotina termina.
Utilizando DAO para definir e executar uma consulta Atualização Quando precisar modificar muitos registros em uma tabela, alterando valores de um campo na tabela, uma consulta Atualização com freqüência é a melhor escolha. Suponha que, seguindo o exemplo introduzido no estudo de caso anterior, você queira configurar a data do próximo exame médico para todos os empregados como 30 de junho de 2005. Você poderia utilizar um código como este: Sub Dim Dim Dim
UpdatePhysicalDates(FilePath As String) dbEmployees As DAO.Database qdfUpdateDates As DAO.QueryDef strUpdateSQL As String
Depois de declarar as variáveis necessárias, seu código configura o banco de dados. Set dbEmployees = OpenDatabase(FilePath & "Employees.mdb")
Então você atribui a SQL apropriada a uma variável de string. Nesse caso, sua SQL indica que todos os registros na tabela Physicals têm o valor do campo NextPhysical configurado como 6/30/2005. strUpdateSQL = "UPDATE Physicals SET " & _ "Physicals.NextPhysical = #6/30/2005#;"
Você então configura a definição da consulta. Você a torna não-persistente nomeando-a com uma string vazia e passa sua SQL para a consulta na forma da variável alfanumérica strUpdateSQL. Set qdfUpdateDates = dbEmployees.CreateQueryDef _ ("", strUpdateSQL)
Por fim, você executa a consulta e termina a sub-rotina. qdfUpdateDates.Execute End Sub
Isto, naturalmente, não é realista — você não agendaria o próximo exame médico de todos os empregados no mesmo dia — mas serve para mostrar como é possível atribuir uma constante a todos os registros em uma tabela.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
319
Uma abordagem mais realista seria atribuir a todos os empregados uma data para seu próximo exame médico que caísse no ano seguinte ao seu exame médico atual. Você poderia utilizar exatamente o mesmo código mostrado anteriormente, mas modificaria a SQL: strUpdateSQL = "UPDATE Physicals SET " & _ "Physicals.NextPhysical = [DateOfPhysical]+365;"
Agora a SQL instrui o banco de dados a adicionar 365 ao valor do campo chamado DateOfPhysical e armazenar o resultado no campo chamado NextPhysical. Como nenhum critério é aplicado, a atualização é levada a todos os registros na tabela.
Utilizando o ADO para executar consultas de ação e procedures armazenadas Se preferir utilizar ADO ao DAO (ou se não estiver utilizando um banco de dados Jet de modo que o DAO não esteja disponível), você precisará ajustar o código VBA para utilizar objetos ADO. Os exemplos nesta seção utilizam o objeto Command do ADO que, junto com os objetos Connection e Recordset, é um dos três objetos fundamentais no modelo do ADO. Normalmente, você utiliza o objeto Command quando o código for executar uma consulta de ação — em termos gerais, uma procedure armazenada.
A T O N
O ADO tende a seguir a terminologia de SQL Server nessa área, e o SQL Server utiliza o termo procedure armazenada em vez de consulta de ação. Mas os dois termos não são sinônimos: a SQL que o Access poderia denominar como consultas de ação constitui parte das procedures armazenadas, mas não todas elas.
Utilizando o ADO para executar consultas Exclusão O código VBA a seguir, utilizado em conjunção com o ADO, parece um pouco diferente daquele utilizado com o DAO. Eis uma maneira de utilizar o VBA e o ADO para excluir registros com um valor do campo de data que é anterior a 1/1/1997. Sub Dim Dim Dim
DeleteOldOrders() cmd As New ADODB.Command cnn As New ADODB.Connection prm As ADODB.Parameter
Três variáveis de objeto de ADO são declaradas: cmd representará o comando a ser executado, cnn representará a conexão ao banco de dados, e prm representará um parâmetro que acompanha o comando. Então a conexão é definida especificando o provedor, a fonte de dados e o catálogo, e como a segurança é tratada. A especificação SSPI, abreviação de Security Support Providers Interface , indica a autenticação de usuário NT: o NT autoriza o usuário com base em seu logon. cnn.Open _ ConnectionString:="Provider=SQLOLEDB.1;" & _ "Data Source=(local);" & _ "Initial Catalog=NorthwindCS;Integrated Security=SSPI"
320
Gerenciando dados com o Microsoft Excel
Depois que a conexão é aberta, ela é atribuída ao objeto Command. Set cmd.ActiveConnection = cnn
Então o código fornece as especificações do comando. Sua propriedade CommandText, uma string, nomeia a consulta ou procedure armazenada existente que o comando deve representar, ou contém a SQL que o comando executará. A propriedade CommandType especifica o que C o m m a n d T e x t contém. No exemplo presente, é uma procedure armazenada chamada DeleteOldOrders. Se, em vez disso, a propriedade CommandText incluísse a SQL real, a propriedade CommandType especificaria adCmdText — isto é, indicaria que CommandText incluiu texto de comando. With cmd .CommandText = "DeleteOldOrders" .CommandType = adCmdStoredProc
Agora um parâmetro é criado. Ele recebe o nome WhichDate e um tipo de dados de adDate, que significa que um valor atribuído a ele deve ser um valor de data. A propriedade Direction talvez seja nova para você. No DAO e em SQL do Access, os parâmetros são unidirecionais. Eles são introduzidos à consulta e funcionam como critério de seleção. No ADO (e Transact-SQL e SQL Server), os parâmetros podem atuar da maneira como atuam no DAO, como entradas, mas também podem atuar como saídas, retornando informações sobre o que aconteceu quando a consulta ou outra procedure armazenada executou. Nesse caso, o parâmetro é do tipo de entrada familiar, conforme especificado pelo valor adParamInput. Set prm = .CreateParameter(Name:="WhichDate", _ Type:=adDate, Direction:=adParamInput)
O parâmetro é acrescentado ao comando e permanece disponível a este enquanto o próprio comando existir. O código dá ao parâmetro um valor — nesse caso, 1/1/97 — e o comando executa, excluindo todos os registros de acordo com os requisitos da consulta DeleteOldOrders existente. .Parameters.Append prm prm.Value = "1/1/97" .Execute End With End Sub
Não é necessário estabelecer e acrescentar formalmente um parâmetro ao objeto de comando para utilizar um (mas consulte a próxima seção para um exemplo de como fazer isso pode tornar as coisas mais convenientes). O exemplo anterior de exclusão de registros utilizando um parâmetro de data poderia ser escrito da seguinte maneira: Sub Dim Dim Dim Dim
DeleteOldOrdersWithSQL() cmd As New ADODB.Command cnn As New ADODB.Connection prm As ADODB.Parameter HowMany As Long
cnn.Open _ ConnectionString:="Provider=SQLOLEDB.1;" & _ "Data Source=(local);" & _ "Initial Catalog=NorthwindCS;Integrated Security=SSPI"
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
321
Set cmd.ActiveConnection = cnn With cmd .CommandText = "EXEC DeleteOldOrders '1/1/97'" .CommandType = adCmdText .Execute RecordsAffected:=HowMany End With MsgBox HowMany & " records were deleted." End Sub
Esse exemplo mostra que você não precisa declarar nem configurar um parâmetro para utilizar um que já existe em uma procedure armazenada. Há três diferenças funcionais entre as duas procedures dadas nesta seção: • A propriedade CommandText do objeto de comando não nomeia meramente a procedure armazenada, mas fornece a SQL que nomeia a consulta e também as condições que deveriam ser executadas. • A propriedade CommandText inclui o valor que o VBA passará ao parâmetro da procedure armazenada. • A propriedade CommandType declara que o tipo do comando é Text — isto é, o comando inclui sintaxe SQL real. Outra diferença é que a segunda procedure mostra o uso da propriedade RecordsAffected do método Execute. Pode ser útil saber quantos registros foram excluídos (ou, no caso de uma consulta de ação Acréscimo ou Atualização, quantos registros foram inseridos em uma tabela ou quantos tiveram seus valores modificados). Declarando uma variável como um inteiro Longo e utilizandoa no método Execute da consulta, você pode visualizar com uma caixa de mensagem ou armazenar em uma célula de planilha, o número de registros afetados. Se o número for zero, por exemplo, você poderia saber que algo que você esperava ocorrer não ocorreu. E se precisar de uma pista de auditoria das alterações que seu código fez ao banco de dados, pode ser valioso saber, enquanto avança, o número de registros que foram afetados pelo seu código.
Utilizando o ADO para executar consultas Atualização Observe o uso do objeto de parâmetro no código VBA anterior. O parâmetro utilizado, prm, é estabelecido com a instrução Set. Depois ele é acrescentado à coleção Parameters, que pertence ao objeto Command (cmd no código de exemplo). Isso é útil quando você quiser executar uma consulta várias vezes, toda vez com um valor diferente para o parâmetro. “Consultando múltiplas tabelas”, no Capítulo 5, “Utilizando o Microsoft Query”, discutiu a configuração de um banco de dados de informações sobre a manutenção de portas em prédios de escritório. O próximo estudo de caso mostra como os dados poderiam entrar nesse banco de dados do Excel.
322
Gerenciando dados com o Microsoft Excel
ESTUDO DE CASO A sua equipe de técnicos de manutenção inspeciona as portas de fogo nos prédios de escritório da sua empresa em uma agenda regular. No fim de cada dia de trabalho, você quer que os técnicos registrem o ID de cada porta que eles inspecionaram naquele dia. Há outras informações que os técnicos devem registrar e a localização mais conveniente para registrar todas as informações do dia é em uma planilha do Excel, mostrada na Figura 12.3. Figura 12.3 O Excel presta-se bem a esse tipo de layout de entrada de dados informal.
Você quer mover a data de inspeção das portas da planilha para um projeto do Access. Não é necessário nenhum tipo de tratamento especial que possa exigir a utilização de um recordset. Tudo que você precisa fazer é localizar cada porta inspecionada no banco de dados e atualizar seu campo DateLastInspected. Você decide utilizar uma procedure armazenada, uma que o Access poderia denominar como uma consulta Atualização. A consulta é mostrada no modo de Design na Figura 12.4. Figura 12.4 O funil na tabela, em DoorID, indica que você aplicou um critério àquele campo.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
A T O N
323
A construção de procedures armazenadas é, sob alguns aspectos, diferente da construção de consultas de ação, ainda que elas muitas vezes atinjam os mesmos objetivos. Observe na Figura 12.4, por exemplo, que o parâmetro não é identificado como tal incluindo-o em colchetes, mas precedendo-o com um “e” comercial. Em uma consulta de ação do Access, o nome do parâmetro poderia ter um espaço em branco incorporado, mas você evita isso em uma procedure armazen ada. Observe também o uso da função GETDATE() em vez da função DATE(). Essas diferenças são um resultado direto do uso de Transact-SQL em vez da versão de SQL utilizada pelos arquivos MDB do Access.
Depois que os técnicos terminaram de registrar os IDs de porta, eles fecham a pasta de trabalho. Você configurou o evento BeforeClose da pasta de trabalho para executar o seguinte código antes de a pasta de trabalho fechar: Option Explicit Private Sub Workbook_BeforeClose(Cancel As Boolean) Dim cmd As New ADODB.Command Dim cnn As New ADODB.Connection Dim prm As ADODB.Parameter Dim i As Integer Dim LastDoor As Long LastDoor = ThisWorkbook.Sheets("Doors Inspected") _ .Cells(65536, 1).End(xlUp).Row
Você configurou a variável LastDoor como a linha em que a entrada final está localizada na planilha. Na Figura 12.3, essa é a linha19. Isso será o valor de contador final utilizado enquanto o código faz loops pelos IDs de porta. cnn.Open _ ConnectionString:="Provider=SQLOLEDB.1;" & _ "Data Source=Fran;" & _ "Initial Catalog=FireDoors;Integrated Security=SSPI" Set cmd.ActiveConnection = cnn
Você define uma conexão por meio de uma string, especificando o provedor, o servidor (a fonte de dados), o catálogo e a segurança. Você abre a conexão e a atribui a um objeto Command. With cmd .CommandText = "RecordLastInspectDate" .CommandType = adCmdStoredProc
Você utiliza a propriedade CommandText do objeto Command para identificar a procedure armazenada a ser executada, RecordLastInspectDate . Você também indica que o objeto Command representa uma procedure armazenada por meio de acCmdStoredProc. Set prm = .CreateParameter(Name:="WhichID", _ Type:=adVarChar, Size:=10) .Parameters.Append prm
Segue utilizando os IDs de porta localizados na planilha como valores de parâmetro: Isto é, você os submeterá à consulta um por um no loop seguinte. A cada passagem pelo loop, a consulta executa depois que localizar o registro que é identificado pelo valor do parâmetro.
324
Gerenciando dados com o Microsoft Excel
Ao configurar prm como um novo parâmetro,você fornece seu nome, que é idêntico àquele nome na procedure armazenada. A especificação adVarChar significa que o valor do parâmetro é um tipo de dados de comprimento variável, aproximadamente equivalente a um valor Text. O parâmetro Size configura o comprimento máximo do valor como 10 caracteres. Depois de configurar as propriedades do parâmetro, você o acrescenta à coleção de parâmetros que pertence a esse objeto Command.Nesse caso, há somente um parâmetro na coleção, mas ele ainda deve ser acrescentado.Com esse parâmetro na coleção você pode utilizá-lo repetidamente sem ter de especificar outra vez suas propriedades. (Uso repetido é a principal razão em estabelecer o parâmetro dessa maneira). For i = 2 To LastDoor prm.Value = ThisWorkbook.Sheets("Doors Inspected") _ .Cells(i, 1) .Execute Next i End With End Sub
Seu código entra no loop que executa por cada ID de porta localizado na planilha. A cada passagem pelo loop, o valor do parâmetro é configurado como igual ao ID de uma porta diferente e a procedure armazenada é executada. A procedure armazenada localiza a porta em sua tabela e configura seu campo LastInspected como data atual.
Utilizando recordsets DAO para mover dados do Excel para um banco de dados Jet Até agora, este livro só abordou muito superficialmente o tópico de utilização de recordsets para armazenar dados em um banco de dados. Há métodos mais eficientes, discutidos nas seções anteriores deste capítulo bem como nos capítulos anteriores. Quando puder utilizar uma consulta Seleção para retornar campos e registros de um banco de dados, faça isso sem dúvidas. Quando puder utilizar uma consulta de ação existente ou criar uma temporariamente, para modificar registros e campos, esse normalmente é o método a utilizar. Consultas baseadas em SQL fazem uso mais eficiente dos recursos do sistema do que os recordsets. Consultas Acréscimo são boas quando se quer adicionar um grupo de registros a uma tabela de banco de dados sem grande quantidade de processamento intermediário. Consultas Exclusão funcionam bem quando se quer remover registros, ou um grupo de registros facilmente identificável, de uma tabela. As consultas Atualização são uma boa escolha quando se quer executar a mesma ação em alguns ou todos os registros em uma tabela. Mas quando precisar que seu código exami ne cada registro individualmente e talvez realize uma ação condicionalmente, é provavel que você precise utilizar um recordset. Suponha que você queira colocar novos registros em uma tabela de banco de dados ou alterar o valor de um ou mais campos em um registro existente. Essas duas ações muito comuns indicam o método AddNew do recordset e seu método Edit; noDAO, esses dois métodos requerem que você também utilize o método Update do recordset. As próximas duas seções mostram exemplos desses métodos.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
325
Adicionando dados a um recordset Há muitas razões pelas quais você desejaria adicionar dados a um banco de dados da plataforma Excel. Uma das razões mais importantes é que embora você ou seus usuários prefiram inserir dados diretamente no Excel, um banco de dados faz muito mais sentido que uma planilha para armazenamento de longo prazo. A outra razão de importância é que você quer utilizar uma ou mais funções de planilha do Excel antes de armazenar os dados em outra parte. Suponha que esteja monitorando o desempenho de fundos em uma conta de aposentadoria. Você acha útil traçar seus valores diários por ações contra algum índice de grande abrangência como o S&P 500 ou o NASDAQ e incluir linhas de tendência nos gráficos. Essa é uma tarefa relativamente básica no Excel, mas desajeitada na melhor das hipóteses na maioria das aplicações de banco de dados. Então, você decide realizar sua análise no Excel e de vez em quando colocar os dados de fundos de ações em um banco de dados do Access. Você poderia automatizar esse processo com o seguinte código: Sub AddStocks() Dim Dim Dim Dim Dim
dbStocks As DAO.Database rs401k As DAO.Recordset wksStocks As WorkSheet i As Integer FinalRow As Long
Set dbStocks = OpenDatabas(ThisWorkbook.Path & _ "\Stocks.mdb") Set rs401k = dbStocks.TableDefs("Retirement") _ .OpenRecordset(dbOpenDynaset) Set wksStocks = ThisWorkbook.Worksheets("2004") FinalRow = wksStocks.Cells(65536,1).End(xlUp).Row With rs401k For i = 2 to FinalRow .AddNew .Fields("StockName") = wksStocks.Cells(i,1) .Fields("PriceDate") = wksStocks.Cells(i,2) .Fields("Price") = wksStocks.Cells(i,3) .Update Next i End With End Sub
A procedure chamada AddStocks começa abrindo um banco de dados do DAO chamado Stocks.mdb. Ela estabelece um recordset baseado em uma tabela chamada Retirement, localizada em Stocks.mdb. Utiliza um loop For-Next para adicionar novos registros ao recordset e assim à tabela que o recordset representa. Toda vez que um novo registro é adicionado por meio do método AddNew, três campos nesse novo registro são preenchidos com valores localizados nas colunas A, B e C da planilha nomeada 2004. O nome do fundo é obtido da coluna A; a data em que o valor da ação foi consultado, a partir da coluna B; e o preço, a partir da coluna C. Depois que os três campos obtêm seus valores, o método Update é chamado. Ao utilizar o método AddNew (ou, como mostrado em seguida, o método Edit), um registro é colocado em um buffer e as alterações são feitas no registro no buffer. Somente ao chamar o método Update, o registro e as alterações feitas são movidos do buffer para fonte de dados subjacente.
326
Gerenciando dados com o Microsoft Excel
Depois que o registro atual tiver sido atualizado, o loop continua, estabelecendo um novo registro, preenchendo seus campos e movendo o registro do buffer de cópia na tabela Retirement.
Editando dados existentes Quando já tiver todos os registros necessários disponíveis e a única tarefa for editar os valores em seus campos, considere utilizar uma consulta Atualização. Suponha que você venda 100 cotas de cada participação em sua conta de aposentadoria. Como está vendendo o mesmo número de cotas independentemente da participação, uma consulta Atualização faz sentido. Você poderia fornecer a SQL ao longo destas linhas: strSellShares = "UPDATE Retirement SET " & _ "Retirement.SharesHeld = Retirement.SharesHeld – 100;"
Mas se você quisesse registrar que vendeu um número diferente de cotas em cada fundo, uma consulta Atualização poderia não fazer sentido e talvez processar um recordset talvez fosse muito mais atraente. Suponha que sua planilha tem o nome de cada fundo em sua conta de aposentadoria na coluna A e o número de cotas que você vendeu na coluna B. Então o código poderia ser assim: Sub SellStocks() Dim Dim Dim Dim Dim
dbStocks As DAO.Database rs401k As DAO.Recordset wksStocks As Worksheet i As Integer FinalRow As Long
Set dbStocks = OpenDatabase(ThisWorkbook.Path & _ "\Stocks.mdb") Set rs401k = dbStocks.TableDefs("Retirement") _ .OpenRecordset(dbOpenDynaset) Set wksStocks = ThisWorkbook.Worksheets("2004") FinalRow = wksStocks.Cells(65536,1).End(xlUp).Row With rs401k For i = 2 To FinalRow .FindFirst "StockName = '" _ & wksStocks.Cells(i, 1) & "'" If Not .NoMatch Then .Edit .Fields("SharesHeld") = _ .Fields("SharesHeld") _ – wksStocks.Cells(i, 2) .Update Else wksStocks.Cells(i,3) = _ "Could not find this stock in database." End If Next i End With End Sub
A procedure chamada SellStocks difere de AddStocks de duas maneiras fundamentais. Quando os fundos estavam sendo adicionados ao banco de dados, não era necessário localizar um fundo em particular: o código simplesmente adiciona o nome, data e preço de cada fundo à tabela.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
327
Mas SellStocks edita os registros existentes e (como é típico quando você estiver editando dados existentes) tem de começar localizando o registro a ser editado. Então, a primeira diferença importante entre as duas procedures é que SellStocks utiliza o método FindFirst para localizar cada fundo. Ela obtém o nome do fundo a partir da planilha e localiza seu registro no banco de dados. Depois que o registro tiver sido localizado, ela edita o registro subtraindo um número, também tomado da planilha, do campo SharesHeld. A sintaxe de FindFirst é Recordset.FindFirst Critério
onde Critério é uma string que consiste em um nome de campo, um operador e um valor a localizar. Por exemplo: strCriterion = "SharesHeld = '" & _wksStocks.Cells(i, 1) & "'" rs401k.FindFirst strCriterion
Então, para utilizá-lo, simplesmente nomeie o recordset que você quer pesquisar, seguido por FindFirst. Forneça um nome de campo, um operador e o valor que você quer localizar. Você não precisa utilizar o sinal de igual como o operador: também pode utilizar menor que, maior que, não igual a e assim por diante. A outra diferença importante entre as duas procedures é que SellStocks tem de fazer uma provisão da possibilidade de que o nome de um fundo localizado na planilha não exista no banco de dados. Portanto a propriedade NoMatch é utilizada. NoMatch é uma
propriedade que pertence aos recordsets DAO. Ela é utilizada depois de um Find (em SellStocks, é utilizada depois de FindFirst). Se seu código for bem-sucedido em localizar um registro que satisfaça o critério, NoMatch é configurado como False. Se seu código não pode localizar tal registro, NoMatch é configurado como True. Você deve considerar o caso em que um registro não pode ser localizado. Na procedure SellStocks, o código escreve uma mensagem informando que o fundo não pôde ser localizado. Ele coloca essa mensagem na mesma linha que o nome do fundo, na terceira coluna.
A T O N
Há quatro métodos Find no DAO. FindFirst inicia no começo de um recordset e procura um registro correspondente em direção ao fim. FindLast inicia no fim e procura em direção ao início. FindPrevious procura do registro atual em direção ao começo de um recordset. FindNext procura a partir do registro atual em direção ao fim.
Utilizando ADO para mover dados a partir do Excel para um banco de dados Há algumas diferenças menos importantes entre DAO e ADO quando se trata de modificar dados. Esta seção examina como você poderia estruturar seu código utilizando ADO para realizar as duas tarefas discutidas na seção anterior, adicionar registros e editar registros existentes.
Utilizando ADO para adicionar registros Como antes, a intenção é varrer os dados em uma planilha, pegar os nomes e preços de ações e copiá-los para uma tabela de banco de dados.
328
Gerenciando dados com o Microsoft Excel
Naturalmente, as declarações na sub-rotina são um pouco diferentes no ADO. Uma nova conexão é estabelecida (o objeto cnn) e utilizada para abrir o recordset. Sub AddStocks() Dim Dim Dim Dim Dim
cnn As New ADODB.Connection rs401k As ADODB.Recordset wksStocks As Worksheet i As Integer FinalRow As Long
cnn.Open _ ConnectionString:="Provider=SQLOLEDB.1;" & _ "Data Source=(local);" & _ "Initial Catalog=NorthwindCS;Integrated Security=SSPI" Set rs401k = New ADODB.Recordset rs401k.Open "Retirement", cnn, adOpenStatic, _ adLockOptimistic, adCmdTable
A instrução anterior contém muitas funcionalidades: • Nomeia a fonte de dados para o recordset: a tabela Retirement, como antes. • Fornece o objeto conexão, cnn, de modo que o recordset pode localizar a fonte de dados. • Configura o cursor de recordset como adOpenStatic. Essa configuração significa que o recordset pode ser modificado e que você pode se mover para frente e para trás ao longo dele. Você não seria capaz de ver as alterações que outros usuários poderiam fazer. • E configura o bloqueio de registro como optimistic: seu código terá a capacidade de modificar os dados e quaisquer outros usuários seriam capazes de modificar o mesmo registro com o qual você está trabalhando. Quando suas modificações forem salvas, o registro é bloqueado até que o banco de dados tenha terminado de salvar o registro. • Informa o ADO, por meio da opção adCmdTable , de que a fonte, já especificada como Retirement, é uma tabela ou uma consulta Seleção. Set wksStocks = ThisWorkbook.Worksheets("2004") FinalRow = wksStocks.Cells(65536, 4).End(xlUp).Row With rs401k For i = 2 To FinalRow .AddNew .Fields("StockName") = wksStocks.Cells(i, 1) .Fields("PriceDate") = wksStocks.Cells(i, 2) .Fields("Price") = wksStocks.Cells(i, 3) .Update Next i End With End Sub
O código que realmente adiciona os registros parece idêntico ao código no DAO, mas há uma ligeira diferença. Utilizando o ADO, o método Update não é necessário. Se você omitir Update no DAO, seguindo um AddNew ou um Edit , o compilador irá gerar uma mensagem de erro.
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
329
Não é esse o caso no ADO, que trata Update como uma instrução opcional. Depois que um registro foi adicionado ou editado, o processo de atualização ocorre automaticamente quando você se move para um registro diferente. (Quando você adicionar um novo registro, ele se torna o registro atual e, portanto, você se moveu.) Se você não se mover para um registro diferente, as alterações feitas não serão salvas. Por exemplo, suponha que sua planilha tivesse dados sobre fundos nas linhas de 2 a 51 e que você omitisse o método Update no código. Para as linhas 2 a 50, o loop move você para um novo registro toda vez que ele executa, então a atualização automática ocorre e os dados são salvos corretamente. Entretanto, embora os dados na linha 51 fossem colocados em um novo registro, o registro não seria salvo porque o loop termina e não se move para um registro diferente. Portanto, a atualização automática não ocorre. Tudo isso é uma maneira longa de dizer que embora o compilador vá fazer o trabalho dele sem utilizar Update, você deve utilizá-lo de qualquer jeito.
Utilizando o ADO para editar registros Na seguinte versão da procedure SellStocks, as declarações e preparações são idênticas à da procedure AddStocks anterior utilizando o ADO em vez do DAO: Sub SellStocks() Dim Dim Dim Dim Dim
cnn As New ADODB.Connection rs401k As ADODB.Recordset wksStocks As Worksheet i As Integer FinalRow As Long
cnn.Open _ ConnectionString:="Provider=SQLOLEDB.1;Data Source=(local);" & _ "Initial Catalog=NorthwindCS;Integrated Security=SSPI" Set rs401k = New ADODB.Recordset rs401k.Open "Retirement", cnn, adOpenStatic, adLockOptimistic, adCmdTable Set wksStocks = ThisWorkbook.Worksheets("2004") FinalRow = wksStocks.Cells(65536, 1).End(xlUp).Row
Há diferenças mais óbvias no loop utilizando ADO quando registros existentes estão sendo editados. Uma é que em vez de um método FindFirst, o ADO tem um método Find . Sua sintaxe completa é Recordset.Find Critérios , SkipRows, SearchDirection, Start
Todos os argumentos além dos Critérios são opcionais. Forneça um valor numérico para SkipRows se quiser que a pesquisa pule um número de registros ao verificar uma correspondência no critério. Você pode especificar adSearchForward ou adSearchBackward para controlar a direção da pesquisa. E se fornecer um indicador, a pesquisa iniciará nesse registro em vez de no come ço do recordset. Uma limitação real ao uso de Find no ADO é que você não pode fornecer múltiplos critérios. Por exemplo, o seguinte Find resultaria em um erro de tempo de execução:
330
Gerenciando dados com o Microsoft Excel
Rst.Find "StockName = 'IBM' And StockPrice > 100"
Observe também que a falha na localização de um registro que corresponda ao critério não é testada por NoMatch. Em vez disso, a propriedade BOF ou EOF do recordset torna-se True. Qual se torna verdadeiro depende da direção em que você estava pesquisando, adSearchForward — uma falha resulta em um EOF True — ou adSearchBackward — uma falha resulta em um BOF True. (BOF significa Beginning of File — começo de arquivo — e EOF significa — End of File — fim de arquivo.) With rs401k For i = 2 To FinalRow .Find "StockName = '" _ & wksStocks.Cells(i, 1) & "'" If Not .EOF Then .Fields("SharesHeld") = _ .Fields("SharesHeld") _ - wksStocks.Cells(i, 2) Else wksStocks.Cells(i, 3) = _ "Could not find this stock in database." End If Next i End With End Sub
Embora você possa pesquisar somente um campo utilizando o método possível alternativa é seu método Filter. Isto seria válido:
Find do ADO, uma
Rst.Filter = "StockName = 'IBM' And StockPrice > 100"
e limitaria os registros no recordset àqueles com um valor de IBM em seu campo StockName e com um valor em StockPrice que é maior do que 100. Observe que o método Filter utiliza um sinal de igual, ao contrário do método Find. Se, depois de aplicar um filtro, você precisar ter todos os registros originais acessíveis, você pode restaurá-los com a constante adFilterNone: Rst.Filter = adFilterNone
Lembre-se de que se utilizar Filter, você pode ter vários registros acessíveis no recordset. Se quiser editar todos eles, é recomendável fazer um loop pelos registros restantes. Por exemplo, suponha que você queira reduzir suas participações na IBM por 100 cotas em cada conta, sem criar um número negativo de cotas em nenhuma conta (a venda a descoberto é muito arriscada para um portfólio de aposentadoria): With rs401k .Filter = "StockName = '" _ & wksStocks.Cells(i, 1) & _ "' And SharesHeld > 100" Do Until .EOF .Fields("SharesHeld") = _ .Fields("SharesHeld") - 100
Capítulo 12 – Controlando um banco de dados a partir do Excel com o ADO e o DAO
331
.MoveNext Loop
.Filter = adFilterNone End With
Olhando para frente Você alcançou o EOF. Este livro não pode oferecer mais nenhum conselho sobre a utilização da plataforma Excel para gerenciar seus dados. Posso esperar, porém, que você tenha achado úteis as informações. Fiz aqui um “backup” completo, incluindo neste livro tudo que eu achei valioso sobre a utilização do Excel, VBA, DAO, ADO, bancos de dados do Access e projetos do Access para monitorar dados. O Excel é uma aplicação muito poderosa, mas nem sempre foi assim. Houve um tempo que o Excel era considerado como a versão “júnior” do 1-2-3. Acredite ou não, houve um tempo em que a Microsoft foi criticada na mídia de computador pessoal por não oferecer uma aplicação de planilha que suportasse mais de uma planilha por arquivo. Mas a Microsoft continuou aprimorando o que foi uma vez conhecido como MultiPlan. O Excel 2004 tornou-se o padrão ouro de software de planilha do mercado. Você pode dizer isso porque as novas versões têm cada vez menos aprimoramentos de verdadeiro valor. O Access ainda não chegou lá. Nem o SQL Server. Quando este livro foi escrito, a atenção da Microsoft estava na correção de brechas em seus sistemas operacionais e dedicava relativamente pouca atenção a suas aplicações de usuário final. Mas cada versão do Access e do SQL Server aprimorou suas capacidades e existem todas as razões para se acreditar que eles seguirão o caminho estabelecido pelo Excel. É isso aí. Se continuar utilizando o Excel, para analisar seus dados, e sistemas de gerenciamento de banco de dados verdadeiramente relacionais, para armazená-los e recuperá-los, seu esforço será recompensado.
Página em b ranco
Índice Símbolos
AddComment, função, 290 ADO Extensions, 247 AddNew, função, 325 adOpenDynamic, tipo de cursor, AddNew, instrução, 268, 269 275 AddRecordsWithDAO(), função, adOpenForwardOnly, tipo de 267, 269 cursor, 275, 276 AddStocks(), função, 325, 328 adOpenKeyset, tipo de cursor, Adicionar tabelas, janela, 90 276 adLockBatchOptimistic, tipo de adOpenStatic, tipo de cursor, 276 A bloqueio, 276 Agrupamento, caixa de diálogo, adLockOptimistic, tipo de 141, 146 abrindo bloqueio, 276 agrupando bancos de dados do Access campos de data/hora, 140, 141, adLockPessimistic, tipo de protegidos, 215, 216 14 2 bloqueio, 276 conexões ADO, 188, 189, 194 Adminstrador, usuários, 191, 204 campos numéricos, 142, 143 função GetConnectionStrings, Agrupar e detalhar, comando restringindo, 209 195, 196 ADO (ActiveX Data Objects), (menu Dados), 141 função MakeTheConnection, 154, 155 Altura, argumento (função 195, 196 DESLOC), 27, 28, 29 ADO Extensions, 247 método Open com argumenaninhando bancos de dados, criando , (vírgula), 36 #VALOR!, erro, 64 ; (ponto-e-vírgula), 36 < (menor que), sinal, 93 > (maior que), sinal, 93 _ (sublinhado), 220, 222 {} (chaves), 28
tos, 195, 196 método Open sem argumentos, 194, 195 conexões DAO, 200
Abrir, eventos, 102, 103, 104, 105 ação, consulta. Consulte consultas Access consultas criando, 118, 119, 120, 122
Access, bancos de dados, 202, 211 abrindo, 215, 216 arquivos de informações de grupo de trabalho, 205, 206, 207 contas de usuário, criando, 208, 20 9 restrições de Administrador, 209 segurança em nível do usuário, 204, 205 senhas de banco de dados, 202, 203, 204 solicitações de senha, 207, 208 tornando seguro, 211, 213, 215
acomodando usuários, 158 acrescentando registros, 244 Acréscimo, consulta, comandos do menu, 244 Acréscimo, consultas, 107, 238, 239, 244
executando com DAO (Data Access Objects), 316, 317, 318
ActiveX Data Objects. ADO
Consulte
recordsets, 275, 276, 277 tabelas, 261, 262, 263, 264, 265, 266 conexões de banco de dados, 185 (objeto Connection), 189 abrindo, 188, 189, 194, 195, 19 6 arquivos UDL (universal data link), 190, 191, 192, 193, 19 4 biblioteca ADO, referenciando, 186, 187 declarando, 187 fontes de dados, especificando, 18 9 instanciando, 187, 188 palavra-chave New, 188 strings de conexão, 189, 190, 191, 192, 193, 195, 196 consultas, executando consultas Atualização, 321, 322, 323, 324 consultas Exclusão, 319, 320, 32 1 recordsets copiando para pastas de trabalho, 279 registros, adicionando a bancos de dados, 327, 328, 329 registros, editando, 329, 330, 331 versões, 156
instruções With, 175 loops, 170
Application.DisplayAlerts, comando, 104 ÁREAS, função, 24 Arquivo, comandos do menu Fechar e retornar ao Microsoft Excel, 103 Retornar dados ao Microsoft Excel, 91 Salvar, 244
Arquivo de informações do grupo de trabalho, caixa, 125, 207 arquivos arquivos de consulta, 87 arquivos de informações de grupo de trabalho, 205, 206, 207 arquivos DSN, 87, 88, 89 (data source name), 87 arquivos simples, 112 arquivos UDL (universal data link), 190, 192, 193, 194 filtrando tipos de arquivo, 200, 20 2 UDL (universal data link), 191
arquivos de informações de grupo de trabalho, 205, 206, 207 arquivos simples, 112 array, fórmulas, 12 arrays
334
Gerenciando dados com o Microsoft Excel
RoomArray, 284, 287 TimeArray, 284, 287 Assistente de consulta, 94, 95, 96, 98 janela Escolher colunas, 93 janela Ordem de classificação, 95 Assistente de segurança no nível do usuário, 211, 213, 215 Assistente de tabela dinâmica, 132, 134 importando dados, 99 assistentes Assistente de consulta, 94, 95, 96, 98 janela Escolher colunas, 93 janela Ordem de classificação, 95 Assistente de segurança no nível do usuário, 211, 213, 215 Assistente de tabela dinâmica, 132, 134 importando dados, 99 Ativar atualização em segundo plano, propriedade (, 125 Atualização, consulta, 107 Atualização, consultas, 235, 237 executando com ADO (ActiveX Data Objects), 321, 322, 323, 324 executando com DAO (Data Access Objects), 318, 319 atualizando intervalos de dados externos, 96 registros, 235, 237 atualizando cache, 100 Atualizar a cada x minutos, opção (tabelas dinâmic, 139, 256, 257 Atualizar ao abrir, opção, 102, 103, 104, 105 Atualizar ao abrir, opção (tabelas dinâmicas), 139 Atualizar dados em Abrir arquivo, propriedade (int, 125 Atualizar dados externos, comando (menu Dados), 99, 124, 126, 127, 137, 140 AutoConclusão, recurso, 17 AutoCorreção de nome, recurso, 220 AutoFiltro, 70, 71, 72, 74 outros critérios, 72, 73, 74 Personalizar AutoFiltro, 72, 73, 74 seta de filtro, 71 AutoFiltro, comando (menu Filtro), 71
B
B, árvore, 229 Ballou Realty, estudo de caso consultas de múltiplas tabelas, 108, 109, 111 bancos de dados, 185. Consulte também consultas; reservas, aplicação bancos de dados Jet, 202, 211 abrindo, 215, 216 arquivos de informações de grupo de trabalho, 205, 206, 207 contas de usuário, criando, 208, 209 restrições de Administrador, 20 9 segurança em nível do usuário, 204, 205 senhas de banco de dados do Access, 202, 203, 204 solicitações de senha, 207, 208 tornando seguro, 211, 213, 21 5 campos, 219 configurando tipo de, 223, 224 definindo, 254, 255, 256 nomes, 219, 220, 221, 222 número de, 253 chave primária, 115 conexões ADO, 185 abrindo, 188, 189, 194, 195, 19 6 arquivos UDL (universal data link), 191, 192, 193, 194 biblioteca ADO, referenciando, 186, 187 declarando, 187 fontes de dados, especificando, 18 9 instanciando, 187, 188 palavra-chave New, 188 registros, adicionando, 327, 328, 329 registros, editando, 329, 330, 33 1 strings de conexão, 189, 190, 191, 192, 193, 195, 196 UDL (universal data link) arquivos, 190 conexões DAO, 197, 324 abrindo, 200 biblioteca de DAO, referenciando, 197, 198 declarando, 198 localizando bancos de dados, 198, 199
método GetOpenFilename, 198, 199, 200, 201, 202 método OpenDatabase, 200 registros, adicionando, 325, 32 6 registros, editando, 326, 327 tipos de arquivo, filtrando, 200, 202 consultas consultas Acréscimo, 238, 239, 24 4 consultas Atualização, 235, 23 7 consultas Exclusão, 238, 239, 243, 244 criando, 234, 235 executando, 245, 246 criando a partir do Excel, 247 criando com ADO recordsets, 275, 276, 277 tabelas, 261, 262, 263, 264, 265, 266 criando com DAO, 248 bancos de dados preexistentes, verificando, 256, 257, 258, 25 9 campos, definindo, 254, 255, 25 6 campos, número de, 253 função CreateDatabaseWithDAO(), 248, 249 instrução ReDim, 253, 254 nomes definidos pelo usuário, 250, 251, 252 recordsets, 267, 268, 269, 271, 272, 273, 274 referências, 248 tabelas, 249, 250, 259, 260, 26 1 gerenciando a partir do Excel, 15 7 flexibilidade, 158, 159, 160 recursos Excel, 158 usuários, acomodando, 158 visão geral, 160, 161 importando dados para, 239, 240, 241, 242, 243 índices, 229, 230 árvore B, 229 criando automaticamente, 230 criando manualmente, 137, 230, 231, 232 índices de múltiplos campos, 232, 233, 234 Jet, 202, 211 abrindo, 215, 216
Índice
arquivos de informações de grupo de trabalho, 205, 206, 207 contas de usuário, criando, 208, 209 restrições de Administrador, 20 9 segurança em nível do usuário, 204, 205 senhas de banco de dados do Access, 202, 203, 204 solicitações de senha, 207, 208 tornando seguro, 211, 213, 21 5 preexistentes, verificando, 256, 257, 258, 259 recordsets, 266 copiando para pastas de trabalho, 278, 279, 280 criando com ADO, 275, 276, 27 7 criando com DAO, 267, 268, 269, 271, 272, 273, 274 estabelecendo, 300 registros acrescentando, 244 adicionando com ADO (ActiveX Data Objects), 327, 328, 329 adicionando com DAO (Data Access Objects), 267, 268, 269, 271, 325, 326 atualizando, 235, 237 bloqueando, 272, 276 editando com ADO (ActiveX Data Objects), 329, 330, 33 1 editando com DAO (Data Access Objects), 326, 327 excluindo, 243, 244 excluindo da aplicação de reservas, 305, 307, 308, 309, 310, 311 excluindo de aplicação de reservas, 298, 299, 300, 301, 302, 303, 304, 305, 30 6 localizando, 229 números variáveis de, 19, 20, 21 pesquisando, 197, 272, 273 registros-filho, 21 registros-pai, 21 relacionamentos de um-para-um, 226 definindo, 226, 227 identificando, 226, 227 um-para-muitos, 225
visualizando, 224, 225 tabelas chaves primárias, 228, 229 criando, 218 criando com ADO, 261, 262, 263, 264, 265, 266 criando com DAO, 249, 250, 259, 260, 261 definição, 89 integridade referencial, 227, 22 8 joins, 224, 225 Baron, Andy, 205 base, células, 25 BaseCells, 26, 27 BeforeClose, evento, 317 Beginning Of File (BOF), 287 bibliotecas biblioteca ADO, referenciando, 186, 187 biblioteca DAO, referenciando, 197, 198 bibliotecas de objeto, 154 DLLs (dynamic link library), 156 bibliotecas de objeto, 154 bibliotecas de vínculo dinâmico (DLLs), 156 blocos. Consulte instruções bloqueando registros, 276 bloqueando registros bloqueio pessimista, 272 bloqueio otimista, 272 bloqueio pessimista, 272 BOF (Beginning Of File), 286, 287 Boolean values retornando a planilhas, 129 botões, botões de recolher diálogo, 75 C
cache atualizando, 100, 102, 103, 104, 10 5 suprimindo, 99 caixas de combinação, 295 caixas de diálogo Agrupamento, 141, 146 Arquivo de informações de grupo de trabalho, 207 Colar especial, 44 Configurar ODBC para Microsoft Access, 83, 84 Confirmar informações do grupo de trabalho, 207 Criar nova fonte de dados, 82
335
Editar relacionamentos, 227 Escolher fonte de dados, 82, 88, 89 Importar dados, 96, 111 Mostrar tabela, 224 Opções de fonte de dados, 88 Opções de tabela, 100 Opções de tabela dinâmica, 100 Propriedades do intervalo de dados, 124, 126, 127 Propriedades do intervalo de dados externos, 125 referências, 186 Selecionar banco de dados, 84, 85 caixas de seleção consultas, 129, 130 cálculo comissões função PROCV, 41, 42, 43 campos, 219 booleanos retornando a planilhas, 129 retornando às planilhas, 127, 12 8 campos de lista, 52, 70 configurando tipo de, 223, 224 definindo, 254, 255, 256 nomes escolhendo, 219, 220 espaços em, 220, 221, 222 recurso AutoCorreção de nome, 220 número de, 253 Campos Por Coluna, opção (tabelas dinâmicas), 138 Campos Por Linha, opção (tabelas dinâmicas), 138 catálogos, 262 CellColor , variável, 298 células BaseCells, 26, 27 células de base, 25 células mais à direita definição, 33 células mais inferiores definição, 33 pesquisa da última célula, estendendo, 35, 36, 37 Células, comando (menu Formatar), 37 chaves ({}), 28 chaves, primárias, 107, 115, 228, 229, 263 Chipman, Mary, 205 classes definição, 187 classificando
336
Gerenciando dados com o Microsoft Excel
listas, 58, 59 Classificar, comando (menu Dados), 58, 59 CleanupPeriods, variável, 283 Clear, função, 286 cmd, variável de objeto, 319 cnn, variável de objeto, 319 codificação, 164 códigos códigos versus rótulos, 16 pesquisas, 16, 17, 18 validação de dados, 18, 19 Colar especial, caixa de diálogo, 44 Colar especial, comando (menu Editar), 13, 14, 44, 45 Colar especial, comando Transpor, 45 COLUNA, função, 24 colunas títulos de coluna, 52 transpondo linhas e colunas, 13 comando Colar especial, 13, 14 função TRANSPOR, 15 COLUNAS, função, 24 comandos Application.DisplayAlerts, 104 menu Arquivo Retornar dados ao Microsoft Excel, 91 Salvar, 244 menu Colar especial Transpor, 45 menu Consulta consulta Acréscimo, 244 consulta Atualização, 237 consulta Exclusão, 243 Executar, 237 menu Dados Agrupar e detalhar, 141 Atualizar dados externos, 99 Classificação, 58, 59 Filtro, 71 Formulário, 56 Importar dados externos, 82, 109, 122, 282 Lista, 54 Obter dados externos, 81, 82 Relatório de tabela dinâmica e gráfico dinâmico, 132 menu Editar Colar especial, 13, 14, 44, 45 Localizar, 9 menu Exibir Relacionamentos, 224 menu Ferramentas Macros, 161
Opções, 163 Referências, 156 Segurança, 203 menu Filtro AutoFiltro, 71 Filtro avançado, 74 Mostrar tudo, 71 menu Formatar Células, 37 menu Inserir Módulos, 162 Nome, 60 menu Lista Criar lista, 54 Linha de totais, 56 menu Macro Gravar nova macro, 167, 177 Parar gravação, 177 menu Nome Definir, 60, 63, 64 menu Obter dados externos Importar, 239 Nova consulta ao banco de dados, 82 menu Relacionamentos Editar relacionamentos, 227 Mostrar direto, 227 Mostrar tabela, 224 menu Tabela Relações, 116 comissões, cálculo com PROCV, 41, 42, 43 conectando-se a bancos de dados, 185 bancos de dados Jet, 202, 211 abrindo, 215, 216 arquivos de informações de grupo de trabalho, 205, 206, 207 contas de usuário, criando, 208, 209 restrições de Administrador, 20 9 segurança em nível do usuário, 204, 205 senhas de banco de dados do Access, 202, 203, 204 solicitações de senha, 207, 208 tornando seguro, 211, 213, 21 5 conexões ADO, 185 abrindo, 188, 189, 194, 195, 19 6 arquivos UDL (universal data link), 190, 191, 192, 193, 19 4 biblioteca ADO, referenciando, 186, 187
declarando, 187 fontes de dados, especificando, 18 9 instanciando, 187, 188 palavra-chave New, 188 registros, adicionando, 327, 328, 329 registros, editando, 329, 330, 33 1 strings de conexão, 189, 190, 191, 192, 193, 195, 196 conexões DAO, 197, 324 abrindo, 200 biblioteca DAO, referenciando, 197, 198 declarando, 198 localizando bancos de dados, 198, 199 método GetOpenFilename, 198, 199, 200, 201, 202 método OpenDatabase, 200 registros, adicionando, 325, 32 6 registros, editando, 326, 327 tipos de arquivo, filtrando, 200, 202 Configurar ODBC para Microsoft Access, caixa de di, 83, 84 confirmando, solicitações de usuário, 300 Confirmar informações do grupo de trabalho, caixa, 207 Connect, parâmetro (método OpenDatabase), 198 Connection, objeto (ADO) abrindo, 188, 189, 194 arquivos UDL (universal data link), 190, 191, 192, 193, 19 4 biblioteca ADO, referenciando, 186, 187 declarando, 187 fontes de dados, especificando, 18 9 instanciando, 187, 188 palavra-chave New, 188 propriedade ConnectionString, 189, 190, 191, 192, 193, 195, 196 ConnectionString, propriedade (objeto Connection), 189, 190, 191, 192, 193, 195, 196 constantes, nomeando, 61 Consulta acréscimo, comando (menu Consulta), 244
Índice
Consulta atualização, comando (menu Consulta), 237 Consulta, comandos do menu consulta Atualização, 237 consulta Exclusão, 243 Executar, 237 Consulta, janela, 90 Consulta, janela (Access), 120 consultas, 106, 107, 108, 117, 118 arquivos de consulta, 87 construindo com Assistente de consulta, 94, 95, 96, 98 janela Escolher colunas, 93 janela Ordem de classificação, 95 construindo com Microsoft Query, 89, 90, 91, 92 construindo no Access, 118, 119, 120, 122 consultas Acréscimo, 107, 238, 239, 244 consultas Atualização, 107, 235, 23 7 consultas de múltiplas tabelas, 108, 109, 110, 111 consultas Exclusão, 107, 238, 239, 243, 244 consultas parametrizadas, preenchendo planilhas com, 28 2 arrays de memória, preenchendo, 286, 287 planilhas, colocando dados em, 288, 289, 290 planilhas, limpando, 286 recordsets, fazendo loop por, 287, 288, 291 requisitos de layout, 282, 283, 284, 286 consultas Seleção, 106, 311. Consulte também consultas: consultas parametrizadas criando, 234, 235 critérios, 146, 147, 148 dados de caixa de seleção, 129, 13 0 definição, 90, 106 executando, 245, 246 executando com ADO consultas Atualização, 321, 322, 323, 324 consultas Exclusão, 319, 320, 32 1 executando com DAO, 312 consultas Acréscimo, 316, 317, 31 8 consultas Atualização, 318, 31 9
consultas Exclusão, 313 consultas não-persistentes, 314 parâmetros de SQL, 315 executando com DAO (Data Access Objects) consultas não-persistentes, 314 intervalos de dados, 124, 125, 126, 127 joins, 111 estruturas relacionais, 113, 114, 225, 226 inner joins, 114, 115 outer joins, 116, 117 registros-pai, escolhendo, 112, 11 3 não-persistentes, 314 parametrizadas, 286 parametrizadas, preenchendo planilhas com, 286 retornando resultados de, 122, 12 3 RetrieveSingleDay, 284, 285 salvando, 95 sites da Web, consultando, 148 dados financeiros, 148, 149, 15 0 opções de Consulta da Web, 150, 151 SQL (Structured Query Language), 106, 107 valores booleanos, 127, 128 consultas da Web dados financeiros, retornando, 148, 149, 150 opções, 150, 151 consultas parametrizadas consultas parametrizadas recordsets, fazendo loop por, 28 7 consultas parametrizadas, preenchendo planilhas com, 282 arrays de memória, preenchendo, 286, 287 planilhas, colocando dados em, 289, 290 planilhas, limpando, 286 recordsets, fazendo loop por, 291 requisitos de layout, 282, 283, 28 4 CONT.NÚM, função, 66 contas de usuário criando, 208, 209 usuários Administradores, 204 restringindo, 209 copiando dados de recordset para pastas de trabalho, 278, 279, 280
337
recordsets ADO, 279 recordsets DAO, 278, 279 intervalos de dados externos, 96 CopyFromRecordset, função, 278, 279, 280 CopyFromRecordset, método recordsets ADO, 279 recordsets DAO, 278, 279 CopyFromRecordsetWithADO(), função, 279 CopyFromRecordsetWithDAO(), função, 278 CORRESP, função, 25 célula mais inferior, distinguindo de última célula, 37, 38 combinando com função ÍNDICE, 31, 32 correspondências aproximadas, 33, 34 localizando dados com, 29, 30, 31 matrizes de duas vias, 38, 39 pesquisa da última célula, estendendo, 35, 36, 37 tipos de pesquisa, 39 correspondência de dados. Consulte CORRESP, função correspondências aproximadas, 33, 34 CreateDatabase, instrução, 247 CreateDatabase, método, 249 CreateDatabaseWithDAO(), função, 248, 249, 256, 257, 259 CreateQueryDef, função, 314 criando índices automaticamente, 230 criando índices manualmente, 137, 230, 231, 232 Criar lista, comando (menu Lista), 54 Criar nova fonte de dados, caixa de diálogo, 82 critérios (consultas), 146, 147, 148 CurDir, método, 200 cursores, 275, 276 CursorLocation, propriedade (recordsets), 276, 277 CursorType, propriedade (recordsets), 275, 276 D
dados.
Consulte também
intervalos externos, importando, 80
338
Gerenciando dados com o Microsoft Excel
filtrando, 69, 70 arquivos DSN, 87, 88, 89 AutoFiltro, 70, 71, 72, 74 consultas, construindo com Filtro avançado, 74, 75, 76 Assistente de consulta, 94, fórmulas como critérios de 95, 96, 98 filtro, 76, 77 janela Escolher colunas, 93 importando, 80, 81, 82 janela Ordem de classificação, arquivos DSN, 87, 88, 89 95 consultas, construindo com consultas, construindo com Assistente de consulta, 94, Microsoft Query, 89, 90, 91, 95, 96, 98 92 consultas, construindo com localização e formato de dados, Microsoft Query, 89, 90, 91, especificando, 82, 83, 84 92 caixa de diálogo Configurar localização e formato de dados, ODBC para Microsoft Ac, especificando, 82, 83, 84 83, 84 modo compartilhado, 85, 86 caixa de diálogo Criar nova modo de leitura, 85, 86 fonte de dados, 82 modo exclusivo, 85, 86 caixa de diálogo Escolher fonte para tabelas dinâmicas, 98, 99, de dados, 82 100, 102, 103, 104, 105 caixa de diálogo Selecionar listas banco de dados, 84, 85 classificando, 58, 59 modo compartilhado, 85, 86 criando, 54, 55, 56 modo de leitura, 85, 86 definição, 51 modo exclusivo, 85, 86 estrutura de, 51, 52, 53, 54 para tabelas dinâmicas, 98, 99, Formulário de dados, 56, 57, 103, 105 58 atualizando cache, 100 linhas, 52, 55 preenchendo tabelas dinâminomes de variável, 52, 70 cas, 99 ordem de classificação, 53 dados externos, intervalos, 92 localizando atualizando, 96 função CORRESP, 29, 30, 31, copiando, 96 32, 33, 34, 36, 37, 38, 39 nomeando, 92 função DESLOC, 26, 27, 28, dados financeiros 29 retornando de sites da Web, 148, função ÍNDICE, 29, 31, 32, 149, 150 DAO (Data Access Objects), 154, 38, 39 155 pesquisas. Consulte pesquisas recuperando a partir de tabelas bancos de dados, conectando-se dinâmicas, 46, 47 bancos de dados preexistentes, reorganizando, 43, 44 verificando, 256 comando Colar especial, 44, bancos de dados, conectando-se 45 a, 197, 324 função TRANSPOR, 45, 46 abrindo conexões, 200 Dados, comandos do menu bancos de dados preexistentes, Agrupar e detalhar, 141 verificando, 257 Atualizar dados externos, 99 biblioteca DAO, referenciando, Classificação, 58, 59 197, 198 Filtro, 71 declarando conexões, 198 Formulário, 56 função GetOpenFilename, Importar dados externos, 82, 198, 199, 200, 201, 202 109, 122, 282 função OpenDatabase, 200 Lista, 54 localizando, 198, 199 Obter dados externos, 81, 82 tipos de arquivo, filtrando, Relatório de tabela dinâmica e 200, 202 gráfico dinâmico, 132 bancos de dados, criando, 248 dados externos, importando, 80, campos, 253, 254, 255, 256 81, 82
função CreateDatabaseWithDAO(), 248, 249 instrução ReDim, 253, 254 nomes definidos pelo usuário, 250, 251, 252 recordsets, 267, 268, 269, 271, 272, 273, 274 referências, 248 registros, 325, 326, 327 tabelas, 249, 250, 259, 260, 26 1 consultas, executando, 312 consultas Acréscimo, 316, 317, 31 8 consultas Atualização, 318, 31 9 consultas Exclusão, 313 consultas não-persistentes, 314 parâmetros de SQL, 315 recordsets copiando para pastas de trabalho, 278, 279 especificando tipo de, 272 recordsets do tipo Dynaset, 27 4 recordsets do tipo instantâneo, 27 4 recordsets do tipo tabela, 272, 273, 274 versões, 156 Data Access Objects. Consulte DAO DATA(), função, 323 data/hora, campos, 140, 141, 142 DatabaseName, propriedade (função CreateDatabaseWi, 256 DatabaseName, variável, 252 dbObject, parâmetro (método OpenDatabase), 198 dbReservation, variável, 284, 292 declarando
bancos de dados de DAO, 197, 198 objetos Connection, 187 variáveis, 103, 164, 165, 166 variáveis de objeto, 182, 183
definindo
nomes de intervalo dinâmicos, 65, 66, 67 relacionamentos, 226, 227
Definir, comando (menu Nome), 60, 63, 64 Definir nomes, janela, 66, 69 DeleteOldOrders, função, 320 DeleteOldOrders(), função, 319
Índice
DeleteOldOrdersWithSQL(), função, 320, 321 DeleteOldRecords(), função, 313, 315 DeleteOldRecordsWithString1(), função, 314 DESLOC, função, 25, 26 Dim, instrução, 164, 165, 187 Dir, função, 257 DLLs (dynamic link library), 156 Do While, loops, 171, 172, 173 Driver(), função, 252, 255, 256 drop-down, 295 DSN, arquivos, 87, 88, 89
(data source name), 87
dupla precisão, variáveis, 223
estruturas relacionais, criando com joins, 113, 114 estudos de caso
Ballou Realty, 108, 109, 111 consultas Acréscimo e Exclusão, 238, 239 planilha TotalByVendor, 171, 172, 173 Sisyphus Corporation, 9, 10, 11, 12, 13
Exclusão, consulta, comando (menu Consulta), 243 Exclusão, consultas, 107, 238, 239, 243, 244
executando com ADO (ActiveX Data Objects), 319, 320, 32 1 executando com DAO (Data Access Objects), 313
executando
consultas, 245, 246 consultas com ADO (ActiveX Data Objects) consultas Atualização, 321, 322, 323, 324 consultas Exclusão, 319, 320, 32 1 consultas com DAO (Data Access Objects) consultas Acréscimo, 316, 317, 31 8 consultas Atualização, 318, 31 9 consultas Exclusão, 313 consultas não-persistentes, 314 parâmetros de SQL, 315
eventos
Abrir, 101, 102, 103, 104, 105 BeforeClose, 317
Excel 2000
INFODADOSTABELADINÂMICA, função 48
Excel 2002
E
Edit, método, 325 editando
registros ADO (ActiveX Data Objects), 329, 330, 331 DAO (Data Access Objects), 326, 327
Editar, comandos do menu
Colar especial, 44, 45 Localizar, 9
Editar relacionamentos, caixa de diálogo, 227 Editar relacionamentos, comando (menu Relacionamen, 227 editores, VBE (Visual Basic Editor), 102, 156, 161, 162 End, função, 286 End Of File (EOF), 287 End Sub, instrução, 163 End With, instrução, 174, 175, 291 End(), função, 253 ENDEREÇO, função, 24 EOF (End Of File), 287 erros
#VALOR!, 64 tipo definido pelo usuário não definido, 157
Escolher colunas, janela (Assistente de consulta), 93 Escolher fonte de dados, caixa de diálogo, 82, 88, 89 escopo
nomes, 68, 69
espaços
em nomes de campo, 220, 221, 22 2
estendendo
pesquisa da última célula, 35, 36, 37
INFODADOSTABELADINÂMICA, função , 47, 48
Excel 2003
INFODADOSTABELADINÂMICA, função , 47, 48
Excel 97
INFODADOSTABELADINÂMICA, função , 48
Excel, mal uso, 8
maneiras de contornar problemas não-relacionais, 19, 20, 21 orientação de dados ineficaz, 13 alterando com comando Colar especial, 13, 14 alterando com função TRANSPOR, 15 rótulos versus códigos, 16 pesquisas, 16, 17, 18 validação de dados, 18, 19 sobrecarga de funções, 9, 10, 11, 12, 13
excluindo
registros, 243, 244 registros da aplicação de reservas, 29 8 declarações de variável, 298, 29 9 instrução Sub, 298 limpeza, 305, 306 listagem de código, 307, 308, 309, 310, 311 listagem de códigos, 306 recordsets, estabelecendo, 300 registros, localizando, 301, 30 2 reservas recorrentes, 302, 303, 304, 305 solicitações de usuário, verificando e confirmando, 30 0
339
Executar, comando (menu Consulta), 237 exibindo
planilhas, 162
extensões, ADO Extensions, 247 F
Ferramentas, comandos de menu
Opções, 163 Referências, 156
Ferramentas, comandos do menu
Macros, 161 Segurança, 203
FieldLength, variável, 254 Filter, função, 330 FilterIndex, parâmetro (método GetOpenFilename), 201 filtrando dados, 69, 70
AutoFiltro, 70, 71, 72, 74 outros critérios, 72, 73, 74 Personalizar AutoFiltro, 72, 73, 74 seta de filtro, 71 Filtro avançado, 74, 75, 76 critérios, 75, 76 exemplo, 74, 75 fórmulas, 76, 77 fórmulas como critérios de filtro, 76, 77
340
Gerenciando dados com o Microsoft Excel
Filtro avançado, 74, 75, 76
critérios, 75, 76 exemplo, 74, 75 fórmulas, 76, 77
Filtro avançado, comando (menu Filtro), 74 Filtro, comando (menu Dados), 71 Filtro, comandos do menu
AutoFiltro, 71 Filtro avançado, 74 Mostrar tudo, 71
filtros
AutoFiltro, 70, 71, 72, 74 outros critérios, 72, 73, 74 Personalizar AutoFiltro, 72, 73, 74 seta de filtro, 71 Filtro avançado, 74, 75, 76 critérios, 75, 76 exemplo, 74, 75 fórmulas, 76, 77 fórmulas como critérios de filtro, 76, 77
FindARecord(), função, 273 FindFirst, função, 327 FindLast, função, 327 FindNext, função, 327 FindPrevious, função, 327 FindReservationDetailIDInDB, método, 304 FindReservationMasterIDInDB, método, 302, 304 flexibilidade, 158, 159, 160 fontes de dados
ADO, 189
For Each, loops, 183 For-Next, loops, 168, 169, 170, 171
exemplo simples, 168, 169 loops For-Next internos, 170, 17 1
For-Next, loops internos, 170, 171 Formatação automática de tabela, opção (tabelas di, 137 Formatar, comandos do menu
Células, 37
formato de dados externos, especificando
caixa de diálogo Configurar ODBC para Microsoft Ac, 83, 84, 252 caixa de diálogo Criar nova fonte de dados, 82 caixa de diálogo Escolher fonte de dados, 82
caixa de diálogo Selecionar banco de dados, 84, 85 Formulário, comando (menu Dados), 56 Formulário de dados, 56, 57, 58 formulários
Formulário de dados, 56, 57, 58 formulários de usuário retornando dados de banco, 82 formulários de usuário, retornando dados de banco, 291, 292 registros, identificando, 292, 293, 294
fórmulas
como critérios de filtro, 76, 77 fórmulas de array, 12 fórmulas de matriz, 28, 29 nomeando, 60, 61
FromDBtoForm(), função, 292 funções, 24
AddComment, 290 AddNew, 325 AddRecordsWithDAO(), 267, 269 AddStocks(), 325, 328 ÁREAS, 24 Clear, 286 COLUNA, 24 COLUNAS, 24 CONT.NÚM, 66 CopyFromRecordset, 278, 279, 280 recordsets ADO, 279 recordsets DAO, 278, 279 CopyFromRecordsetWithADO(), 279 CopyFromRecordsetWithDAO(), 278 CORRESP, 25 célula mais inferior, distinguindo de última célula, 37, 38 combinando com função ÍNDICE, 31, 32 correspondências aproximadas, 33, 34 localizando dados com, 29, 30, 31 matrizes de duas vias, 38, 39 pesquisa da última célula, estendendo, 35, 36, 37 tipos de pesquisa, 39 CreateDatabase, 249 CreateDatabaseWithDAO, 256, 257, 259 CreateDatabaseWithDAO(), 248, 249 CreateQueryDef, 314
CurDir, 200 DATA(), 323 definição, 8 DeleteOldOrders, 320 DeleteOldOrders(), 319 DeleteOldOrdersWithSQL(), 320, 321 DeleteOldRecords(), 313, 315 DeleteOldRecordsWithString1(), 314 DESLOC, 25, 26, 28 argumentos Altura/Largura, 27, 28, 29 BaseCells, 26, 27 exemplo, 26, 27 múltiplas células, retornando, 27, 28, 29 Dir, 257 Driver(), 252, 255, 256 Edit, 325 End, 286 End(), 253 ENDEREÇO, 24 Filter, 330 FindARecord(), 273 FindFirst, 327 FindLast, 327 FindNext, 327 FindPrevious, 327 FindReservationDetailIDInDB, 304 FindReservationMasterIDInDB, 302, 304 FromDBtoForm, 292 função TRANSPOR, 45, 46 GetConnectionStrings, 195, 196 GETDATE(), 323 GetMasterID(), 297, 298 GetOpenFilename, 198, 199, 200, 201, 202 HLOOKUP, 25 ÍNDICE, 25 combinando com função CORRESP, 31, 32 localizando dados com, 29 matrizes de duas vias, 38, 39 INDIRETO, 25 INFODADOSTABELADINÂMICA Excel 2002/Excel 2003, 47, 48 Excel 97/Excel 2000, 48 inserindo automaticamente, 48, 49 quando utilizar, 49, 50 LINHA, 25 LINS, 26 MakeNewTableWithDAO(), 250, 260, 261 MakeTheConnection, 195, 196
Índice
Match, 288 NewShortStayTable(), 262, 265, 26 6 Offset, 289 Open, 275 argumentos, 195, 196 exemplo, 194, 195 OpenDatabase, 198, 200 OpenRecordset(), 267, 271, 272 PROCV, 17, 25, 40 comissões de cálculo com, 41, 42, 43 intervalos de duas colunas, 40, 41 matrizes de pesquisa, 40 números de coluna, 40 tipos de pesquisa, 40 valores de pesquisa, 40 RecordLastInspectDate, 323 RemoveReservation(), 285 Resize, 289, 290 Seek(), 272 SeekARecord(), 273 SellStocks(), 326, 329 sobrecarga, 9, 10, 11, 12, 13 Sort, 180 Text, 287 TransferText, 245 TRANSPOR, 15, 26 Ucase, 288 Update, 325, 329 UpdateEmployeeData(), 245 UpdatePhysicalDates(), 318 Workbook_BeforeClose(), 317 G
Gerar InfoDados TabelaDinâmica, botão, 49 GetConnectionStrings, função, 195, 196 GetDataFromAccess, macros, 163 GETDATE(), função, 323 GetMasterID(), função, 297, 298 GetOpenFilename, método, 198, 199, 200, 201, 202 gravando macros, 177, 179 Gravar nova macro, comando (menu Macro), 167 H
handlers de evento, 101 HLOOKUP, função, 25 I
identificando relacionamentos, 226, 227
importando texto, 239, 240, 241, 242, 243
importando dados
arquivos DSN, 87, 88, 89 consultas, construindo com Assistente de consulta, 94, 95, 96, 98 janela Escolher colunas, 93 janela Ordem de classificação, 95 consultas, construindo com Microsoft Query, 89, 90, 91, 92 localização e formato de dados, especificando, 82, 83, 84 caixa de diálogo Configurar ODBC para Microsoft Ac, 83, 84 caixa de diálogo Criar nova fonte de dados, 82 caixa de diálogo Escolher fonte de dados, 82 caixa de diálogo Selecionar banco de dados, 84, 85 modo compartilhado, 85, 86 modo de leitura, 85, 86 modo exclusivo, 85, 86 para tabelas dinâmicas, 99, 102, 10 3 atualizando cache, 100 preenchendo tabelas dinâmicas, 99
341
VBE (Visual Basic Editor), 161
inner joins, 114, 115 inserindo função INFODADOSTABELADINÂMICA automatic, 48, 49 Inserir, comandos do menu Módulos, 162 Nome, 60
Insira linhas inteiras para novos dados, exclua as, 125 instanciando objetos Connection, 187, 188
instruções
AddNew, 268, 269 Configure, 271 CreateDatabase, 247 Dim, 164, 165, 187 End Sub, 163 End With, 175, 291 loops, 168 Do While, 171, 172, 173 For Each, 183 For-Next, 168, 169, 170, 171 New, 188 On Error GoTo, 257 Option Explicit, 163 ReDim, 253, 254 Set, 182 Sub, 163, 298 Update, 269 With, 174, 175, 176, 177, 294
integridade
integridade referencial, 227, 228 Importar, comando (menu Obter dados externos), 239 interseções Importar dados, caixa de diálogo, interseções implícitas, 63, 64 96, 111 intervalos comparado com matrizes, 30 Importar dados externos, interseções implícitas, 63, 64 comando (menu Dados), 82, 109, 122, 282 intervalos de dados externos, 92 ÍNDICE, função, 25, 29 atualizando, 96 combinando com função CORRESP, 31, 32 matrizes de duas vias, 38, 39
copiando, 96 nomeando, 92 intervalos de duas colunas função PROCV, 40, 41 intervalos definidos dinamicamente, selecionando, 67, 68 nomeando, 62, 63 nomes de intervalo dinâmicos, 65, 66, 67, 68 nomes de intervalo estáticos, 64 propriedades, 124, 125, 126, 127
índices, 229, 230, 263
árvore B, 229 criando automaticamente, 230 criando manualmente, 137, 230, 231, 232 índices de múltiplos campos, 233, 234
INDIRETO, função, 25 INFODADOSTABELADINÂMICA, função, 24 intervalos de dados. intervalos Excel 2002/Excel 2003, 47, 48 Excel 97/Excel 2000, 48 inserindo automaticamente, 48, 49 quando utilizar, 49, 50
iniciando
Consulte
J
janelas Adicionar tabelas, 90
342
Gerenciando dados com o Microsoft Excel
Consulta, 90 Consulta (Access), 120 Definir nomes, 66, 69 Nova consulta, 119 Propriedades (VBE), 162 Jet, bancos de dados, 202 Jet, bancos de dados protegidos, 202, 211
Linha de totais, 56 aplicação de reservas, 306, 307, 308, 309, 310, 311
listas
classificando, 58, 59 criando, 54, 55, 56 definição, 13, 51 estrutura de, 51, 52, 53, 54 Formulário de dados, 56, 57, 58 linhas, 52 linha de inserções, 55 nomes de variável, 52, 70 ordem de classificação, 53
abrindo, 215, 216 arquivos de informações de grupo de trabalho, 205, 206, 207 contas de usuário, criando, 208, 20 9 restrições de Administrador, 209 ListIndex, propriedade (caixas de segurança em nível do usuário, 204, 205 combinação), 296 Locale, propriedade (método senhas de banco de dados do CreateDatabase), 249 Access, 202, 203, 204 localização de dados externos, solicitações de senha, 207, 208 especificando tornando seguro, 211, 213, 215 caixa de diálogo Configurar joins, 111 estruturas relacionais, 113, 114 ODBC para Microsoft Ac, de um-para-um, 226 83, 84 um-para-muitos, 225 caixa de diálogo Criar nova fonte inner joins, 114, 115 de dados, 82 outer joins, 116, 117 caixa de diálogo Escolher fonte preparando, 224, 225 de dados, 82 registros-pai, escolhendo, 112, caixa de diálogo Selecionar banco 11 3 de dados, 84, 85 localizando
L
Largura, argumento (função DESLOC), 27, 28, 29 layout
planilhas, 282, 283, 284, 286
Layout de página, opção (tabelas dinâmicas), 138 left outer join, 116 limpando
planilhas, 286
linha de inserções, 55 Linha de totais, comando (menu Lista), 56 LINHA, função, 25 linhas
linhas de lista, 52 linha de inserções, 55 transpondo linhas e colunas, 13 comando Colar especial, 13, 14 função TRANSPOR, 15
linhas de relacionamento, 110 LINS, função, 26 Lista, comando (menu Dados), 54 Lista, comandos do menu
Criar Lista, 54
registros função FindARecord(), 273 função SeekARecord(), 197, 272, 273
listagem de códigos
registros, 229 aplicação de reservas, 301, 302 função FindARecord(), 273 função SeekARecord(), 197, 272, 273
localizando dados
função CORRESP, 29, 30, 31 célula mais inferior, distinguindo de última célula, 37, 38 combinando com função ÍNDICE, 31, 32 correspondências aproximadas, 33, 34 matrizes de duas vias, 38, 39 pesquisa da última célula, estendendo, 35, 36, 37 tipos de pesquisa, 39 função DESLOC, 26 argumentos Altura/Largura, 27, 28, 29 BaseCells, 26, 27 exemplo, 26, 27 múltiplas células, retornando, 27, 28, 29 função ÍNDICE, 29 combinando com função CORRESP, 31, 32 matrizes de duas vias, 38, 39
Localizar, comando (menu Editar), 9 LockfileName, variável, 252 LockType, propriedade (recordsets), 276 loops, 168
Do While, 171, 172, 173 For Each, 183 For-Next, 168, 169, 170, 171 exemplo simples, 168, 169 loops For-Next internos, 170, 17 1 loops aninhados, 170 números de itens, fazendo loop com, 169, 170, 171
M
Macro, comandos do menu
Gravar nova macro, 167, 177 Parar gravação, 177
Macros, comando (menu Ferramentas), 161 maior que (>), sinal, 93 MakeNewTableWithDAO(), função, 250, 260, 261 MakeTheConnection, função, 195, 196 MasterResID, variável, 298 Match, função, 288 matrizes
comparados com intervalos, 30 fórmulas de matriz, 28, 29 matrizes de duas vias função CORRESP, 38, 39 função ÍNDICE, 38, 39
memória
arrays de memória, preenchendo, 286, 287 cache atualizando, 100 suprimindo, 99
menor que (<), sinal, 93 Mesclar rótulos, opção (tabelas dinâmicas), 138 métodos. Consulte funções Microsoft Data Engine (MSDE), 280 Microsoft Query, 106, 107, 108
construindo consultas, 89, 90, 91, 92 consultas da Web, 148, 149, 150, 151
Índice
consultas de múltiplas tabelas, intervalos, 62, 63 108, 109, 110, 111 nomes de intervalo dinâmicos, critérios, 146, 147, 148 65, 66, 67, 68 joins, 111 nomes de intervalo estáticos, estruturas relacionais, 113, 114 64 inner joins, 114, 115 intervalos de dados externos, 92 outer joins, 116, 117 notação de ponto, 166, 167, 168 registros-pai, escolhendo, 112, variáveis, 166 11 3 nomes, 60 resultados de consultas, interseções implícitas, 63, 64 retornando, 122, 123 nomes de campo modelos de objeto, 156, 157 escolhendo, 219, 220 aprendendo nomes em, 179 espaços em, 220, 221, 222 referenciando, 156 recurso AutoCorreção de modos nome, 220 compartilhado, 85, 86 nomes de constantes, 61 de leitura, 85, 86 nomes de fórmula, 60, 61 exclusivo, 85, 86 nomes de intervalo, 62, 63 Módulo, comandos (menu nomes de intervalo dinâmicos, Inserir), 162 65, 66, 67, 68 módulos nomes de intervalo estáticos, criando, 161, 162, 163 64 Mostrar direto, comando (menu nomes no nível de pasta de Relacionamentos), 227 trabalho, 68, 69 Mostrar tabela, caixa de diálogo, nomes no nível de planilha, 68, 224 69 Mostrar tabela, comando (menu notação de ponto, 166, 167, 168 Relacionamentos), 224 variáveis, 166 Mostrar tudo, comando (menu notação de ponto, 166, 167, 168 Filtro), 71 Nova consulta, janela, 119 MSDE (Microsoft Data Engine), números 280 campos numéricos, agrupando, múltiplas tabelas, consultando, 142, 143 108, 109, 110, 111 números de itens, fazendo loop MultiSelect, parâmetro (método com, 169, 170, 171 GetOpenFilename), 201 variáveis de registros, tratamento, MultiSelect, propriedade (caixas 21 de listagem), 295 O
N
Name, parâmetro (método OpenDatabase), 198 New, palavra-chave, 188 NewShortStayTable(), função, 262, 265, 266 Next, instrução. Consulte For Next, loops nível de livro, nomes, 68, 69 nível de planilha, nomes, 68, 69 Nome, caixa, 63 Nome, comando (menu Inserir), 60 Nome, comandos do menu Definir, 60, 63, 64 nomeando constantes, 61 fórmulas, 60, 61
objetos. Consulte também ADO adicionando a VBA, 154 adicionando ao VBA, 155 ADO (ActiveX Data Objects), 154, 155 versões, 156 bibliotecas de objeto, 154 Connection (ADO) abrindo, 188, 189, 194, 195, 19 6 arquivos UDL (universal data link), 190, 191, 192, 193, 19 4 biblioteca ADO, referenciando, 186, 187 declarando, 187 fontes de dados, especificando, 18 9
343
instanciando, 187, 188 palavra-chave New, 188 propriedade ConnectionString, 189, 190, 191, 192, 193, 195, 196 DAO (Data Access Objects), 154, 15 5 versões, 156 métodos, 156 modelos de objeto, 156, 157 aprendendo nomes em, 179 referenciando, 156 Parameter, 285 propriedades, 156 Obter dados externos, comando (menu Dados), 81, 82 Obter dados externos, comandos do menu Importar, 239 ocultando planilhas, 162 Offset, função, 289 Offset function, 289 OLAP, cubos, 87 On Error GoTo, instrução, 257 Opções, comando (menu Ferramentas), 163 Opções de fonte de dados, caixa de diálogo, 88 Opções de tabela, caixa de diálogo, 100 Opções de tabela dinâmica, caixa de diálogo, 100 Open, função, 275 Open, método argumentos, 195, 196 exemplo, 194, 195 OpenDatabase, método, 198, 200 OpenRecordset(), função, 267, 271, 272 Option Explicit, opção, 163 Options, parâmetro (método OpenDatabase), 198 Options, propriedade (função OpenRecordset()), 271, 272 Options, propriedade (método CreateDatabase), 249 ordem de classificação listas, 53 Ordem de classificação, janela (Assistente de cons, 95 orientação alterando com comando Colar especial, 13, 14 alterando com função TRANSPOR, 15 orientação de dados ineficaz, 13
344
Gerenciando dados com o Microsoft Excel
alterando com comando Colar especial, 13, 14 alterando com função TRANSPOR, 15 Otimizar memória, opção (tabelas dinâmicas), 140 outer joins, 116, 117 P
Para células vazias, mostrar, opção (tabelas dinâm, 139 Para valores de erro, mostrar, opção (tabelas dinâ, 138 Parameter, objeto, 285 parâmetros (consulta de SQL), 315 parâmetros (consultas), 146, 148 Parar gravação, comando (menu Macro), 177 passando por referência, 255 passando por valor, 255 Pasta de trabalhoAbrir, evento, 102, 103, 104, 105 pastas de trabalho, 166
importando dados, 80, 81, 82 arquivos DSN, 87, 88, 89 consultas, construindo com Assistente de consulta, 94, 95, 96, 98 consultas, construindo com Microsoft Query, 89, 90, 91, 92 localização e formato de dados, especificando, 82, 83, 84 modo compartilhado, 85, 86 modo de leitura, 85, 86 modo exclusivo, 85, 86 para tabelas dinâmicas, 98, 99, 100, 102, 103, 104, 105 nomes no nível de pasta de trabalho, 68, 69
Permitir ramificação de detalhes, opção (tabelas d, 139 Personalizar AutoFiltro, 72, 73, 74 pesquisas
células BaseCells, 26, 27 células de base, 25 células mais à direita, 33 células mais inferiores, 33, 37, 38 pesquisa da última célula, estendendo, 35, 36 consultas parametrizadas colocando dados em, 288 planilhas, limpando, 286 copiando recordsets para, 278, 279, 280 recordsets ADO, 279 recordsets DAO, 278, 279 funções. Consulte funções limpando, 286 listas classificando, 58, 59 criando, 54, 55, 56 definição, 51 estrutura de, 51, 52, 53, 54 Formulário de dados, 56, 57, 58 linhas, 52, 55 nomes de variável, 52, 70 ordem de classificação, 53 localizando dados em função CORRESP, 29, 30, 31, 33, 34, 35, 36, 37, 38 função DESLOC, 26, 27 função ÍNDICE, 29, 31, 32 nomes no nível de pasta de trabalho, 68, 69 ocultando/reexibindo, 162 pesquisa da última célula, estendendo, 37 preenchendo com consultas parametrizadas, 282 arrays de memória, preenchendo, 286, 287 planilhas, colocando dados em, 288, 289, 290 planilhas, limpando, 286 recordsets, fazendo loop por, 287, 288, 291 requisitos de layout, 282, 283, 284, 286 preenchendo, consultas parametrizadas, 288 reorganizando dados, 43, 44 comando Colar especial, 44, 45 função TRANSPOR, 45, 46 requisitos de layout, 282, 283
função PROCV, 17 comissões de cálculo com, 41, 42, 43 intervalos de duas colunas, 40, 41 matrizes de pesquisa, 40 números de coluna, 40 tipos de pesquisa, 40 valores de pesquisa, 40 preenchendo, 99 rótulos versus códigos, 16, 17, 18 preenchendo planilhas planilhas consultas parametrizadas, 282
arrays de memória, preenchendo, 286, 287 planilhas, colocando dados em, 289, 290 planilhas, limpando, 286 recordsets, fazendo loop por, 287, 291 requisitos de layout, 282, 283, 28 4 Preencher fórmulas em colunas adjacentes à tabela,, 127 preparando dados para tabelas dinâmicas, 140
campos de tempo/data, agrupando, 140, 141, 142 campos numéricos, agrupando, 142, 143 valores nulos, evitando, 144, 145, 14 6
Preservar formatação, opção (tabelas dinâmicas), 138 prm, variável de objeto, 319 problemas não-relacionais, maneiras de contornar, 19, 20, 21 procedures armazenadas, 319, 323. Consulte também consultas Processamento Analítico On-line (Online Analytic P, 87 procurando dados. Consulte localizando dados PROCV, função, 17, 25, 40
comissões de cálculo com, 41, 42, 43 intervalos de duas colunas, 40, 41 matrizes de pesquisa, 40 números de coluna, 40 tipos de pesquisa, 40 valores de pesquisa, 40
programa de gravação de macros, 167
código, adaptando a outros propósitos, 179, 180 macros, gravando, 177, 178, 179 modelo de objeto, aprendendo nomes em, 179
projetos
definição, 162
propriedades, 156
ConnectionString (objeto Connection), 189, 190, 191, 192, 193, 195, 196 CursorLocation (recordsets), 276, 277 CursorType (recordsets), 275, 276 definição, 124
Índice
intervalos de dados, 124, 125, 126, 127 ListIndex (caixas de combinação), 29 6 LockType (recordsets), 276 MultiSelect (caixas de listagem), 29 5 Visible (planilhas), 162 Propriedades, janela (VBE), 162 pt, variável, 103, 104
Q qdfRetrieveCurrent, variável, 284
R ReadOnly, parâmetro (método OpenDatabase), 198 recolher diálogo, botões, 75 reconfigurando tabelas dinâmicas, 135, 136, 137 RecordCount, variável, 254 RecordLastInspectDate, função, 323 recordsets, 266 criando com ADO, 275 propriedade CursorLocation, 276, 277 propriedade CursorType, 275, 27 6 propriedade LockType, 276 criando com DAO, 267, 268, 269, 271 especificando tipo de recordset, 271, 272 recordsets do tipo Dynaset, 27 4 recordsets do tipo instantâneo, 27 4 recordsets do tipo tabela, 272, 273, 274 estabelecendo, 300 ReDim, instrução, 253, 254 referência, passando por, 255 referenciando modelos de objeto, 156 Referências, comando (menu Ferramentas), 156 Registre Novo comando de Macro (menu de Macro), 177 registros acrescentando, 244 adicionando com ADO (ActiveX Data Objects), 327, 328, 32 9
adicionando com DAO (Data Access Objects), 267, 268, 269, 271, 325, 326 atualizando, 235, 237 bloqueando, 276 bloqueio otimista, 272 bloqueio pessimista, 272 editando com ADO (ActiveX Data Objects), 329, 330, 33 1 editando com DAO (Data Access Objects), 326, 327 excluindo, 243, 244 excluindo da aplicação de reservas listagem de código, 307, 308, 309, 310, 311 reservas recorrentes, 305 excluindo de aplicação de reservas, 298 declarações de variável, 298, 29 9 instrução Sub, 298 limpeza, 305, 306 listagem de códigos, 306 recordsets, estabelecendo, 300 registros, localizando, 301, 30 2 reservas recorrentes, 302, 303, 30 4 solicitações de usuário, verificando e confirmando, 30 0 joins, 111 estruturas relacionais, 113, 114, 225, 226 inner joins, 114, 115 outer joins, 116, 117 registros-pai, escolhendo, 112, 11 3 localizando, 229 macros, 167 números variáveis de, 19, 20, 21 pesquisando FindARecord(), função, 273 função FindARecord(), 273 função SeekARecord(), 197, 272, 273 registros-filho, 21 registros-pai, 21 registros-filho, 21 fazendo join com registros-pai, 11 1 estruturas relacionais, 113, 114 inner joins, 114, 115 outer joins, 116, 117 seleção de registro pai, 112, 11 3
345
registros-pai, 21 fazendo join com registros-filhos, 11 1 estruturas relacionais, 113, 114 inner joins, 114, 115 outer joins, 116, 117 seleção de registro pai, 112, 11 3 relacionamentos definindo, 226, 227 identificando, 226, 227 integridade referencial, 227, 228 um-para-muitos, 225 um-para-um, 226 visualizando, 224, 225 Relacionamentos, comando (menu Exibir), 224 Relacionamentos, comandos do menu Mostrar direto, 227 Relações, comando (menu Tabela), 116 Relatório de tabela dinâmica e gráfico dinâmico, 132 RemoveReservation(), função, 285 reorganizando dados, 43, 44 comando Colar especial, 44, 45 função TRANSPOR, 45, 46 Repetir rótulos de item em cada página impressa, o, 138 reserva, aplicação excluindo registros de listagem de código, 307, 308, 309, 310, 311 reservas, aplicação consultas parametrizadas, 282 arrays de memória, preenchendo, 286, 287 localizações de reserva, localizando, 288, 291 planilhas, colocando dados em, 288, 289, 290 planilhas, limpando, 286 requisitos de layout, 282, 283, 284, 286 excluindo registros a partir de, 29 8 declarações de variável, 298, 29 9 instrução Sub, 298 limpeza, 305, 306 listagem de códigos, 306 recordsets, estabelecendo, 300 registros, localizando, 301, 30 2 registros recorrentes, 302, 303, 30 4
346
Gerenciando dados com o Microsoft Excel
solicitações de usuário, verificando e confirmando, 30 0 excluindo registros de registros recorrentes, 305 objetivos, 280, 281 reservas recorrentes (aplicação de reservas) localizando, 302, 303, 304, 305 permitindo, 298 ReservationRange, variável, 288 Resize, função, 289, 290 ResourceCount, variável, 284 resultados de consultas retornando, 122, 123 Retornar dados ao Microsoft Office Excel, comando, 91 RetrieveSingleDay, consulta, 284, 285 right outer join, 116 RoomArray, array, 284, 287 rótulos rótulos versus códigos, 16 pesquisas, 16, 17, 18 validação de dados, 18, 19 rsRecordsToEdit, variável, 293, 294 S
salvando consultas, 95 Salvar, comando (menu Arquivo), 244 Salvar dados com layout de tabela, opção, 139 Salvar definição de consulta, propriedade (interva, 124 Salvar senha, opção (tabelas dinâmicas), 140 Salvar senha, propriedade (intervalos de dados), 124 SECFAQ.doc, 205 Security, comando (menu Ferramentas), 203 Security Support Provider Interface (SSPI), 319 Seek(), função, 272 SeekARecord(), função, 273 segurança SPPI (Security Support Provider Interface), 319 segurança em nível do usuário bancos de dados Access, 204, 205 Seleção, consultas, 106, 311. Consulte também
consultas; consultas parametrizadas
selecionando intervalos definidos dinamicamente, 67, 68 Selecionar banco de dados, caixa de diálogo, 84, 85 SellStocks(), função, 326, 329 senhas bancos de dados Jet, 202, 203, 20 4 solicitando, 207, 208 servidores SQL Server SQL Server Desktop Engine, 28 0 Set, instrução, 182, 271 SetupPeriods, variável, 283 Sim/Não, valores. Consulte valores booleanos Sisyphus Corporation, estudo de caso funções, sobrecarga, 9, 10, 11, 12, 13 sites da Web consultando, 148 dados financeiros, 148, 149, 15 0 opções de Consulta da Web, 150, 151 sobrecarga, funções, 9, 10, 11, 12, 13 Sobrescreva as células existentes com novos dados,, 127 solicitando senhas, 207, 208 Somas totais para linhas, opção, 137 Sort, método, 180 Source, propriedade (função OpenRecordset()), 267, 271 SourceName, variável, 263 SPPI (Security Support Provider Interface), 319 SQL (Standard Query Language) consultas parâmetros, 315 SQL (Structured Query Language), 106, 107 SQL Server SQL Server Desktop Engine, 28 0 SQL Server Desktop Engine, 280 StartTime, variável, 293 strings de conexão ADO, 189, 190, 191, 192, 193, 195, 196
Structured Query Language (SQL), 106, 107 strUpdateSQL, variável, 318 Sub, instrução, 163, 298 sub-rotinas, 161 criando, 163 sublinhado (_), 220, 222 suprimindo cache, 99 T
Tabela, comandos do menu Relações, 116 tabelas chaves primárias, 115, 228, 229 criando, 218 criando com ADO, 261, 262, 263, 264, 265, 266 chave primária, 263 função NewShortStayTable(), 26 2 NewShortStayTable(), função, 262, 265, 266 variáveis de objeto, 263 criando com DAO, 249, 250, 259, 260, 261 definição, 89 integridade referencial, 227, 228 joins preparando, 224, 225 múltiplas tabelas, consultando, 108, 109, 110, 111 relacionamentos de um-para-um, 226 definindo, 226, 227 identificando, 226, 227 um-para-muitos, 225 um-para-um, 226 visualizando, 224, 225 tabelas dinâmicas, 10, 132 campos de data/hora, agrupando, 140, 141, 142 campos numéricos, agrupando, 142, 143 criando, 132, 133, 134 importando dados para, 98, 99, 100, 102, 103, 104, 105 opções de tabela, 137, 138, 139, 140 preenchendo, 99 reconfigurando, 135, 136, 137 valores nulos, evitando, 144, 145, 146 tabelas dinâmicas, 10, 132 campos de data/hora, agrupando, 140, 141, 142
Índice
campos numéricos, agrupando, 142, 143 criando, 132, 133, 134 importando dados para, 98, 99, 102, 103, 104, 105 atualizando cache, 100 preenchendo tabelas dinâmicas, 99 opções de tabela Atualizar a cada x minutos, 13 9 Atualizar ao abrir, 139 campos Por Coluna, 138 campos Por Linha, 138 Consulta em segundo plano, 14 0 Formatação automática de tabela, 137 Layout de página, 138 Mesclar rótulos, 138 Otimizar memória, 140 Para células vazias, mostrar, 13 9 Para valores de erro, mostrar, 13 8 Permitir ramificação de detalhes, 139 Preservar formatação, 138 Repetir rótulos de item em cada página impressa, 138 Salvar dados com layout de tabela, 139 Salvar senha, 140 Somas totais para Colunas, 13 7 Somas totais para Linhas, 137 preenchendo, 99 reconfigurando, 135, 136, 137 valores nulos, evitando, 144, 145, 14 6 vantagens de, 46, 47 Text, função, 287 texto importando, 239, 240, 241, 242, 24 3 TimeArray, array, 284, 287 tipo Dynaset, recordsets, 274 tipo tabela, recordsets, 272, 273, 274 tipos campos, 223, 224 tipos de dados campos, 223, 224 definidos pelo usuário não definido, 157 tipos de dados definidos pelo usuário, 251
Title, parâmetro (método GetOpenFilename), 201 títulos títulos de coluna, 52 títulos de coluna, 52 TotalByVendor, planilha, 171, 172, 173 Transact-SQL, 107 TransferText, método, 245 transpondo dados, 43, 44 comando Colar especial, 44, 45 função TRANSPOR, 45, 46 transpondo linhas e colunas, 13 comando Colar especial, 13, 14 função TRANSPOR, 15 Transpor, comando (menu Colar especial), 45 TRANSPOR, função, 15, 26, 45, 46 Type, propriedade (função OpenRecordset()), 271 U
Ucase, função, 288 UDL (universal data link), arquivos, 190, 191, 192, 193, 194 últimas células distinguindo de células mais inferiores, 37, 38 pesquisa da última célula, estendendo, 35, 36, 37 universal data link (UDL), arquivos, 190, 191, 192, 193, 194 Update, função, 325, 329 Update, instrução, 269 UpdateEmployeeData(), função, 245 UpdatePhysicalDates(), função, 318 usuários acomodando, 158 Administrador, 191 V
validação de dados rótulos versus códigos, 18, 19 valor, passando por, 255 Valor vazio (variáveis), 164 valores booleanos, retornando às planilhas, 127, 128 nulos, evitando, 144, 145, 146 valores estranhos, 67 variáveis aplicação de reservas
347
CellColor, 298 CleanupPeriods, 283 dbReservation, 284, 292 MasterResID, 298 qdfRetrieveCurrent, 284 ReservationRange, 288 ResourceCount, 284 rsRecordsToEdit, 293, 294 SetupPeriods, 283 StartTime, 293 cmd, 319 cnn, 319 declarando, 103, 164, 165, 166 FieldLength, 254 LockfileName, 252 nomeando, 166 passando por referência, 255 passando por valor, 255 prm, 319 pt, 103, 104 RecordCount, 254 SourceName, 263 strUpdateSQL, 318 variantes, 164 variáveis alfanuméricas, 164 variáveis de dupla precisão, 223 variáveis de objeto, 181 configurando, 182 declarando, 182, 183 objetos em loops For Each, 18 3 variáveis de objeto, configurando, 18 2 variáveis de objeto, declarando, 182, 183 variáveis de objeto, objetos em loops For Each, 183 variáveis de registros, números, tratamento, 19 variáveis numéricas, 164 variáveis variantes, 164 wks, 103, 104 VBA (Visual Basic for Applications), 154 benefícios de, 157 acomodar usuários, 158 flexibilidade, 158, 159, 160 recursos Excel, 158 visão geral, 160, 161 erros tipo definido pelo usuário não definido, 157 história, 156 instruções Dim, 164, 165, 187 End Sub, 163 New, 188 Option Explicit, 163
348
Gerenciando dados com o Microsoft Excel
Set, 182 definição, 162 Sub, 163 sub-rotinas, 161 With, 174, 175, 176, 177 criando, 163 loops, 168 variáveis Do While, 171, 172, 173 declarando, 164, 165, 166 For Each, 183 nomeando, 166 For-Next, 168, 169, 170, 171 variáveis alfanuméricas, 164 números de itens, fazendo loop variáveis numéricas, 164 com, 169, 170 variáveis variantes, 164 macros variáveis de objeto, 181 GetDataFromAccess, 163 configurando, 182 gravando, 177, 179 declarando, 182, 183 programa de gravação de objetos em loops For Each, macros, 167, 177, 178, 179, 18 3 18 0 VBE (Visual Basic Editor), 156 modelo de objeto, 156, 157 iniciando, 161 aprendendo nomes em, 179 janela Propriedades, 162 VBE (Visual Basic Editor), 156 módulos criando, 161, 162, 163 abrindo, 102 notação de ponto, 166, 167, 168 iniciando, 161 objetos janela Propriedades, 162 Verdadeiro/Falso, valores. adicionando, 154, 155 Consulte valores métodos, 156 propriedades, 156 booleanos perspectiva histórica, 155, 156 verificando projetos solicitações de usuário, 300
vírgula (,), 36 Visible, propriedade (planilhas), 162 Visual Basic Editor. Consulte VBE (Visual Basic Editor) Visual Basic for Applications. Consulte VBA visualizando relacionamentos, 224, 225 W
With, instrução, 294 aninhando, 175 blocos With-End With, 174, 17 5 vantagens de, 176, 177 wks, variável, 103, 104 Workbook_BeforeClose(), função, 317 Workspace, parâmetro (método OpenDatabase), 198 Wrkgadm.exe, 205, 206 X
xlSheetHidden, configuração