Programação de Banco de Dados
Programação de Banco de Dados
Programação de Banco de Dados
Este é o primeiro de uma série de artigos relacionado com programação de Banco de Dados. Nos artigos
iniciais o enfoque é explicar as tecnologias relacionadas ao acesso a dados voltado para os programadores
que não estão acostumados com várias siglas como: OLE DB, ODBC, ADO , com termos como: duas
camadas, três camadas.
Conceitos Iniciais
» Introdução
Um banco de dados é usado para armazenar informações estruturadas e organizadas de forma a permitir
sua recuperação rápida e eficiente.Existem diversos Modelos de banco de dados como: Em Rede,
hierárquico, Relacional e Orientado a Objeto.
Os Modelos em Rede e Hierárquico no momento não são mais utilizados, somente em projetos antigos
você ainda encontra esses modelos, nesse artigo vamos focar o Modelo Relacional que é o modelo usado
no momento. O modelo Orientado a Objeto ainda em estudo e com certeza o modelo do futuro (no último
artigo falarei um pouco desse modelo).
No Modelo Relacional a informação é dividida em tabelas, e cada tabela representa entidades, desta forma
dividimos as informações em porções onde as entidades se relacionam.As tabelas possuem atributos
(campos) que são as colunas, as linhas são os registros (dados).Os relacionamentos permitem que os
usuários combinem informações de várias tabelas através de chaves primárias e chaves estrangeiras ou
secundárias.
O SGBD (Sistema Gerenciador de Banco de Dados) é responsável em manter a integridades dos dados
onde o programador pode definir algumas regras outras possui definição default.Os SGBD tem sete
características operacionais elementares sempre observadas, que passaremos a listar:
Característica 2: Compartilhamento dos Dados - O SGBD deve incluir software de controle de concorrência
ao acesso dos dados, garantindo em qualquer tipo de situação a escrita/leitura de dados sem erros.
Característica 3: Controle de Acesso - O SGDB deve dispor de recursos que possibilitem selecionar a
autoridade de cada usuário. Assim um usuário poderá realizar qualquer tipo de acesso, outros poderão ler
alguns dados e atualizar outros e outros ainda poderão somente acessar um conjunto restrito de dados
para escrita e leitura.
Característica 6: Controle de Integridade - Um Banco de Dados deverá impedir que aplicações ou acessos
pelas interfaces possam comprometer a integridade dos dados.
Característica 7: Backups - O SGBD deverá apresentar facilidade para recuperar falhas de hardware e
software, através da existência de arquivos de "pré-imagem" ou de outros recursos automáticos, exigindo
minimamente a intervenção de pessoal técnico.
OBS: O SQL é uma linguagem universal de definição de base de dados e manipulação de dados. SQL
Server , MySql são SGBD.
O OLE DB é uma camada que está situada no topo do banco de dados. O ADO, por sua vez, está no topo
do OLE DB e oferece uma visão simplificada do banco de dados. Como cada BD expõe sua funcionalidade
com uma série de funções próprias API (Application Programming Interface) para acessar cada BD pela
interface nativa você tem de aprender características do BD (baixo nível).Para desenvolver aplicativos que
conversem com dois bancos de dados diferentes (SQL Server e Oracle, por exemplo) você teria que
conhecer duas API's diferentes e suas peculiaridades, a não ser que você use o OLE DB e o ADO.
O OLE DB oferece uma visão unificada de diferentes fornecedores de dados.Cada BD possui sua própria
série de fornecedores de serviços OLE DB, que fornecem uma visão uniforme do BD. O ADO esconde as
peculiaridades de cada BD e fornece aos desenvolvedores uma visão conceitual simples do BD em uso.
A diferença entre ADO e OLE DB é que o OLE DB permite que você tenha maior controle sobre o processo
de acesso de dados, onde usa uma interface de baixo nível. O OLE DB utiliza ponteiros e outros
mecanismos do C++, tornando substancialmente mais difícil que o ADO.O ADO oferece uma visão
simplificada e de alto nível para o acesso a BD.
Você também pode acessar o BD por meio de ODBC, que é similar ao OLE DB, porém é uma tecnologia
antiga , e não há razão para usar drivers ODBC. Muitos conhecem DAO e RDO, essas são tecnologias
antigas de acesso a BD por meio de ODBC e são equivalentes ao ADO.
» ADO
Veremos uma visão geral do objeto ADO.Um aplicativo cliente realiza o seguinte:
Os objetos básicos do ADO que correspondem a essas operações são chamados de Connection, Command
e Recordset. O objeto Connection representa uma conexão com o banco de dados.Você especifica o banco
de dados que deseja conectar e chame o método Open.
Uma vez que você estabeleceu uma conexão com o BD, os comandos podem ser executados. Um
comando pode ser uma instrução SQL ou o nome de um procedimento armazenado.Para executar uma
instrução SQL ou um procedimento armazenado, você deve configurar um objeto Connection e depois
chamar seu método Execute para executar o comando.O objeto Command contém a instrução SQL ou o
nome do procedimento armazenado, bem côo os argumentos necessários.Se o comando recupera
informações os resultados serão mantidos em um objeto Recordset onde poderá acessa-los dentro do seu
aplicativo por meio dos métodos e das propriedades do Recordset.
Nos próximos artigos falaremos detalhadamente sobre o objeto ADO. Mas antes veremos a Arquitetura
Cliente / Servidor onde abordaremos o Modelo de Duas e Três camadas.
Este é o segundo artigo relacionado com programação de Banco de Dados. No primeiro artigo o enfoque
foi explicar as tecnologias relacionadas ao acesso a dados. Neste segundo artigo voltaremos nossa
atenção para a arquitetura cliente/servidor. Se você está desenvolvendo aplicativos de banco de dados
para executar em uma rede local, a arquitetura cliente/servidor é adequada para esse trabalho.
Arquitetura Cliente/Servidor.
A arquitetura cliente/servidor possui uma premissa simples: computadores diferentes executam tarefas
diferentes e cada computador pode se otimizado para uma tarefa em particular.Em um ambiente de rede,
o SGBD fica em uma única máquina.Contudo, muitos aplicativos acessam o banco de dados e todos os
clientes fazem solicitações ao mesmo banco de dados.O programa que aceita e trabalha essas solicitações
é o SGBD, e a máquina na qual o SGBD está sendo executado é o servidor de dados.
O aplicativo cliente solicita dados de um banco de dados e exibe-os em um ou mais formulários.Um vez
que os dados estão no computador cliente, seu aplicativo pode processa-los.O computador cliente é
inteiramente capaz de manipular os dados localmente, sem que o servidor se envolva no processo.Se o
usuário edita os campos, o aplicativo pode atualizar o banco de dados sem problemas.A comunicação
entre o cliente e o servidor ocorre por meio do ADO, que torna mais simples o processo de manipulação
dos dados.
A segunda camada é servidor de bando de dados, ou SGBD. Essa camada manipula um objeto muito
complexo, o banco de dados , e oferece uma visão simplificada deste por meio do OLE DB e ADO.O
trabalho do servidor é extrair os dados solicitados de uma tabela e fornece-los ao cliente na forma de um
cursor, ou seja, ele simplesmente transmite um cursor ao cliente para que este processe a com termos
como: duas camadas, três camadas.
No modelo de 2 camadas existe a possibilidade de deixar do lado do Servidor parte ou toda a regra do
negócio.Essa metodologia foi e ainda é muito usado. Para se dizer que se trabalha em 2 camadas a
camada cliente possui um software e a camada servidor também é um software, neste caso software
conversa com software.No caso do Access , mesmo colocando em uma máquina na rede sendo o servidor
e os aplicativos acessando o Access não seria modelo de 2 camadas pois o Access não é um SGBD , não
existe a comunicação software x software. Quem efetua essa comunicação é o driver.O aplicativo cliente
efetua a solicitação e o "driver" que se encarrega de atender. O SQL Server, My SQL , Oracle são SGBD.
No entanto muitos consideram o Access um SGBD pelo fato de trazer algumas características de um SGBD
como controle de concorrência, mas ele não é um banco cliente/servidor.
O MODELO DE 3 CAMADAS
O modelo de 3 camadas possui uma arquitetura muito eficiente para aplicativos de bando de dados, mas
nem sempre é a melhor escolha.Muitos programadores desenvolvem aplicativos de duas camadas que são
executados em pequenas redes.
Em 2 camadas o cliente conversa diretamente com o servidor. O papel do servidor do banco é acessar e
atualizar os dados. O resto é deixado para o cliente.Se encontra aplicativo onde é deixado toda a regra do
negocio do lado do cliente, toda a regra do negócio do lado do servidor ou então dividido entre cliente e
servidor.
• Regra do negócio do lado do cliente: uma vez que regras do negócio refletem práticas comerciais,
elas mudam com muita freqüência. Novas regras são introduzidas, as existentes são revisadas e
isso significa que o código que as implementam está sujeito a alterações freqüentes. Se
implementar regras do negócio no cliente , você deve distribuir os novos executáveis para as
estações de trabalho e certificar-se de que todos os usuários da rede estão usando a última versão
do software cliente.
• Regra do negócio do lado do servidor: se as regras do negócio são implementadas no servidor,
você não precisa redistribuir o aplicativo, mas atribui uma carga adicional ao servidor.
• Regra do negócio tanto no servidor com no cliente: desta forma divide o processamento entre
cliente e servidor, mas no entanto fica segmentado a regra do negócio trazendo assim maior
dificuldade em atualizações.
Isso nos leva, naturalmente para introdução de uma 3 camada, a camada intermediaria.A camada
intermediaria é um objeto que se situa entre o aplicativo cliente e o servidor.Trata-se de uma classe ou
várias classes que expõe vários métodos e isola o cliente do servidor.
A principal vantagem da camada intermediaria é que ela isola o cliente do servidor. O cliente já não acessa
o bando de dados.Em vez disso, ele chama os métodos expostos pelos objetos na camada
intermediaria.Um aplicativo cliente eventualmente adicionará um novo cliente ao banco, mesmo essa
simples operação requer alguma validação.Um aplicativo bem estruturado implementa essas operações na
camada intermediária. O aplicativo cliente não precisa saber como adicionar um novo cliente, basta
chamar a função que efetua o cadastro passando os valores como argumentos.
O modelo de 3 camadas divide os componentes de um aplicativo em 3 categorias:
É comum encontrar modelos de 3 camadas sendo uma camada cliente, um servidor Web e um servidor de
banco de dados, ficando a regra do negócio divida ou centralizada em uma das camadas.Dessa forma
surgiu o modelo de 4 camadas onde teria o lado cliente, servidor Web, servidor de banco de dados e
servidor com a regra do negócio.Esse modelo não é ainda muito aplicado e pouco necessário, até mesmo
o de 3 camadas com servidor de regras do negócio é pouco aplicado e necessário.
Quando os Bancos de Dados Relacionais estavam sendo desenvolvidos, foram criadas linguagens
destinadas à sua manipulação. O departamento de Pesquisa da IBM desenvolveu a SQL como forma de
interface para o sistema de BD relacional denominado SYSREM R, início dos anos 70. Em 1986 o American
National Standard Institute ( ANSI) , publicou um padrão SQL.A SQL estabeleceu-se como linguagem
padrão de Banco de Dados Relacional.
SQL apresenta uma série de comandos que permitem a definição dos dados, chamada de DDL ( Data
Definition Language) , composta entre outros pelos comandos Create, que é destinado a criação do Banco
de Dados, das tabelas que o compõe, além das relações existentes entre as tabelas. Como exemplo de
comandos da classe DDL temos os comandos Create, Alter e Drop.
Os comandos da série DML ( Data Manipulation Language), destinados a consultas, inserções, exclusões e
alterações em um ou mais registros de uma ou mais tabelas de maneira simultânea. Com exemplo de
comandos da classe DML temos os comandos Select, Insert, Update e Delete.
Um subclasse de comandos DML, a DCL ( Data Control Language), dispõe de comandos de controle como
Grant e Revoke.
A linguagem SQL tem como grandes virtudes sua capacidade de gerenciar índices, sem necessidade de
controle individualizado de índice corrente, algo muito comum nas linguagens de manipulação de dados do
tipo registro a registro.Outra característica muito importante disponível em SQL é sua capacidade de
construção de visões, que são formas de visualizarmos os dados na forma de listagens independente das
tabelas e organização lógica dos dados.
Outra característica interessante da linguagem SQL é a sua capacidade que dispomos de cancelar uma
série de atualizações ou de as gravarmos, depois de iniciarmos uma seqüência de atualizações. Os
comandos Commit e Rollback são responsáveis por estas facilidades.
Devemos notar que a linguagem SQL consegue implementar estas soluções, somente pelo fato de estar
baseada em Banco de Dados, que garante por si mesmo a integridade das relações existentes entre as
tabelas e seus índices.
» COMANDOS SQL
Comando Descrição
SELECT Recupera dados do Banco de Dados
INSERT Insere novas linhas, altera linhas existentes e remove linhas de tabelas do banco de dados,
UPDATE respectivamente. Estes comandos são conhecidos como comandos DML (Data Manipulation
DELETE Language).
CREATE Cria, altera e remove objetos do banco de dados. São conhecidos como comandos DDL (Data
ALTER Definition Language).
DROP
RENAME
TRUNCATE
COMMIT Gerenciam as modificações realizadas pelos comandos DML. As modificações efetuadas pelos
ROLLBACK comandos DML podem ser agrupadas em transações lógicas.
SAVEPOINT
GRANT Atribuem e removem direitos de acesso ao banco de dados e aos objetos a ele pertencentes. São
REVOKE conhecidos como comandos DCL (Data Control Language).
COMANDO SELECT
SELECT NOME, NUMERO_DEPT, SALARIO FROM EMPREGADOS WHERE SALARIO > 1000;
Observações:
1. Operadores para comparações lógicas: = > >= <= <> 2. Outros comparadores:
Observações:
- "%" representa nenhum ou muitos caracteres.
- "_" representa um único caracter.
Com esta Query recuperamos o único empregado da empresa que não é gerenciado por ninguém, isto é, o
Presidente da empresa.
Observação: O resultado da cláusula Where abaixo é sempre falso pois um valor nulo não pode ser igual
ou diferente de outro valor, mesmo que este outro valor seja nulo. Se valores nulos forem comparados por
operadores que não o "IS NULL" o resultado será sempre falso.
SELECT NOME, SALARIO, NUMERO_DEPT FROM EMPREGADOS WHERE SALARIO >= 3000 AND
(NUMERO_DEPT = 10 OR NUMERO_DEPT = 30);
- Funções de linhas
- Funções de grupos
Um função de linha retorna um resultado por linha da tabela acessada. Já uma função de grupo retorna
um resultado por grupo de registros.
SELECT NOME, SALARIO, NUMERO_DEPT FROM EMPREGADOS WHERE UPPER (NOME) = 'CELIO';
SELECT NOME, LENGTH (NOME) FROM EMPREGADOS WHERE SUBSTR (NOME, 1, 3) = 'CEL';
Como selecionar dados de mais de uma tabela
Para se exibir dados de mais de uma tabela, através de um comando SQL, é preciso definir condições de
junção. (Joins)
Os joins geralmente ocorrem entre valores de chave primária e de chave estrangeira. Tipos de Joins:
• Equijoin
• Non-equijoin
• Outer join
• Self Join
• Set operators
Um produto cartesiano geralmente ocorre quando a condição de junção é omitida ou inválida. Para evitar
produtos cartesianos é preciso incluir, na cláusula Where, condições de junção válidas.
Exemplo 1:Uma junção simples entre a Tabela de Empregados (Emp) e a tabela de Departamentos (Dept).
SELECT EMPREGADOS.NOME, NUMERO_DEPT, DEPARTAMENTOS.NOME FROM EMPREGADOS,
DEPARTAMENTOS WHERE EMPREGADOS.NUMERO_DEPT = DEPARTAMENTOS.NUMERO;
Obs: Haverá um ganho de desempenho e de clareza se você sempre qualificar as colunas com o nome das
tabelas às quais elas pertencem.
Exemplo 2: Uma junção simples entre a Tabela de Empregados e a tabela de Departamentos considerando
apenas aqueles empregados que ganham mais de 2500,00.
SELECT EMPREGADOS.NOME, EMPREGADOS.NUMERO_DEPT, DEPARTAMENTOS.NOME FROM
EMPREGADOS, DEPARTAMENTOS WHERE EMPREGADOS.NUMERO_DEPT = DEPARTAMENTOS.NUMERO AND
EMPREGADOS.SALARIO > 2500;
OU
SELECT E.NOME, E.NUMERO_DEPT, D.NOME FROM EMPREGADOS E, DEPARTAMENTOS D WHERE
E.NUMERO_DEPT = D.NUMERO AND E.SALARIO > 2500;
Funções de Grupo
Funções de grupo operam com um conjunto de linhas para dar um resultado por grupo de linhas. Um
conjunto de linhas pode ser uma tabela inteira ou linhas desta tabela divididas em grupos.
Funções de grupo podem aparecer tanto na cláusula Select quanto na cláusula Having.
• AVG
• COUNT
• MAX
• MIN
• STDDEV
• SUM
• VARIANCE
Observações:
A cláusula Distinct pode ser utilizada para que sejam considerados apenas valores não duplicatas. Todas
as funções de grupo ignoram valores nulos. Para substituir um valor nulo por outro valor utilize a função
NVL.
Exemplo 1: Utilização de funções de grupo, considerando todas as linhas de uma tabela um único grupo.
SELECT AVG(SALARIO), MAX(SALARIO), MIN(SALARIO), SUM(SALARIO) FROM EMPREGADOS;
OU
SELECT MIN(NOME), MAX(NOME) FROM EMPREGADOS;
Exemplo 3: Utilização da função COUNT para contar o número de empregados lotados no departamento
número 10.
SELECT COUNT(*) FROM EMPREGADOS WHERE NUMERO_DEPT = 10;
Exemplo 4: Utilização da função COUNT para contar o número de empregados que possuem percentual de
comissão diferente de nulo.
SELECT COUNT(PERC_COMISSAO) FROM EMPREGADOS;
Observações:
COUNT(*) conta o número de linhas na tabela.
COUNT(PERC_COMISSAO) conta o número de linhas com percentual de comissão diferente de nulo.
COUNT(NUMERO) conta o número de linhas na tabela uma vez que a coluna NUMERO é a chave primária
da tabela e toda chave primária não pode conter valores nulos.
A cláusula Group By
Exemplo 6: Utilização da cláusula GROUP BY e da função COUNT para se contar quantos empregados
estão lotados em cada departamento.
SELECT NUMERO_DEPT, COUNT(*) FROM EMPREGADOS GROUP BY NUMERO_DEPT;
Observações:
Qualquer coluna incluída na cláusula SELECT, se não estiver em uma função de grupo, deverá constar da
cláusula GROUP BY.
Com a cláusula WHERE é possível excluir determinadas linhas dos grupos.
Por default as linhas são ordenadas ascendentemente conforme a lista de colunas especificada na cláusula
GROUP BY. Para modificar este comportamento é preciso utilizar a cláusula ORDER BY.
Exemplo 7: Utilização da cláusula Group By, da função COUNT e de um JOIN para se contar quantos
empregados estão lotados em cada departamento.
SELECT D.NOME "DEPARTAMENTO", COUNT(*) "QTD" FROM EMPREGADOS E, DEPARTAMENTOS D
WHERE E.NUMERO_DEPT = D.NUMERO GROUP BY D.NOME;
Exemplo 8: A query abaixo está correta? A intenção é listar o número dos departamentos seguido da
média salarial. No entanto, deseja-se listar apenas aqueles departamentos cuja média salarial é superior a
2000.
SELECT NUMERO_DEPT, AVG(SALARIO) FROM EMPREGADOS WHERE AVG(SALARIO) > 2000 GROUP BY
NUMERO_DEPT;
A cláusula Having
Para se restringir a inclusão de grupos não se pode utilizar a cláusula WHERE.
A cláusula WHERE deve ser utilizada para se restringir a inclusão de linhas em um grupo. Para se omitir a
inclusão de grupos inteiros do resultado de uma query deve-se utilizar a cláusula HAVING.
Exemplo 9: A utilização da cláusula HAVING para listar o número dos departamentos seguido da média
salarial de seus empregados. No entanto, deseja-se listar apenas aqueles departamentos cuja média
salarial é superior a 2000.
SELECT NUMERO_DEPT, AVG(SALARIO) FROM EMPREGADOS GROUP BY NUMERO_DEPT HAVING
AVG(SALARIO) > 2000;
Exemplo 10: A utilização da cláusula GROUP BY para listar a quantidade de empregados por
departamento/cargo. Isto é, grupos dentro de grupos. Não deve ser exibido Número de Departamento
NULO.
SELECT NUMERO_DEPT, CARGO, COUNT(*) FROM EMPREGADOS GROUP BY NUMERO_DEPT, CARGO
HAVING NUMERO_DEPT IS NOT NULL;
Uma subconsulta é um comando SELECT embutido em uma cláusula de outro comando SQL.
Exemplo 1: A utilização de uma subconsulta aninhada para recuperar o nome e salário de todos os
empregados que trabalham no mesmo departamento que o JOSE trabalha.
Exemplo 2: A utilização de uma subconsulta aninhada para recuperar o nome e salário de todos os
empregados que ganham mais do que a média salarial da empresa.
SELECT NOME, SALARIO FROM EMPREGADOS WHERE SALARIO > (SELECT AVG(SALARIO) FROM
EMPREGADOS);
Exemplo 3: A utilização de uma subconsulta aninhada para recuperar o nome, número do departamento e
salário de todos os empregados que trabalham no departamento situado no RIO ou no departamento
denominado VENDAS.
Exemplo 4: A utilização de uma subconsulta aninhada para recuperar o nome, número do departamento e
salário de todos os empregados que trabalham no departamento situado no RIO ou no departamento
denominado VENDAS.
Correção da query anterior com a utilização da cláusula IN uma vez que esta subconsulta retorna mais de
uma linha.
Exemplo 5: A utilização de uma subconsulta aninhada para recuperar o número do departamento e sua
média salarial. Devem ser recuperados apenas os departamentos cuja média salarial é maior do que a
média salarial do departamento número 30.
No exemplo abaixo a subconsulta é executada uma vez para cada linha da tabela de empregados na
consulta mais externa.
Exemplo 1: A utilização de uma subconsulta correlacionada para recuperar o nome dos empregados que
trabalham no projeto numero 2.
Quantificador Existencial
Exists representa o quantificador existencial, uma noção emprestada da lógica formal. Em SQL, um
predicado existencialmente quantificado é representado pela expressão da forma EXISTS (SELECT * FROM
... ).
Essa expressão será verdadeira se o resultado do cálculo da subconsulta representado por "SELECT *
FROM ..." não estiver vazio, isto é, se existir pelo menos um registro na tabela FROM da subconsulta que
satisfaz a condição WHERE dessa mesma subconsulta.
Qualquer consulta que utilize IN pode alternativamente ser formulada com EXISTS, porém o inverso não é
verdadeiro.
SELECT NOME FROM EMPREGADOS E WHERE EXISTS (SELECT * FROM TRABALHAM WHERE NUMERO_EMP
= E.NUMERO AND NUMERO_PROJ = 2);
OU
SELECT NOME FROM EMPREGADOS E WHERE NOT EXISTS (SELECT * FROM TRABALHAM WHERE
NUMERO_EMP = E.NUMERO AND NUMERO_PROJ = 2);
OU
SELECT NOME FROM EMPREGADOS MINUS SELECT NOME FROM EMPREGADOS E, TRABALHAM T
WHERE T.NUMERO_EMP = E.NUMERO AND T.NUMERO_PROJ = 2;
Linhas duplicatas são eliminadas do resultado de uma união a não ser que o operador UNION inclua
explicitamente o quantificador ALL. Assim, no exemplo nº 1, o projeto nº 3 é selecionado em ambos os
SELECTS, mas só aparece uma vez no resultado final.
Quando sabemos que não haverá elementos duplicados no resultado é conveniente utilizarmos UNION ALL
para que o sistema não seja forçado a eliminar duplicidades, desnecessariamente.
Exemplo 1: Obter o número dos projetos que, ou se iniciaram após 31-JUL-97, ou possuem o empregado
7566 nele trabalhando. União sem repetição.
SELECT NUMERO FROM PROJETOS WHERE DT_INICIO > '31-JUL-97' UNION SELECT NUMERO_PROJ FROM
TRABALHAM WHERE NUMERO_EMP = 7566;
Exemplo 2: Obter o número dos projetos que, ou se iniciaram após 31-JUL-97, ou possuem o empregado
7566 nele trabalhando. União com repetição.
SELECT NUMERO FROM PROJETOS WHERE DT_INICIO > '31-JUL-97' UNION ALL SELECT NUMERO_PROJ
FROM TRABALHAM WHERE NUMERO_EMP = 7566;
Exemplo 3: A possibilidade de incluirmos constantes numa cláusula SELECT é freqüentemente útil quando
a cláusula UNION é utilizada. Por exemplo, para indicar qual das duas condições WHERE foi atendida para
a inclusão do elemento no resultado final.
SELECT NUMERO, 'DT_INICIO > 07-JAN-90' CONDIÇÃO FROM PROJETOS WHERE DT_INICIO > '07-JAN-
90' UNION ALL SELECT NUMERO_PROJ, 'NUMERO_EMP = 7566' FROM TRABALHAM WHERE NUMERO_EMP
= 7566;
Resultado:
NUMERO CONDIÇÃO
------------ ---------------------
2 DT_INICIO > 07-JAN-90
3 DT_INICIO > 07-JAN-90
4 DT_INICIO > 07-JAN-90
3 NUMERO_EMP = 7566
No próximo artigo vamos continuar a falar sobre os comandos SQL DML (Insert, Delete e Update).
Este é o quarto artigo relacionado com programação de Banco de Dados, neste artigo vamos continuar
abordar os comandos SQL. No terceiro falamos mais do comando SELECT agora vamos falar sobre os
demais comandos fechando o assunto SQL.
Achei que no terceiro artigo ficou faltando falar melhor na recuperação de dados de várias tabelas (JOINS)
vamos então fechar o SELECT com esse assunto.
JOINS
A cláusula WHERE permite que você conecte duas ou mais tabelas, com base nos valores de duas colunas
nas duas tabelas.A cláusula WHERE, no entanto, deve ser usada para expressar restrições envolvendo
uma ou mais tabelas, e não para conectar tabelas, embora seja comum fazermos isso. O método
apropriado para vincular tabelas é a operação JOIN.
Vou citar 2 exemplos onde ocorre problemas ao usar a clausula WHERE em junções:
Bem nesta consulta somente iria retornar os funcionários que possuem dependentes, os que não tivessem
dependentes ficaria de fora.
A operação JOIN combina colunas de duas tabelas se as linhas possuírem campos de correspondência.
Sua sintaxe é:
TIPOS DE JUNÇÕES
O SQL suporta dois tipos de junções:
INNER JOIN esta junção retorna todos os pares com correspondentes de linhas nas duas tabelas e
descartam as linhas sem correspondentes de ambas as tabelas.
OUTER JOIN esta junção retorna todas as linhas normalmente retornadas pela operação INNER JOIN,
mas as linhas da tabela esquerda ou da direita que não atendam à condição.
CROSS JOIN incluímos cada uma das combinações de todas as linhas entre as tabelas.
Na sintaxe MS-SQL Server, são comparadas as tabelas por uma coluna específica para cada tabela (chave
estrangeira), linha por linha, e são listadas as linhas em que a comparação é verdadeira.
INNER JOIN
Considere as tabelas:
CLIENTE: Cod_cliente, Nome,Endereço
PEDIDO: Num_Pedido,Prazo_Entrega Cod_Cliente, Cod_Vendedor,Data
ITEMPEDIDO: num_pedido,Cod_produto,Quantidade
PRODUTO: Cód_produto,Descrição,Unidade,ValUnidade.
VENDEDOR: Cód_Vendedor,Nome, Comissão,Salario
SELECT Cliente.nome,Pedido.cod_cliente,pedido.num_pedido
FROM Cliente INNER JOIN Pedido
ON Cliente.Cod_cliente = Pedido.Cod_cliente
Problema: Mostre os clienter (ordenados) que têm prazo de entrega maior que 15 dias para o produto
"ARROZ" e sejam do Rio de Janeiro.
SELECT Cliente.Nome
FROM Cliente INNER JOIN Pedido
ON Cliente.Cod_cliente=Pedido.Cod_Cliente
INNER JOIN ItemPedido
ON pedido.num_pedido = itempedido.num_pedido
INNER JOIN Produto
ON itempedido.Cód_produto= Produto.Cod_Produto
WHERE Pedido.Prazo_Entrega > 15 AND
Produto.Descrição='ARROZ' AND
Cliente.UF = 'RJ'
ORDER BY Cliente.Nome
Problema: Mostre todos os vendedores que venderam chocolate em quantidade superior a 5 Kg.
Problema: Quantos clientes da cidade do Rio de Janeiro e de Niterói tiveram seus pedidos tirados pelo
vendedor 'PAULO' fez no mês de janeiro.
OUTER JOIN
É a seleção em que são restritas as linhas que interessam em uma tabela, mas são consideradas todas as
linhas de outra tabela.
Ou seja, queremos ver quais linhas de uma tabela estão relacionadas com a outra tabela e quais as linhas
não estão.
Poderíamos dizer, que queremos ver quais clientes tem pedidos e quais clientes não tem pedidos.
Um OUTER JOIN somente pode ser realizado entre duas tabelas, não mais que duas tabelas.
LEFT OUTER JOIN - são incluidas todas as linhas da primeira tabela na expressão.
Sintaxe: INSERT INTO nome Tabela (nome das colunas ) VALUES ( valores )
Exemplo:
Quem concede estes direitos para os usuários do banco é o Administrador de Banco de Dados. (DBA)
Exemplo:
Observações:
- O nome de uma tabela deve começar por uma letra.
- Pode ter até 30 caracteres.
- Deve conter apenas: A-Z, a-z, 0-9, _, $ e #.
- Não pode ter o mesmo nome de qualquer outro objeto existente no esquema do usuário.
Tipos de Dados
- NUMBER
- NUMBER(p,s)
- DATE
- CHAR(s)
- VARCHAR2(s)
- LONG
Tipos de Constraints - PRIMARY KEY
- FOREIGN KEY
- NOT NULL
- UNIQUE
- CHECK
Observações:
- É possível criar uma constraint após a criação da tabela.
- Uma constraint pode ser definida a nível de coluna ou a nível de tabela.
- Constraints são armazenadas no Dicionário de Dados e podem ser facilmente recuperadas se
possuírem nomes razoáveis.
Como dar Nome às Constraints
Exemplo 4:
CREATE TABLE DEPENDENTES
(NUMERO_EMP NUMBER(4)
CONSTRAINT DEPENDENTES_EMP_NUMERO_EMP_FK
REFERENCES EMPREGADOS (NUMERO),
NUM_ORDEM NUMBER(2),
NOME VARCHAR2(10),
CONSTRAINT DEPENDENTES_NUM_EMP_NUM_ORD_PK
PRIMARY KEY(NUMERO_EMP, NUM_ORDEM));
Este é o quinto artigo e vamos abordar o componente ADO. Não falaremos de tudo que a biblioteca ADO
oferece, mas com o que vai exposto será possível desenvolver aplicações completas que interagem com
banco.
ADO
Agora chegou o momento de entrar realmente na programação de banco de dados. Neste artigo
exploraremos o componente ADO ( Active Dta Objects - Objeto de Dados Ativos).
O ADO é um componente para acesso a bancos de dados.Ele expõe alguns objetos muito flexíveis que
expõem suas próprias propriedades, métodos e eventos.
Para usar objetos ADO em um aplicativo, você deve primeiro adicionar uma referencia ao componente
ADO.Inicie um projeto Standard EXE e depois selecione Project->References.Na janela References,
localizeMicrosoft ActiveX Data Objects 2.x library e marque a sua caixa de seleção.
Existem diversas formas de se trabalhar com o ADO, já vi vários códigos usando ADO e de forma
diferente.Vou mostrar nesse artigo a forma mais simples considerada por mim de usar o ADO.
Neste caso vamos ver cada passo desse processo.Neste artigo não vamos abordar a execução de trigger
ou de procedimentos armazenados que tem parâmetros para serem executados.
pVarConBanco.ConnectionString = _
"provider = microsoft.jet.oledb.4.0;" & _
"data source=" + PathBanco + NomeBanco + ";jet oledb:database password=" + SenhaBanco
É usado o Jet 4.0 que da suporte para o Access 2000 e para os demais. (Não sei se da suporte para o
Access XP)
OBS: Caso o banco não seja protegido por senha basta colocar:
pVarConBanco.ConnectionString = _
"provider = microsoft.jet.oledb.4.0;" & _
"data source=" + PathBanco + NomeBanco
Após informa a string de conexão basta chamar o evento OPEN que estabelece a conexão com o banco.
pVarConBanco.Open
Não é possível abrir duas conexões ao mesmo tempo usando a mesma variável de connection.
Para fechar a conexão com o banco:
pVarConBanco.Close
Neste caso o evento Close apenas fecha a conexão com o banco, no entanto a variável continua na
memória.Neste caso você pode usar Set pVarConBanco= Nothing para liberar da memória a variável.Não
aconselho a fechar o banco somente usando Set Nothing.
Falaremos somente isso sobre a classe Connection, sendo que essa classe possui diversos outros
comandos e especificações de conexão principalmente para banco cliente servidor no entanto para um
primeira visão isso é o suficiente para criação de aplicações que usem o Banco Access , que vai ser o
banco usado nesses artigos.
Uma função de conexão com banco de dados pode ser implementada dessa forma:
Public Function (ByVal NomeBanco As String,ByVal Endereco_Banco As String,ByVal Senha_Banco As
String) As
Boolean
'Rotina generica que conecta e abre o banco em ADO
On Error GoTo T_Erro
Conexao_Banco = False
pVarConBanco.ConnectionString = _
"provider = microsoft.jet.oledb.4.0;" & _
"data source=" + Endereco_Banco + NomeBanco + ";jet oledb:database password=" + Senha_Banco
pVarConBanco.Open
Conexao_Banco = True
Exit Function
T_Erro:
MSG_ERRO "Conexao_Banco"
End
End Function
Onde recebe o nome, caminho e senha do banco retornando verdadeiro caso a conexão seja
sucedida.Dessa forma você pode criar um modulo único com as declarações das variáveis Connection e
Recordset e com a função de conexão e usar em todos os seus projetos.
Na chamada da função caso retorne False você pode abortar a execução do programa com Unload ou END.
No tratamento de erro fiz um procedimento chamado MSG_ERRO que uso em todos os tratamento de
erros de minhas aplicações, onde tem como parâmetro o nome do procedimento.
Propriedades do Cursor
CursorLocation
Define em qual lado será aberto o cursor, se do lado cliente ou do lado do servidor.Neste caso válido
somente para banco cliente servidor. Ao abrir um cursor ao lado do cliente as linhas qualificadas são
movidas do servidor para o cliente, e a manipulação dos dados fica a cargo da maquina cliente.Tirando
parte do processamento do lado do servidor.Isso é bastante importante tratando de aplicações onde se
manipulam grandes quantidade de dados e para as consultas em que necessitam de grande
processamento, dessa forma você pode manipular o processamento do servidor para não sobrecarrega-lo
CursorType
AdOpenDynamic = melhor tipo de cursor e também o mais caro (exige mais do servidor para mante-lo).
Consulta abertas com esse tipo de cursor as linhas do banco não são lidas e sim acessadas dessa forma é
possível pegar atualizações, deleções e INSERÇÕES de outros usuários. O Access não dá suporte para
esse tipo de cursor.
AdOpenForwardOnly = este cursor é somente de avanço e também o cursor default(caso você não
especifique o tipo de cursor esse será definido automaticamente).Este cursor não permite retornar nas
linhas acessadas só pode ser percorrido para frente.
AdOpenKeyset = este cursor associa um número para cada linha acessada, ao ser solicitado a linha ele
acessa o banco, dessa forma com esse cursor é possível pegar alterações e deleções numa aplicação
multiusuario.Esse seria o melhor cursor para o Access.
adOpenStatic = este cursor efetua uma copia do banco como se fosse uma foto (snapshot). Não pega
alterações nem deleções.
AdLockReadOnly= Use este tipo de bloqueio para abrir um cursor somente leitura.Neste caso não será
possível alterar ou deletar o registro.Normalmente este cursor é utilizado somente para exibir os dados
para o usuário.
AdLockOptimistic= Esta é a técnica mais comum de bloqueio.O Banco de dados coloca um bloqueio na(s)
linha(s) enquanto a(s) atualiza.
AdLockPessimistic= Esta é a forma mais rígida de controle de concorrência.A linha é bloqueada enquanto
o cursor estiver aberto.
O controle de concorrência determina o que acontecerá quando dois usuários tentarem atualizar a mesma
linha ao mesmo tempo.No entanto não basta configurar as propriedades do cursor para construir
aplicações multiusuarios, primeiro deve ter a política de concorrência do aplicativo.Antes de iniciar um
projeto multiusuário deve-se traçar a estratégia de concorrência, primeiro verificar se existe a
concorrência ou se o sistema é somente para consulta (o que é pouco provável) deva forma após essa
avaliação analisar o tipo de cursor que melhor atenda a sua concorrência.
Por exemplo:
Dois usuários acessam o mesmo registro numa interface que é possível alterar o registro.Neste caso
definindo de forma precisa os cursores somente um usuário vai conseguir alterar, caso isso seja ao mesmo
tempo ou então o primeiro usuário altera, e o segundo quando for alterar vai ser a partir dos dados iniciais
(sem a alteração do primeiro usuário), ou seja, não existe política de concorrência e um aplicativo assim
seria uma "Torre de Babel".
O que poderia ser feito acima seria abrir o cursor com um bloqueio pessimista, ou seja, a linha acessada
fica bloqueada enquanto estiver aberto o cursor. Dessa forma somente um usuário iria ter acesso ao
registro para editá-lo.Mas esse tipo de bloqueio é perigoso, caso o usuário abra o registro e depois vai
tomar um cafezinho o registro fica preso para todos os demais usuários.
Uma solução que proponho para um caso assim seria fazer uma interface de visualização do registro, com
um botão para alterar o registro abrindo assim a janela que permite alterar.Dessa forma o registro no
banco teria um campo que indicaria se o registro está sendo editado ou não.Assim se dois usuários ao
mesmo tempo tentarem editar o mesmo registro somente para um deles a tela de edição será aberta,
para o outro teria uma mensagem de bloqueio que pode ser tratada com uma mensagem amigável tipo:
Registro sendo editado no momento. Com essa solução o registro não fica bloqueado para leitura.
Para entender melhor, suponha um sistema com duas janelas.Na primeira mostra em uma listview todos
os clientes cadastrados, então ao selecionar um cliente abre outra janela com os dados desse
cliente.Normalmente ao visualizar esses dados o programador aproveita a mesma janela para alterações
colocando um botão "Alterar". Se neste caso ao abrir o cursor para carregar os dados do cliente for um
cursor com bloqueio pessimista o registro ficará bloqueado inclusive para leitura, pois não será possível
abrir dois cursores pessimista para o mesmo registro.Por isso deve-se tomar cuidado ao usar cursores
pessimista.
Para um caso desse tipo, basta o programador mudar os tipos de cursores, não seria necessário alterar a
interface. Quando o usuário selecionar o registro na primeira janela abre um cursor somente leitura,
carrega todos os campos e fecha o cursor. No botão "Alterar" abre o cursor novamente neste caso com
bloqueio otimista efetua a alteração e fecha o cursor.Assim o registro praticamente não ficaria bloqueado,
somente no instante do UPDATE.
Nos próximos artigos vou colocar exemplos de códigos explicados, abaixo posto um exemplo simples
somente para não ficarmos só na teoria.
Em Modulo
Primeiro define se o cursor será do lado cliente ou do servidor.Na segunda linha pelo evento OPEN coloca o
comando SQL, depois o variável de conexão seguido do Tipo do cursor e do Tipo de bloqueio usado. Esse
tipo de consulta seria para carregar um Grid.No termino de carregar o Grid deve fechar o cursor.
OBS: Evite ao máximo ficar com o cursor aberto, sempre tenha o controle de abrir e fechar o cursor,
manter cursor aberto além de caro para o servidor pode numa queda de luz aumenta as chances de
corromper a base de dados.
Exemplo para Update:
With pVarRegBanco
.CursorLocation = adUseServer
.Open "Select IdMesa From ContaMesa where IdMesa=" & RegSelecionado & "", pVarConBanco,
adOpenStatic,
adLockOptimistic
If Not .EOF Then'Se retorna verdadeiro então não achou o registro
.MoveFirst 'Posiciona para o primeiro registro retornado
Do While Not (.EOF) 'Enquanto não chegar no final
!IdMesa = 0 ' Efetua a alteração de IdMesa para 0
.Update 'Efetua o Update para o registro acessado
.MoveNext 'Avança com o cursor.
Loop
End If
.Close 'Fecha o cursor
End With
Repare que neste exemplo o tipo do bloqueio foi adLockOptimistic pois é para efetuar alterações.Essa é
apenas uma forma de alterar registro pelo ADO, poderia simplesmente colocar a instrução SQL UPDATE
para isso, no entanto caso a instrução UPDATE não encontrar o registro não acontece nada, nem
mensagem de erro.Fazendo a alteração igual ao exemplo acima é possível saber se te fato houve ou não
alteração.O código acima apenas trata o erro se não achar o registro, mas caso o registro não seja achado
não retorna uma mensagem para o usuário.
O mesmo problema ocorre no caso da Deleção, se for através da instrução SQL , caso o registro não
exista não ocorre nada.Por isso eu não uso as instruções UPDATE e DELETE do SQL, somente uso SELECT
e INSERT.As alterações e deleções faço através das propriedades do ADO
Exemplo de Deleção:
With pVarRegBanco
.CursorLocation = adUseServer
.Open "Select IdMesa From Mesa where IdMesa=" & RegSelecionado & "", pVarConBanco,
adOpenStatic,
adLockOptimistic
.Delete adAffectCurrent
.Close
End With
A propriedade adAffectCurrente seria deletar o cursor acessado.Caso a consulta possa retornar mais de
uma registro faça um Loop como o do exemplo da alteração colocando .Delete adAffectCurrente no lugar
do .Update.
Outro detalhe as consultas SQL não foram abertas usando o curinga (*), se utilizar o curinga não tem
problema o código funcionária da mesma forma, no entanto iria retornar campos desnecessário,
resultando maior trafego na rede sem necessidade.Por isso evite abrir cursores com curinga (*)
desnecessariamente, use o curinga quando de fato for para retornar todos os campos de um registro,
como por exemplo, exibir um registro para o usuário.
Um Exemplo de Inserção:
With pVarRegBanco
.CursorLocation = adUseServer
.Open "Insert Into Mesa values(" & Trim(FTxtIdMesa.text) & "," &
CmbFumantes.ItemData(CmbFumantes.ListIndex) & ",'" & Trim(FTxtLugares.text) & "' ,0,1,'" &
Trim(TxtLocalizacao.text) & "' )", pVarConBanco.Conection, adOpenStatic, adLockOptimistic
End With
Bem simples não? Basta usar o comando SQL, com a atenção para o tipo de bloqueio, neste caso
"sempre"o otimista.Existe outra forma de incluir registro que seria através das propriedades do ADO
usando o ADDNEW, no entanto não acho bom usá-lo, pois ele cria um registro no caso com todos os
campos com NULL e depois atribui para cada campo os valores (igual ao UPDATE) e no final chama o
evento do ADO .UPDATE, ou seja, a inserção pelo ADO ele simplesmente cria um novo registro e depois o
altera.Caso a luz acabe justamente quando tiver sendo atribuído os valores aos campos você terá no seu
banco um registro com campos NULL mesmo que tenha configurado para o campo não receber nulo.Sendo
assim acho muito mais eficaz usar a instrução SQL INSERT para inserir.
No próximo artigo continuamos a abordar o ADO com mais exemplos, e também vamos abordar as
transações.
Este é o sexto artigo sobre Banco de Dados e vamos continuar abordar o componente ADO. No quinto
artigo falamos como usar o ADO para conectar ao banco e como consulta, incluir, excluir e atualizar
registros. Agora vamos abordar Transações e como implementar isso usando ADO.
Uma transação é uma unidade atômica de processamento que inclui uma ou mais operações de leitura ou
escrita no banco de dados. Algumas características de transações:
- Atomicidade: como principal característica sua execução tem que ser integral, ou nenhuma parte deve
ser executada, ou seja, você utiliza transação quando determinada ação envolve atualização de mais de
uma tabela do banco.E para garantir que todas as atualizações sejam feitas utiliza-se transação. Se uma
ação pode ser separada em ações menores, então temos duas (ou mais) transações, ou seja, se uma ou
mais ações podem falhar sem deixar o banco de dados em estado inconsistente, estas ações não devem
ser parte da mesma transação.
- Consistência: A execução de uma transação deve levar ao banco de dados de um estado consistente a
outro também consistente. Uma transação é consistente se não violar a integridade do banco de dados.
Se a transação tiver êxito ou falhar, ela deve deixar o banco de dados em um estado consistente.Se uma
transação falhar, ela precisa desfazer todas as alterações temporárias e deixar o banco de dados no estado
em que ele estava antes que a transação iniciou.
- Isolamento: Uma transação não deve tornar suas atualizações visíveis a outras transações antes do seu
fim. Uma transação que debita uma conta e credita outra deve ser totalmente transparente. Quando isso é
feito, a transação precisa atualizar o banco de dados de uma só vez. Por exemplo, se um usuário solicitar
o saldo de uma conta e a mesma está sofrendo uma transação o banco de dados só deve retornar o valor
do saldo depois que completar a atualização, assim dessa forma durante a transação algumas linha são
bloqueadas.
- Durabilidade: Após o termino de uma transação, suas atualizações não podem ser perdidas por causa
de falhas futuras. Se todas as ações forem realizadas com sucesso isso não significa sucesso na transação,
pois precisa gravar os dados de volta ao disco. Caso ocorra uma falha no disco a transação não é válida.
Então antes que uma transação seja completada ela deve verificar se as alterações foram gravadas com
sucesso antes de terminar.
Dessa foram ao utilizar transação você garante que será executado cada linha do código pertencente à
transação. Caso ocorra alguma falha durante a execução da transação devem ser canceladas todas as
alterações até a ocorrência da falha.Assim você mantém a integridade do banco de dados além da
consistência dos dados da sua aplicação.
As transações podem ser divididas em dois tipos: de integridade dos dados e de consistência da ação.
Integridade e consistência do banco podem dizer a mesma coisa, mas quando me refiro à integridade dos
dados é em relação às regras impostas nos relacionamentos.
Exemplo:
Temos uma tabela VENDA que armazena os dados da venda como: valor, data, caixa e etc. Outra tabela
PRODUTOS com os dados dos produtos cadastrados no estoque. E por ultimo uma tabela PRODUTOS
VENDIDOS que relaciona a venda com o produto assim:
Tabela Venda
IdVenda= 0001
Valor= R$ 150,00
Caixa= Fulano
Tabela Produto
IdProduto= 200
Valor= R$ 5,00
Estoque=100
Tabela ProdutosVendidos
IdVenda= 0001
IdProduto= 200
Qtde=30
Então a integridade seria quebrada caso apaga-se o produto 200 da tabela produto e não propagar a
exclusão para a tabela ProdutosVendidos, ou incluir um registro na tabela ProdutosVendidos onde o
IdVenda não existe na tabela Venda e a mesma coisa para o IdProduto.E sobre a consistência da ação
seria ocorrendo a venda 0001 gravar na tabela ProdutosVendidos os itens da venda e para cada item
vendido atualizar o estoque.
Sendo que as regras de integridade o SGBD trata para nós sendo transparente. Claro algumas regras de
integridade devem ser impostas ao banco sendo por trigger, procedures ou por regras oferecidas
diretamente pela interface do banco, como por exemplo: manter a integridade do relacionamento
propagando exclusão ou alteração no Access.
Não vou abordar as transações de integridade do banco, sendo que podem ser impostas na própria
aplicação pelo que vai ser mostrado aqui, no entanto um projeto que deixa do lado da aplicação a
integridade referencial do banco tem o banco de dados mal projetado.
Vamos então nos preocupar somente com as transações das "regras do negócio". Essas transações podem
passar como despercebidas no banco de dados.Isso depende do projeto, caso seja definido que as regras
do negócio sejam do lado do banco então as transações seriam através de script's em SQL definidos por
trigges e procedures no banco. Aonde a aplicação iria apenas executá-las e aguardar o seu retorno. O
ADO nesse caso somente iria "participar" no comando da execução da trigger ou procedure.
Existe uma diferença entre manter a integridade do banco e manter as regras do negocio. As regras de
integridade do banco devem estar definidas no próprio banco, enquanto as regras do negócio podem ser
dividas entre banco ou aplicação, ou então definidas em uma terceira camada, como abordamos no artigo
2.
O que vou vamos ver nesse artigo são transações que definem a regra do negócio fora do banco de dados
usando ADO. O que é muito simples, pois todas as características de uma transação o ADO juntamente
com o SGBD tratam para nós. Para nós basta informar quando se inicia uma transação, o seu fim e no
tratamento de erro cancelar a transação. Nossa atenção deve voltar para a analise do problema e verificar
para cada ação no banco se envolve ou não uma transação para manter a regra do negócio.
Agora chega de teoria e vamos finalmente a prática.
Os comandos de transação são métodos da classe Connection. As ações da transação têm que usar o
mesmo objeto (variável da classe connection) que chamou o método de inicio da transação. Conforme
abordado no quinto artigo declare a variável connection, exemplo:
Exit Function
T_Erro:
MSG_ERRO "NovaConta"
Exit Function
T_Transacao:
Set pVarRegBanco = Nothing
pVarConBanco.RollbackTrans
MSG_ERRO "NovaConta"
Exit Function
T_Consulta:
pVarRegBanco.Close
Set pVarRegBanco = Nothing
pVarConBanco.RollbackTrans
MSG_ERRO "NovaConta"
T_Alteração:
pVarRegBanco.CancelUpdate
Set pVarRegBanco = Nothing
pVarConBanco.RollbackTrans
MSG_ERRO "NovaConta"
End Function