1. Programação: Módulos e Procedimentos

    Situações que envolvem uma sequência de operações (cálculos complexos ou validação de registos que dependem da informação de outros registos) requerem a elaboração de programas definidos pelo utilizador. O Access dispõe de uma linguagem de programação: O Visual Basic for Applications (VBA) que é comum a outras aplicações do mesmo fabricante.

     

    Para programarmos no Access usamos objectos Module (módulos). Na janela de cada módulo podemos definir procedimentos. Há dois tipos de procedimentos: sub-rotinas e funções. O exemplo a seguir contém a definição duma função que permite calcular a soma de dois números.

     

     

    A função soma admite dois parâmetros a e b que deverão ser números inteiros. A função retorna um número inteiro, que é a soma dos dois parâmetros. Por baixo da função soma está também definida a sub-rotina diz que escreve uma mensagem dada. Esta sub-rotina tem um argumento do tipo String (texto).

    Para executar a função vamos abrir a "Debug Window". Nesta janela podemos fazer testes aos procedimentos que definimos, entre outras coisas. Tendo um módulo aberto, podemos aceder à janela de debug a partir do menu View.

    Depois de aberta a janela de debug escrevemos "?soma(3, 4)". O Access vai chamar a função, e à variável que, na função, é designada por a, é atribuído o valor 3, à variável designada por b, o valor 4. A função retorna o valor 7 sendo este valor escrito na janela de debug.

     

    A sub-rotina pode ser executada escrevendo call diz("Olá Mundo"). O argumento está entre aspas para ser identificado como texto.

     

     

    1. Organização e Execução de Procedimentos

Cada módulo é constituído pela secção de declarações (mais adiante vamos ver o que são) e por um conjunto de procedimentos.

 

Qualquer procedimento, antes de poder ser utilizado tem que ser definido. A definição de um procedimento envolve:

 

  • Escolher o tipo de procedimento;
  • Atribuir um nome ao procedimento;
  • Definir a lista de parâmetros;
  • Definir o conjunto de instruções que efectuam as operações pretendidas.

 

 

Os procedimentos são de dois tipos, Funções e Subrotinas:

 

Funções

Retornam um valor e podem ser utilizadas em expressões em qualquer objecto da Base de Dados

Function nome(parâmetros)

[ instruções]

End Function

SubRotinas

 

Não retornam valores, e assim não podem ser utilizadas em expressões. Podem ser chamadas com Call <nome>

a partir doutros procedimentos.

Sub nome (parâmetros)

[ instruções]

End Sub

      1. Execução de Procedimentos

        As sub-rotinas podem ser chamadas a partir de qualquer procedimento, ou directamente a partir da janela de debug usando a instrução call. As funções podem ser utilizadas em qualquer expressão.

         

        Exemplo: A função soma pode ser usada num campo calculado de uma Query,:

         

         

         

        Uma outra possibilidade é utilizar a função num formulário. Para isso, constrói-se um formulário com três controlos do tipo "TextBox". Os primeiros dois serão utilizados para os valores de entrada da função, e o terceiro para o resultado. Estes controlos terão os nomes "x", "y" e "resultado" respectivamente. No controlo "resultado" a propriedade Control Source deve ter o seguinte valor:

         

        ControlSource: =soma([ x] ,[ y] )

         

         

        Uma função pode ser também usada por uma macro. Neste caso a caixa de texto "resultado" deveria ser "Unbound" e as caixas de texto "x" e "y" deveriam ter a propriedade:

         

        AfterUpdade: Macro1

         

        que dispara a macro (Macro1) que actualiza o valor de "resultado" utilizando a acção "SetValue" com os argumentos Item:[resultado] e Expression: soma([x],[y]).

         

         

      2. Procedimentos de Evento

A cada evento (click, update, etc) que possa suceder em cada objecto (formulários, controlos, relatórios, objectos definidos pelo utilizador, etc) está associada uma sub-rotina designada por event procedure (procedimento de evento). O programador pode alterar esta rotina mudando a sua definição. Em particular, esta rotina pode chamar outros procedimentos já definidos.

 

Exemplo: No formulário que nos mostra o resultado da função soma, podemos obter idêntica funcionalidade de forma diferente. Em vez de termos a propriedade ControlSource da caixa resultado com uma chamada à função soma, vamos usar um procedimento de evento.

 

Para isso, começamos por consultar as propriedades da caixa y. Na propriedade OnExit carregamos no botão com três pontos e escolhemos a opção CodeBuilder. aparece então um procedimento de evento com a definição vazia. O nome do procedimento é escolhido automaticamente a partir do nome do controlo (y) e do nome do evento (Exit). Agora basta acrescentar uma instrução de atribuição. Eis o procedimento de evento final:

 

Private Sub y_Exit(Cancel As Integer)

[resultado] = soma([x],[y])

End Sub

 

Nota: A funcionalidade do formulário não vai ser exactamente a mesma, pois o valor na caixa resultado só é alterado quando o cursor sai da caixa y. u

 

Os procedimentos de evento encontram-se em módulos especiais associados a objectos do Access, em vez de estarem nos módulos standard de que falamos acima. Para vermos/alterarmos procedimentos de evento associados, por exemplo, a um formulário, escolhemos a opção Code do menu View com o formulário em modo de construção (design).

 

 

    1. Algumas noções básicas
      1. Variáveis

Vejamos a seguinte sub-rotina de nome conta_até.

 

Sub conta_até(n As Integer)

Dim i As Integer

i = 1

Do While i <= n

Debug.Print i

i = i + 1

Loop

MsgBox "contei até " & n

End Sub

 

Esta sub-rotina, quando chamada com um argumento inteiro k, escreve na janela de debug todos os inteiros de 1 até k. Depois disso faz aparecer uma janela com a mensagem "contei até k". Nesta rotina é utilizada a variável i. Esta variável é, em primeiro lugar, declarada como inteira

Dim i As Integer

 

Depois é-lhe atribuído o valor 1 através de uma instrução de atribuição

i = 1

A seguir é executado um ciclo Do While-Loop que, para além de escrever o valor corrente da variável i, vai aumentar o seu valor em uma unidade. Isto é repetido enquanto o valor corrente de i for menor ou igual do que o k dado. No final i tem o valor k+1.

 

Este exemplo ilustra algumas das operações típicas com uma variável:

 

Apesar de se poderem utilizar variáveis sem as declarar, isso não é aconselhável. Declarar as variáveis permite, entre outras coisas, evitar erros de programação difíceis de detectar.

 

As declarações podem ser efectuadas na secção de declarações do módulo ou no início da definição de cada procedimento. Como adiante veremos, o local onde são feitas as declarações afecta a visibilidade das variáveis. Uma instrução de declaração típica tem o seguinte formato:

 

DIM <variável> [As <Tipo>]

 

onde "<tipo>" representa o tipo de variável. O Access permite declarar variáveis de vários tipos, incluindo:

 

Integer Números inteiros pequenos (de -32768 a 32767, 2 bytes)

Long Números inteiros maiores (de -2147483648 a 2147483647, 4 bytes)

Single Números reais pequenos

Double Números reais de maior precisão

Currency Números que representam valores monetários

String Texto

Variant Qualquer tipo

 

Não é aconselhável omitir declarações e/ou utilizar o tipo "Variant", pois essas podem constituir uma fonte de erros difíceis de detectar. Além da forma acima é possível declarar variáveis utilizando sufixos. Por exemplo, o símbolo "%" utilizado a seguir ao nome da variável indica que essa é de tipo "Integer", como em X% = 1. De maneira semelhante, o símbolo "$" indica que a variável é de tipo "String" etc.

 

 

      1. Vectores e Matrizes ("Arrays")

        O Access permite definir vectores ou matrizes, isto é, variáveis que podem conter um conjunto finito de valores que partilham o mesmo nome e o mesmo tipo de dados. Os diferentes elementos da matriz ("array") são acedidos por um índice que indica a ordem do elemento dentro da matriz. As matrizes têm de ser previamente declaradas com DIM:

         

        DIM notas(9) As Integer

        Define um vector com 10 elementos

        DIM notas(4,4) As Integer

        Define uma matriz de 5 colunas por 5 linhas

         

        No primeiro exemplo é criada uma variável notas do tipo inteiro com 10 elementos. A expressão "notas(0)" acede ao 1º elemento do array e "notas(1)" ao 2º.

        Visibilidade das Variáveis

        As variáveis podem ser declaradas quer no interior de um procedimento quer na secção de declaração dos módulos. A maneira como uma variável é definida determina a sua visibilidade isto é, a possibilidade de alterar o valor dessa variável ou de a utilizar num determinado ponto do programa. Às vezes, utiliza-se, neste contexto, também o termo âmbito ou "scope". Assim, e de acordo com a sua visibilidade, iremos ter três tipos de variáveis:

         

        Local: Variável declarada num procedimento. É visível apenas no procedimento onde foi declarada. Para os outros procedimentos, esta variável não existe. Podemos inclusivamente ter duas variáveis locais com o mesmo nome em procedimentos diferentes. Apesar de terem o mesmo nome são duas variáveis distintas.

         

        Módulo: Variável declarada com DIM na secção de declarações do módulo. É visível por todos os procedimentos desse módulo.

         

        Global: Variável declarada como Public na secção de declarações de um módulo. É visível por todos os procedimentos de qualquer módulo.

         

      2. Constantes

        Os valores 3.1415 ou "abc" representam constantes. Estes podem ser associados a nomes simbólicos, como por exemplo:

         

        CONST pi = 3.1415

         

        As constantes True, Yes e ON são predefinidas e têm o valor -1. De maneira semelhante, as constantes False, No, Off têm o valor 0. As constantes podem ter diferentes visibilidades de forma idêntica à das variáveis.

         

      3. Programação Orientada por Objectos

Um objecto representa um elemento do Access. Por exemplo, uma tabela, um formulário, um controlo ou uma query são objectos. A linguagem de programação VBA permite manipular os objectos do Access.

 

Cada objecto dispõe de um conjunto de propriedades e de métodos. Uma propriedade de uma tabela é por exemplo o seu nome. Um método representa uma acção que podemos realizar sobre o objecto e é utilizado de forma idêntica a uma sub-rotina. Podemos, por exemplo, ter um método Delete para apagar um registo de uma tabela.

 

Para nos referirmos à propriedade prop de um objecto obj usamos a sintaxe:

 

obj.prop

 

De forma idêntica, para executarmos o método mtd de um objecto obj usamos a expressão

 

obj.mtd

 

Uma colecção contém um conjunto de objectos. Por exemplo a colecção Forms contém o conjunto dos formulários definidos na base de dados. Para nos referirmos a um elemento obj de uma colecção col usamos a sintaxe

 

col!obj

 

Exemplo: Suponhamos que dentro de um procedimento necessitamos de alterar a título de um formulário form7 para "Novo". Para isso usamos a instrução de atribuição

 

Forms![form7].Caption = "Novo"

 

Ou seja, a propriedade Caption do objecto form7 pertencente à colecção Forms passa a ter valor "Novo".

 

    1. Instruções de Programação

      Um procedimento é definido por um conjunto de instruções, que são executadas sequencialmente. Vejamos os tipos de intruções mais importantes.

       

      1. Instrução de atribuição

        Em geral a operação de atribuição tem a forma:

         

        <Variável> = <Expressão>

         

        Numa instrução deste tipo, a expressão do lado direito do símbolo "="é avaliada e o resultado passa a ser o valor actual da variável cujo nome aparece no lado esquerdo. Se a variável já tiver um valor, este é substituído pelo resultado da expressão (o anterior valor é perdido).

         

      2. Controlo de Execução

        Por vezes é conveniente alterar o modo de execução sequencial. Assim, poderemos ter situações em que um conjunto de instruções só é executado se determinada condição for verificada. Outras vezes teremos situações em que se pretende executar repetidas vezes o mesmo grupo de instruções.

        O VBA, tal como grande parte das linguagens de programação ditas imperativas, contém estruturas de controlo que possibilitam determinar a sequência de instruções a ser executada. Iremos ver estruturas de decisão que permitem executar condicionalmente um conjunto de instruções e estruturas iterativas que permitem executar um conjunto de instruções de maneira repetida.

         

      3. Estruturas de decisão

         

        If <condição> Then <Instrução>

         

        ou

         

        If <condição> Then

        <bloco de instruções>

        End If

         

        If <condição> Then

        <bloco de instruções>

        Else

        <bloco de instruções>

        End If

         

         

        Exemplos:

         

        Function maior2(a As Integer, b As Integer)

        ‘Retorna o maior de 2 números

        If a > b Then

        maior2 = a

        Else

        maior2 = b

        End If

        End Function

         

        Function maior3(a,b,c)

        ‘Retorna o maior de 3 numeros

        If a > b Then

        If a > c Then

        maior3 = a

        Else

        maior3 = c

        End If

        Else

        If b > c Then

        maior3 = b

        Else

        maior3 = c

        End If

        End If

        End Function

         

      4. Estrutura Selectiva

        A estrutura selectiva permite seleccionar uma de várias possibilidades. Se pretender escolher uma de várias alternativas, pode utilizar as estruturas If-Else. No entanto, a estrutura "Select Case" que, por ser mais compreensível, é mais aconselhável.

         

         

        Select Case <expressão>

        Case <valor1>: <bloco de instruções 1>

        Case <valor2>: <bloco de instruções 2>

        ...

        [Case Else: <bloco de instruções N>]

        End Select

         

         

        No fluxo do programa, ao encontrar uma estrutura "Case", a expressão do "Select Case" é avaliada. O resultado obtido é comparado com o valor associado aos sucessivos casos. É executado o bloco de instruções do primeiro caso com valor associado igual ao resultado da expressão e o fluxo do programa continua na instrução imediatamente a seguir ao "End Select" (O fluxo é representado pelos diagramas do lado direito das tabelas.)

         

        Se nenhum caso é satisfeito e se houver um "Case Else" (que deverá ser o ultimo a ser declarado), então o bloco de instruções associado este caso é executado. Se nenhum caso é satisfeito e se não existir um "Case Else", nenhum dos blocos de instruções é executado e o fluxo do programa continua na instrução imediatamente a seguir ao "End Select".

         

        Exemplos:

         

        Function dia (D)

        Select Case WeekDay(D)

        Case 1: dia = "Domingo"

        Case 2: dia = "Segunda"

        Case 3: dia = "Terca"

        Case 4: dia = "Quarta"

        Case 5: dia = "Quinta"

        Case 6: dia = "Sexta"

        Case 7: dia = "Sabado"

        End Select

        End Function

        Function desconto(valor)

        Select Case valor

        Case Is < 4

        desconto = 0

        Case Is < 8

        desconto = 0.1

        Case Else

        desconto = 0.15

        End Select

        End Function

         

         

        Para testar as funções, execute na janela "Debug Window":

         

        ?dia(#1-1-95#)

        Domingo

        ?desconto(10)

        0.15

         

      5. Estruturas Iterativas

      A execução repetida de um conjunto de instruções pode ser feita pelas estruturas:

       

       

       

      Do While <condição>

      <bloco de instruções>

      Loop

       

      Enquanto a <condição> for verificada executa o bloco de instruções

       

      Do Until <condição>

      <bloco de instruções>

      Loop

       

      Até a <condição> ser verificada executa o bloco de instruções

       

       

      Ao encontrar uma estrutura "Do While", a condição associada é avaliada. Se a condição for verdadeira o bloco de instruções é executado sequencialmente. Ao encontrar a palavra "Loop", que indica o fim do conjunto de instruções a serem repetidas, o fluxo do programa passa novamente para a instrução "Do While" e a condição é novamente avaliada. Se a condição for verdadeira, o bloco de instruções é executado mais uma vez, continuando assim enquanto a condição for verdadeira.

      Para a estrutura "Do Until" o funcionamento é semelhante, apenas que, o bloco de instruções é executado enquanto a condição for falsa.

       

      Na implementação de estruturas iterativas, deve ter em atenção duas situações:

      - os ciclos que nunca começam (no caso do DoWhile a condição é inicialmente falsa),

      - os ciclos que nunca acabam (no caso do Do While a condição nunca se torna falsa).

      Se o segundo caso acontecer, o terminal ficará bloqueado, pois o programa entrou em "ciclo infinito". Este pode ser interrompido com as teclas Control + Break.

       

      Para evitar ciclos infinitos, deve prestar atenção a condição associada ao ciclo. A condição determina quando este começa ou acaba. Em geral, o valor destas variáveis deve ser alterado no bloco de instruções que constituem o ciclo.

       

      Exemplos de Programas

       

      Tabuada

      Function tabuada(x As Integer)

      Dim y As Integer

      Debug.Print "Tabuada dos..", x

      y = 1

      Do While y < 10

      Debug.Print x,"*",y,"=",x*y

      y = y + 1

      Loop

      End Function

       

      Máximo Divisor ComumFunction mdc(x As Integer,y As Integer)

      Do While x <> y

      If x > y Then

      x = x - y

      Else

      y = y - x

      End If

      Loop

      mdc = x

      End Function

      A função mdc(x,y) calcula o máximo divisor comum de dois números, utilizando o algoritmo de Euclides:

       

      Enquanto x for diferente de y, faz:

      Se x maior que y

      x = x - y

      Senão

      y = y - x

       

      Inverte Uma String

       

      Function inverte(s As String)As String

      DIM n$, i As Integer

      i = Len(s)

      Do Until i = 0

      n$ = n$ & Mid$(s,i,1)

      i = i - 1

      Loop

      inverte = n$

      End Function

      A função inverte(s)escreve a cadeia de caracteres s de trás para a frente.

      A função utiliza duas variáveis auxiliares n$ e i. A variável n$ vai acumulando o resultado parcial na execução do ciclo, enquanto i indica o próximo caracter a ser passado de s para n$.

       

      A função inverte("abc") retorna "cba".

      Os sucessivos valores das variáveis são:

      n$ i Mid$(s,i,1)

      "" 3 Mid$("abc",3,1)="c"

      "c" 2 Mid$("abc",2,1)="b"

      "cb" 1 Mid$("abc",1,1)="a"

      "cba" 0

       

       

      Uma outra estrutura iterativa (vocacionada para processar "arrays"), é a estrutura "For". A sintaxe é a seguinte:

       

      For contador = valor_inicial To valor_final [ Step incremento]

      <bloco de instruções>

      Next

       

      Quando no fluxo do programa é encontrado um ciclo For, à variável declarada como contador é atribuído o valor inicial. Enquanto o valor do contador for menor que o valor final o bloco de instruções entre o For e o Next é executado. Quando encontrar a linha Next, à variável contador é acrescentado o valor do incremento a seguir de Step. Note que o incremento associado ao Step é opcional, e se não for declarado, é assumido o valor 1.

       

      Exemplo:

      Function iniciar(n(), max As Integer)

      Dim i As Integer

      For i = 0 To max

      n(i) = Int(Rnd * 10)

      Next

      End Function

      Na secção de declarações:

      Dim n(9)

       

       

       

      O funcionamento da função "iniciar" pode ser descrito como:

      Á variável i, utilizada como contador é atribuido o valor inicial 0.

      Enquanto o valor de i for menor que o valor de max executa:

      atribui ao i-ésimo elemento do array o valor da expressão Int(Rnd * 10);

      incrementa o valor de i (i = i + 1).

       

       

      De uma forma geral, um ciclo "For" pode ser simulado utilizando um ciclo "Do While", tal como é exemplificado:

       

      contador = Valor_inicial

      Do while contador < valor_final

      <bloco de instruções>

      contador = contador + incremento

      Loop

       

       

    2. Interacção com o Utilizador

      A comunicação com o utilizador pode ser efectuada utilizando as funções MsgBox(..) e InputBox(..).

       

      MsgBox(mensagem [, tipo [, título] ] ) mostra a mensagem numa caixa de diálogo e espera que o utilizador escolha um dos botões disponíveis. A função retorna um valor indicando qual o botão escolhido.

       

      O segundo argumento, "tipo", permite escolher quer os botões quer o ícone que a caixa de diálogo apresenta. No exemplo foi escolhido 68. Consulte "Help" "MsgBox function" para outros valores, assim como para o significado dos valores que a função retorna.

       

      InputBox(mensagem, [título], [Valor_por_defeito]) mostra uma caixa de diálogo e espera que o utilizador escreve um valor e/ou escolha um botão. A função retorna o valor digitado (tipo "Variant").

       

      nota = InputBox("Qual a nota ?")

       

       

    3. Operações sobre Bases de Dados
      1. Operações com Tabelas

        Todos os objectos criados pelo utilizador no Access podem ser criados e manipulados em VBA. Nesta secção consideramos objectos de tipo "DataBase" (Base de Dados) e "Recordset" (conjunto de registos).

        Quando se pretende aceder directamente à informação das tabelas, é necessário declarar as variáveis correspondentes.

         

        Isso pode ser feito, por exemplo, na secção de declarações do módulo de seguinte maneira:

         

        Dim db As DataBase

        Dim tb As Recordset

         

        onde "db" e "tb" são duas variáveis que escolhemos aqui (poderíamos ter usado outras). Estas variáveis vão-nos permitir referirmo-nos a uma base de dados e a um conjunto de registos (por exemplo uma tabela) a partir de um programa.

         

        A tabela pode ser aberta dentro da janela "Debug Window" utilizando as seguintes instruções:

         

        Set db = CurrentDb()

        Set tb = db.OpenRecordset("<tabela>")

         

        onde <tabela> representa o nome da tabela escolhida. Assim, se quisermos abrir a tabela "alunos", a segunda instrução seria:

         

        Set tb = db.OpenRecordset("alunos")

         

        A função CurrentDb() permite aceder à base de dados corrente. A função OpenDatabase(..)permite aceder a uma Base de Dados específica. As variáveis "db" e "tb" representam referências (apontadores) para os respectivos valores. A atribuição da referência à variável é feita utilizando a instrução Set .

         

        Após a execução das instruções acima, a variável tb aponta para o 1º registo da tabela indicada. É possível aceder a esta informação, executando na "Debug Window", o comando:

         

        ?tb![ <campo>]

         

        que permite visualizar o valor do primeiro registo do campo indicado. Assim, para visualizar, por exemplo, o campo "nome", a instrução seria:

         

        ?tb![ nome]

         

        Após serem efectuadas as operações pretendidas, é aconselhável "fechar" a tabela utilizando o comando:

        tb.Close

         

      2. Movimentação na Tabela

        Abrir uma tabela permite aceder à informação nela contida. Em cada instante só é possível aceder à informação de um registo designado por registo corrente (rc). Quando a tabela é aberta o registo corrente é o 1º registo da tabela. O comando tb.MoveNext permite tornar o seguinte registo corrente:

         

        tb.MoveNext

        torna corrente o registo seguinte. Se o rc é o ultimo, MoveNext atribui à propriedade EOF (End of File) o valor TRUE e rc fica indefinido.

        tb.MovePrevious

        torna corrente o registo anterior. Se o rc é o primeiro, MovePrevious atribui à propriedade BOF (Begin of File) o valor TRUE e rc fica indefinido.

        As seguintes instruções podem ser utilizados para "saltar" para o início (ou fim) da tabela:

         

        tb.MoveFirst

        torna corrente o 1º registo

        tb.MoveLast

        torna corrente o ultimo registo

         

        Considere o seguinte procedimento que permite mostrar o conteúdo da tabela "alunos":

         

        Sub percorre()

        alunos.MoveFirst

        Do Until alunos.Eof

        Debug.Print alunos![ nome]

        alunos.MoveNext

        Loop

        End Sub

         

        Posiciona no 1º registo

        Processa toda a tabela

        Escreve o conteúdo do campo "nome"

        Salta para o próximo registo

         

         

        Para utilizar o procedimento, devemos antes acrescentar nas declarações do módulo:

        Dim db as DataBase

        Dim alunos as Recordset

         

        Na "Debug Window" temos de abrir a base de dados assim como a tabela. Note-se que estas duas intruções set poderiam estar também, por exemplo, dentro da definição da sub-rotina percorre.

        Set Db = Currentdb()

        Set alunos = db.OpenRecordset("alunos")

         

        Agora chamamos a rotina na "Debug Window".

        call percorre

         

        Como resultado da execução, irão aparecer os nomes dos alunos escritos na janela de debug.

         

        Exemplo: Podemos agora partir da sub-rotina percorre para obter uma função que conta o número de registos de uma tabela dada como argumento. Aqui decidimos fazer as declarações dentro da própria função.

         

        Function conta(tabela As String) As Integer

        Dim db As Database, rs As Recordset

        Dim n As Integer

        Set db = CurrentDb()

        Set rs = db.OpenRecordset(tabela)

        n = 0

        rs.MoveFirst

        Do Until rs.EOF

        n = n + 1

        rs.MoveNext

        Loop

        conta = n

        rs.Close

        End Function

         

        O argumento da função é uma string com o nome da tabela da qual queremos contar os registos. A variável n é uma variável contador, que começa por ter o valor 0 sendo incrementada à passagem de cada novo registo. No final o valor de n é retornado.

         

        Para contarmos o número de registos da tabela alunos invocamoes a função na janela de debug:

         

        ?conta("alunos")

        14

         

        Nota: O objecto recordset tem uma propriedade RecordCount que guarda o número de registos. Por isso, poderíamos ter definido a função utilizando a instrução:

         

        conta = rs.RecordCount

         

        Que linhas poderíamos apagar?

         

      3. Procura de Registos que Satisfazem Determinada Condição

        Procurar os registos que satisfazem determinada condição, é das operações mais frequentes na manutenção de Bases de Dados. As instruções que permitem efectuar esta operação são:

         

        <tabela>.FindFirst <critério>

        Localiza o 1º registo que satisfaz o critério

        <tabela>.FindLast <critério>

        Localiza o último registo que satisfaz o critério

        <tabela>.FindNext <critério>

        Localiza o próximo registo que satisfaz o critério

        <tabela>.FindPrevious <critério>

        Localiza o registo anterior que satisfaz o critério

         

        O critério é uma expressão do tipo "String" que contém uma condição lógica envolvendo os nomes dos campos do conjunto de registos.

         

        Exemplo:

        A sub-rotina localiza mostra na "Debug Window" o nome de todos os alunos que satisfazem um dado critério:

         

        Sub localiza ()

        alunos.FindFirst "[ turma] = ‘TP01’"

        Do Until alunos.NoMatch

        Debug.Print alunos![ nome]

        alunos.FindNext "[ turma] = ‘TP01’"

        Loop

        End Sub

         

        Execução na Debug Window:

         

        Set db = CurrentDb()

        Set alunos = db.OpenRecordset("alunos", dbOpenDynaset)

        call localiza

         

        Note que a tabela foi aberta dando mais um argumento a OpenRecordset. Este argumento extra (dbOpenDynaset) indica ao Access que queremos abrir a tabela como um conjunto de registos dinâmico. Isto é necessário para podermos usar os métodos FindFirst, FindNext, etc.

        À propriedade NoMatch é atribuido o valor TRUE quando a instrução (FindNext etc.) falha na localização do registo.

        A instrução FindFirst (FindNext etc.) percorre a tabela virtual registo a registo. Para cada registo testa a condição e torna esse registo corrente se o registo satisfaz a condição.

         

        A versão da rotina localiza apresentada a seguir permite passar o critério através de um parâmetro.

         

        Sub localiza (criterio$)

        alunos.FindFirst criterio$

        Do Until alunos.NoMatch

        Debug.Print alunos![ nome]

        alunos.FindNext criterio$

        Loop

        End Sub

         

        Execução na janela de debug:

         

        Set db = CurrentDb()

        Set alunos = db.OpenRecordset("alunos", dbOpenDynaset)

        call localiza("[turma] = ‘TP01’")

         

        Exemplo: Suponhamos que temos na base de dados corrente uma tabela "Encomendas" com vários campos, entre os quais "Cliente" e "Valor". Vamos definir uma função que indique o total de encomendas, em valor, para um dado cliente.

         

        Function soma_encomendas(Cliente As Long) As Single

        Dim db As Database

        Dim rs As Recordset

        Dim soma As Single

        Dim criterio As String

         

        Set db = CurrentDb

        Set rs = db.OpenRecordset("Encomendas", dbOpenDynaset)

         

        criterio = "[Cliente]=" & Cliente

        soma = 0

        rs.FindFirst criterio

        Do Until rs.NoMatch

        soma = soma + rs![Valor]

        rs.FindNext criterio

        Loop

        soma_encomendas = soma

        End Function

         

        A variável soma tem o papel de acumulador. Para os seguintes valores da tabela encomendas:

         

        Cliente

        Valor

        1

        3200

        1

        4600

        2

        1800

        3

        900

        7

        4500

        2

        1500

        1

        2100

        2

        1000

         

        Temos o seguinte resultado

         

        ?soma_encomendas(1)

        9900

         

      4. Actualização de Dados em Tabelas

        As operações com objectos de bases de dados que temos visto, incidiram sobre o acesso à informação. Muitas vezes há necessidade de editar essa informação. Nesta secção iremos ver como proceder para alterar o valor de campos, eliminar registos e inserir novos registos numa tabela.

         

        Editar Informação

         

        Alterar o valor de campos num registo envolve três passos:

        1) Utilizar a instrução EDIT para preparar o registo;

        2) Atribuir aos campos os valores pretendidos;

        3) Utilizar a instrução UPDATE para efectivar as alterações.

         

        Exemplo:

        Sub altera (xbi, xnota)

        alunos.FindFirst "[NumBI]=" & xbi

        If Not alunos.NoMatch Then

        alunos.Edit

        alunos![ nota] = xnota

        alunos.Update

        End If

        End Sub

         

        Eliminar Registos

         

        Para eliminar um registo completo, é utilizada a instrução Delete.

        A rotina elimina(bi) procura o aluno cujo número é "xbi" e retira este caso da tabela:

         

        Sub elimina (xbi)

        alunos.FindFirst "[NumBI]=" & xbi

        If Not alunos.NoMatch Then

        alunos.Delete

        End If

        End Sub

         

         

        A rotina elim_todos() apaga todos os registos de uma tabela:

         

        Sub elim_todos ()

        alunos.MoveFirst

        Do Until alunos.EOF

        alunos.Delete

        alunos.MoveNext

        Loop

        End Sub

         

        Inserir novos registos

        Inserir novos registos numa tabela envolve três passos:

        1) Utilizar o método AddNew para criar o registo;

        2) Atribuir aos campos os valores pretendidos;

        3) Utilizar o método Update para efectivar o novo registo.

        Exemplo:

        Sub inserir (xbi, xnome, xnota)

        alunos.AddNew

        alunos![ bi] = xbi

        alunos![ nome] = xnome

        alunos![ nota] = xnota

        alunos.Update

        End Sub

         

      5. Procedimentos Associados a Controlos

       

      Os procedimentos podem ser associados a controlos de formulários. A função pode ser utilizada para validar, por exemplo, o número de bilhete de identidade digitado na "Text Box". Se o nome desse controlo for "bi", e a função tiver o nome "valida", a condição para validação seria "=valida(bi)". Além disso, é necessário declarar que função deve ser chamada pelo evento "OnUpdate". O parâmetro passado à função de validação tem que ser de tipo "Control":

       

      Function valida (bi As Control)

      ...

      End Function

       

    4. Exemplos de Programas

       

      1. Máximo Divisor Comum de dois Números

         

        Versão Recursiva

         

        Function mdc(x As Integer,y As Integer)

        If x > y Then mdc = mdc(x-y, y)

        If y > x Then mdc = mdc(x, y-x)

        If x = y Then mdc = x

        End Function

         

      2. Resto da divisão inteira de dois números

        Versão iterativa

         

        Versão Recursiva

        Function modulo (a, b)

        Do While b >= a

        b = b - a

        Loop

        modulo = b

        End Function

         

        Function modulo (a, b)

        If a > b Then modulo = b

        If a <= b Then modulo = modulo(a, b - a)

        End Function

         

      3. Factorial de X

        A função factorial pode ser calculada de duas maneiras:

        Método Iterativo Método Recursivo

        X! = X * (X-1) * (X-2) * ... * 3 * 2 * 1 0! = 1

        X! = X * (X - 1)!

         

        São apresentadas duas funções, que implementam os métodos apresentados:

        Versão iterativa

         

        Versão Recursiva

        Function facti (x)

        resultado = 1

        Do While x > 1

        resultado = x * resultado

        x = x -1

        Loop

        facti = resultado

        End Function

        Function factr (x)

        If x = 0 Then

        factr = 1

        Else

        factr = x * factr(x - 1)

        End If

        End Function

         

         

        Trace para facti(4)

        Trace para factr(4)

        x resultado x > 1

        4 1 TRUE

        3 4 TRUE

        2 12 TRUE

        1 24 FALSE

        x fact(x)

        4 fact(4) = 4 * fact(3)

        3 fact(3) = 3 * fact(2)

        2 fact(2) = 2 * fact(1)

        1 fact(1) = 1 * fact(0)

        0 fact(0) = 1

        1 fact(1) = 1 * 1

        2 fact(2) = 2 * 1

        3 fact(3) = 3 * 2

        4 fact(4) = 4 * 6

         

      4. Cálculo do valor de ex

        O valor da função ex pode ser expandida em série:

        ex=å xn/n!

        ex=1+x/1+x2/2!+x3/3!+...

        ex=1+x/1+(x/1)*(x/2)+(x/1)*(x/2)*(x/3)+...

         

        Function e(x)

        n = 1

        t = 1

        y = 1

        delta = .0001

        Do While t / y > delta

        t = t * x / n

        y = y + t

        n = n + 1

        Loop

        e = y

        End Function

         

        ‘Nr. de termos

        ‘Valor de ex

        ‘Valor do termo n

        ‘Erro aceitável

        ‘Enquanto o erro for maior que o erro `aceitável

        ‘Calcula o novo termo

        ‘acumula o resultado

        ‘termo seguinte

         

      5. Processar Arrays

        Option Compare Database

        Dim n(9)

         

        Function mostra (n(), max)

        For i = 0 To max

        Debug.Print n(i)

        Next

        End Function

         

        Function preenche (n(), max)

        For i = 0 To max

        n(i) = Int(Rnd * 10)

        Next

        End Function

         

        Function ordenar (a(), max)

        fim = False

        Do While Not fim

        fim = True

        For i = 0 To max - 1

        If a(i) > a(i + 1) Then

        v = a(i)

        a(i) = a(i + 1)

        a(i + 1) = v

        fim = False

        End If

        Next

        Loop

        End Function

         

         

         

        A operação de ordenar um conjunto de valores é frequentemente utilizada em aplicações. Existem vários algoritmos que efectuam essa operação. O algoritmo utilizado na função ordenar(), é conhecido por "Bubble Sort". Pode ser descrito como:

         

        Repetir enquanto houver trocas

        Percorrer todo o vector.

        Cada elemento i do vector é comparado com o elemento seguinte i+1

        se a(i) > a(i+1) os elementos são trocados

         

        Supondo que o vector inicial contem a sequência (3,4,1,2), os sucessivos valores do vector são:

        (3,4,1,2) -> (3,1,4,2) -> (3,1,2,4) -> (1,3,2,4) -> (1,2,3,4)

         

        Quando atingir (1,2,3,4) não haverá mais trocas, pelo que o algoritmo termina.

         

      6. Procura binária

      Pense num número entre 0 e 100. A função descobre() vai descobrir o número por sucessivas tentativas. Em cada tentativa escolhe um valor e pergunta ao utilizador se o número em causa é maior ou menor que o valor escolhido. Perante a resposta do utilizador, a função reduz o espaço de possibilidades, pelo que rapidamente encontra o número a descobrir.

       

      A função utiliza duas variáveis auxiliares min e max que contêm, respectivamente, o menor e o maior valores possíveis. Inicialmente o valor destas variáveis é 0 para min e 100 para max. Enquanto não descobre o número, a função tenta um valor x, valor médio do intervalo definido por [ min;max] . Este valor é utilizado na pergunta ao utilizador. Se a resposta do utilizador for "Yes" o intervalo de valores possíveis passa a ser [ x+1; max] , caso contrário [ min; x] . Quando min é igual a max, foi encontrado o número, pelo que a função termina.

       

      Function descobre ()

      Dim x As Integer, min As Integer, max As Integer

      min = 0

      max = 100

      Do While min <> max

      x = min + Int((max - min) / 2)

      Select Case MsgBox("O nr é maior que " & x & "?", 36)

      Case 6: min = x + 1 'Resposta Yes

      Case 7: max = x 'Resposta No

      End Select

      Loop

      MsgBox ("Descobri ! É:" & min)

      End Function

       

      Para o intervalo de 0 a 100 são necessários, no máximo, log2(100) » 7 perguntas. Para o intervalo de 0 a 1000 são necessárias, no máximo, log2(1000) » 10 perguntas.

       

    5. Tópicos Avançados
      1. Procura em Tabelas Utilizando Índices

        Nesta secção falamos do método Seek, que permite fazer alguns tipos de procura de registos. Em comparação com os métodos Find já descritos, o método Seek é menos poderoso (não pode usar critérios arbitrários) mas tem tendência a ser mais eficiente.

         

        A função percorre() permite apresentar os dados da tabela numa determinada ordem. Essa, em geral é determinada pela chave primária ("Primary Key") associada com a tabela. No entanto, o utilizador pode pretender ordenar os dados de outra maneira. Como podemos então ver os registos ordenados pelo valor crescente de um outro campo?

         

        Em primeiro lugar, é necessário definir as chaves, criando um ou mais índices aquando da definição da estrutura da tabela, atribuindo à propriedade "Index" do respectivo campo o valor "Yes".

        Cada índice permite efectuar as operações de procura de uma forma bem mais eficiente e rápida, devendo ser utilizado quando as operações de procura são feitas frequentemente.

         

        O índice pode ser activado com a instrução:

         

        <tabela>.Index = "<campo>"

         

        onde "<campo>" identifica a chave associada com o campo. Se, por exemplo, estivesse criado o índice para o campo "turma" da tabela "tb", poderá utilizar a seguinte instrução para activá-lo.

        tb.Index = "turma"

         

        Os registos da tabela serão ordenados pelo valor crescente da chave, utilizando o respectivo índice na ordenação. A chave primária ("Primary Key") pode ser activada simplesmente através de:

        <tabela>.Index = "PrimaryKey"

         

        Assim, se préviamente à chamada da função percorre(), for estabelecido um índice, a função irá mostrar os registos ordenados pela chave escolhida.

         

        Exemplo:

        Na "Debug Window":

        Set Db = Currentdb()

        Set alunos = db.OpenRecordset("alunos")

         

        alunos.Index = "PrimaryKey"

        ?percorre() (Mostra os alunos ordenados por nome)

         

        alunos.index = "Turma"

        ?percorre() (Mostra os alunos ordenados por turma)

         

        A instrução SEEK permite localizar um dado valor, procurando a informação utilizando o índice corrente. O primeiro parâmetro define o tipo de comparação pretendida que pode ser por exemplo "=" (encontra o valor igual). O parâmetro seguinte indica o valor do campo que que será utilizado na procura (ex. "tp01").

         

        Antes de utilizar esta instrução é necessário activar o índice pretendido, relacionado com o valor utilizado na procura. Se por exemplo, pretende procurar o valor de uma turma, deve activar o índice "turma":

        Set db = CurrentDb()

        Set alunos = db.OpenRecordset("alunos")

        alunos.index = "turma"

        (Este passo pressupõe que na definição da tabela "alunos", nas propriedades do campo "turma" foi declarado "Index : Yes (duplicates OK)" ).

         

        O seguinte exemplo mostra a definição da função percorre(..) que inclui a instrução alunos.Seek "=", turma$. Se a função for chamada na janela "Debug Window", com ?encontra("tp01"), o parametro turma$ teria o valor ("tp01"), e assim, o sistema mostraria os nomes dos alunos desta turma no écran.

         

        Function encontra (xturma$)

        alunos.Seek "=", xturma$

        Do While alunos![ turma] = xturma$

        Debug.Print alunos![ nome]

        alunos.MoveNext

        Loop

        End Function

         

      2. Execução de Acções de Macros a partir de Procedimentos

        A maior parte das acções disponíveis nas macros, são possíveis de executar em procedimentos, utilizando o comando DoCmd. A possibilidade de passar parâmetros e controlar erros, são as vantagens de procedimentos por oposição às macros. A sintaxe do comando é:

        DoCmd acção [ argumentos]

         

        onde acção é uma das acções disponíveis nas macros e argumentos é a lista de argumentos (separados por vírgulas) que a acção requer. Tal como nas macros, o utilizador poderá não definir todos os argumentos de uma acção; o Access assume o valor por omissão para esse argumento, mas deverá colocar as vírgulas correspondentes.

         

        Exemplos:

        Posiciona no primeiro registo

         

        Posiciona no ultimo registo

        Sub ultimo ()

        DoCmd GoToRecord , , A_LAST

        End Sub

        Sub primeiro ()

        DoCmd GoToRecord , , A_FIRST

        End Sub

         

        A_Last e A_First são constantes pré-definidas no Access.

         

      3. Controlo de Erros

Situações em que a execução de uma acção dá origem a erro não são controláveis em "Macros", mas são controláveis no VBA. As situações de erro surgem, por exemplo, quando se tenta posicionar num registo que não existe ou executar uma opção de um menu que não está disponível ou abrir uma tabela que foi aberta em modo exclusivo por outro utilizador, etc.

 

Exemplo:

Num formulário, pretende um botão de comando que torna corrente o registo seguinte. O procedimento "seguinte()" associado à propriedade "OnClick" do botão, efectua a operação desejada:

 

Sub seguinte ()

DoCmd GoToRecord , , A_NEXT

End Sub

 

Este procedimento dá origem a erro, se for executado quando está no fim da fonte de registos.

Sub seguinte ()

On Error GoTo seguinte_erro

DoCmd GoToRecord , , A_NEXT

seguinte_exit:

Exit Sub

seguinte_erro:

MsgBox "Registo inexistente"

Resume seguinte_exit

End Sub

 

A linha "On Error" indica ao Access o que fazer, no caso de ocorrer um erro, quando o procedimento é executado. Neste caso indica "GoTo seguinte_erro", ou seja, no caso de erro continua a execução do procedimento na instrucção imediatamente a seguir à etiqueta "seguinte_erro".

 

 

 

Procedimento para eliminar o registo corrente num formulário:

 

Sub elimina ()

On Error GoTo elimina_erro

DoCmd DoMenuItem A_FORMBAR,A_EDITMENU,A_SELECTRECORD_V2,,A_MENU_VER20

DoCmd DoMenuItem A_FORMBAR, A_EDITMENU, A_DELETE_V2, , A_MENU_VER20

elimina_exit:

Exit Sub

elimina_erro:

MsgBox "Registo inexistente"

Resume elimina_exit

Hosted by www.Geocities.ws

1