Vous êtes sur la page 1sur 28

Programação de Banco de Dados - parte 1

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 1: Controle de Redundâncias - A redundância consiste no armazenamento de uma mesma


informação em locais diferentes, provocando inconsistências. Em um Banco de Dados as informações só
se encontram armazenadas em um único local, não existindo duplicação descontrolada dos dados. Quando
existem replicações dos dados, estas são decorrentes do processo de armazenagem típica do ambiente
Cliente-Servidor, totalmente sob controle do Banco de Dados.

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 4: Interfaceamento - Um Banco de Dados deverá disponibilizar formas de acesso gráfico,


em linguagem natural, em SQL ou ainda via menus de acesso, não sendo uma "caixa-preta" somente
sendo passível de ser acessada por aplicações.

Característica 5: Esquematização - Um Banco de Dados deverá fornecer mecanismos que possibilitem a


compreensão do relacionamento existentes entre as tabelas e de sua eventual manutenção.

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.

» OLE DB / ODBC / ADO

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:

• Conexão com banco de dados.


• Executa comandos por meio do banco de dados.
• Recupera informações do banco de dados.

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.

Programação de Banco de Dados - parte 2

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 Modelo de Duas Camadas.

A primeira camada de um aplicativo cliente/servidor é a camada cliente que é executada no cliente.Essa


camada contém o aplicativo que interagem com o usuário e usualmente são aplicativos em VB.Você
também pode construir camadas clientes que são executadas em um navegador - são páginas Web que
possuem controles semelhantes ao controles básicos do VB e possibilitam ao usuário interagir com o
banco 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.

Na segunda camada é possível através de procedimentos armazenados executar as operações no lado do


SGBD, pela divisão do trabalho entre cliente e servidores, permitimos que cada aplicativo faça o melhor. O
SGBD é executados em uma das máquinas mais rápidas da rede, os clientes não precisam ser poderosos.

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.

Vamos analisar essas 3 possibilidades de implementação em 2 camadas.

• 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:

• Camada de Apresentação: Este programa é executado no cliente e interage com o usuário.Você


usualmente desenvolvera aplicativos para a camada de apresentação e esses aplicativos serão
chamados freqüentemente de serviços do usuário.
• Camada de Aplicação: conhecida como camada de regras do negócio, está camada contém a lógica
do aplicativo.Isso simplifica o acesso do cliente ao banco de dados por meio do isolamento do
serviço de usuários do bando de dados.Essa camada é projetada antes que você comece a codificar
o aplicativo cliente.
• Camada de Dados: esta camada esta no servidor de bando de dados que atende as solicitações
feitas pelos clientes.O servidor do banco de dados deve atualiza-lo e ao mesmo tempo proteger sua
integridade.

É 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.

O ACCESS E APLICATIVOS CLIENTE/SERVIDOR


O Access não é um servidor de bando de dados.Quando você contata o Access e abre uma tabela, a tabela
inteira é carregada para a memória do computador cliente.Se houver 5 usuários na rede e todos estiverem
acessando a mesma informação, então cinco copias das mesmas tabelas estarão nos clientes.
Uma das diferenças mais importantes entre o Access e o SQL Server é o modo como eles lidam com a
concorrência. O SQL Server mantém uma única copia dos dados.Como muitos clientes precisam passar
pelo SGBD, o SQL Server sabe quando um registro esta sendo editado e impede que outros usuários
consigam excluir ou até mesmo ler o mesmo registro.O Access deve comparar as alterações efetuadas no
cliente para o original e depois decidir se outros usuários podem ou não acessar uma linha.Se um usuário
abre uma tabela e seleciona uma linha para editar, nenhum outro usuário pode editar a mesma
linha..Dessa forma a linha permanecerá bloqueada indefinidamente.A medida que o numero de usuários
aumenta, a sobrecarga torna-se insuportável e então é hora de trocar o bando para SQL Server.
O Access 2000 pode ser usado para desenvolver aplicativos cliente/servidor, mas este recurso do Access é
baseado na tecnologia do SQL Server.Através do componente MSDE que é um mecanismo de dados
cliente/sevidor. O MSDE é totalmente compatível com o SQL Sever; ele se baseia-se no mesmo
mecanismo de dados do SQL Server, porém é indicado para pequenos grupos de trabalho.Você pode usar
o MSDE para desenvolver aplicativos e posteriormente se necessário migrar para SQL Server alterando
somente as informações de conexão.
Antes de prosseguirmos falaremos sobre a linguagem SQL mas no próximo artigo.

Programação de Banco de Dados - parte 3


Este é o terceiro artigo relacionado com programação de Banco de Dados, sairemos um pouco de teoria e
vamos abordar mais a prática explicando a linguagem SQL. No segundo artigo o enfoque foi explicar a
arquitetura cliente servidor com os modelos de 2 e 3 camadas.

SQL (Structured Query Language)

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).

» DML ( Data Manipulation Languagem)

Veremos agora os comandos SQL DML destinados a manipulação dos dados.Comandos:

COMANDO SELECT

Sintaxe: Select <campos da tabela> From <nome da tabela> Where <condição>

Exemplo 1: Selecionar linhas de uma tabela

SELECT NUMERO, NOME FROM EMPREGADOS;

Exemplo 2: Utilizando a cláusula DISTINCT para suprimir linhas duplicatas.

SELECT DISTINCT CARGO FROM EMPREGADOS;

Exemplo 3: Utilizando a cláusula DISTINCT com várias colunas.

SELECT DISTINCT CARGO, NUMERO_DEPT FROM EMPREGADOS;

Exemplo 4: Utilizando a cláusula * para listar todas as colunas da tabela.

SELECT * FROM EMPREGADOS

Exemplo 5: Utilizando operadores aritméticos (+, -,*, / ) na cláusula Select.

SELECT NOME, SALARIO * 12, DT_ADMISSAO FROM EMPREGADOS;

Exemplo 6: Utilizando Alias.

SELECT NOME, SALARIO * 12 AS SAL_ANUAL,DT_ADMISSAO FROM EMPREGADOS;


OU
SELECT NOME, SALARIO * 12 "SAL ANUAL", DT_ADMISSAO FROM EMPREGADOS;

Exemplo 7: Utilizando operador de concatenação.

SELECT NOME||' '||SOBRENOME FROM EMPREGADOS;

Como Ordenar e Limitar as Linhas Selecionadas

Exemplo 1: Ordenando as linhas selecionadas com a cláusula ORDER BY

SELECT NOME, SALARIO FROM EMPREGADOS ORDER BY SALARIO;


OU
SELECT NOME, SALARIO FROM EMPREGADOS ORDER BY SALARIO DESC;

Obs: Na ordenação ascendente, valores NULOS aparecem no final.

Exemplo 2: Ordenando pela posição da coluna selecionada


SELECT NOME, SALARIO * 12 FROM EMPREGADOS ORDER BY SALARIO * 12;
OU
SELECT NOME, SALARIO * 12 FROM EMPREGADOS ORDER BY 2;

Exemplo 3: Selecionando apenas os empregados lotados no departamento 20.

SELECT NOME, NUMERO_DEPT, SALARIO FROM EMPREGADOS WHERE NUMERO_DEPT = 20 ORDER BY


SALARIO;

Exemplo 4: Selecionando apenas o empregado denominado SMITH.

SELECT NOME, NUMERO_DEPT, SALARIO FROM EMPREGADOS WHERE NOME = 'CELIO';

Exemplo 5: Selecionando com operadores lógicos.

SELECT NOME, NUMERO_DEPT, SALARIO FROM EMPREGADOS WHERE SALARIO > 1000;

Observações:

1. Operadores para comparações lógicas: = > >= <= <> 2. Outros comparadores:

BETWEEN ... AND ... ou NOT BETWEEN


IN (Lista) ou NOT IN
LIKE ou NOT LIKE
IS NULL ou IS NOT NULL

3. Operadores lógicos: AND


OR
NOT

Exemplo 6: Selecionando linhas com BETWEEN ... AND ...

SELECT NOME, DT_ADMISSAO FROM EMPREGADOS


WHERE DT_ADMISSAO BETWEEN '28-SEP-90' AND '30-JAN-91';

Exemplo 7: Selecionando linhas com a cláusula IN.

SELECT NOME, NUMERO_DEPT, SALARIO FROM EMPREGADOS


WHERE NUMERO_DEPT IN (10, 20) ORDER BY NUMERO_DEPT, SALARIO;

Exemplo 8: Selecionando linhas com a cláusula LIKE.

SELECT NOME, SALARIO FROM EMPREGADOS WHERE NOME LIKE 'S%'


ORDER BY NOME;
OU
SELECT NOME, SALARIO FROM EMPREGADOS
WHERE NOME NOT LIKE '%I%'
ORDER BY NOME;
OU
SELECT NOME, SALARIO FROM EMPREGADOS
WHERE NOME LIKE '_A%';

Observações:
- "%" representa nenhum ou muitos caracteres.
- "_" representa um único caracter.

Exemplo 9: Selecionando linhas com a cláusula IS NULL.

A coluna Num_supervisor contém o número do empregado que supervisiona o empregado corrente.

SELECT NUMERO, NOME, CARGO, NUM_SUPERVISOR FROM EMPREGADOS;

Com esta Query recuperamos o único empregado da empresa que não é gerenciado por ninguém, isto é, o
Presidente da empresa.

SELECT NOME, CARGO, NUM_SUPERVISOR FROM EMPREGADOS WHERE NUM_SUPERVISOR IS NULL;

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, CARGO, NUM_SUPERVISOR FROM EMPREGADOS WHERE NUM_SUPERVISOR = NULL;

Exemplo 10: Selecionando linhas com operadores lógicos.

SELECT NOME, SALARIO, NUMERO_DEPT FROM EMPREGADOS WHERE SALARIO >= 3000 AND
(NUMERO_DEPT = 10 OR NUMERO_DEPT = 30);

Funções Aplicadas a Linhas

Há dois tipos de funções:

- 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.

Exemplos de Funções de Linha:

- LOWER ('UFF') » uff


- UPPER ('uff') » UFF
- INITCAP ('UNIVERSIDADE FEDERAL') » Universidade Federal
- CONCAT ('String1', 'String2') » String1String2
- SUBSTR ('String', 1, 3) » Str
- LENGTH ('UFF') » 3
- NVL (SAL, 0) » Se SAL for NULO seu valor será convertido para zero.
- ROUND (78.731, 2) » 78.73 (Até 4 p/ baixo, Acima de 4 p/ cima)
- ROUND (78.731, 0) » 79
- TRUNC (78.731, 2) » 78.73
- TRUNC (78.731) » 78
- MOD (100, 30) » 10

Exemplo 1: Utilização da função UPPER em uma sentença SQL.

SELECT NOME, SALARIO, NUMERO_DEPT FROM EMPREGADOS WHERE UPPER (NOME) = 'CELIO';

Exemplo 2: Utilização das funções SUBSTR e LENGTH em uma sentença SQL.

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;

Exemplo 3: Uma junção entre a tabela de Empregados, a tabela de Departamentos e a tabela de


Dependentes.
SELECT E.NOME, E.NUMERO_DEPT, DPT.NOME, DEP.NOME FROM EMPREGADOS E, DEPARTAMENTOS DPT,
DEPENDENTES DEP WHERE E.NUMERO_DEPT = DPT.NUMERO AND E.NUMERO = DEP.NUMERO_EMP;

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.

A cláusula Group By divide as linhas de uma ou mais tabelas em grupos de linhas.

A cláusula Having seleciona os grupos que serão aceitos.

Funções de Grupo Existentes:

• 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 2: Um único grupo definido através da cláusula Where.


SELECT AVG(SALARIO), MAX(SALARIO), MIN(SALARIO), SUM(SALARIO) FROM EMPREGADOS WHERE
CARGO LIKE 'VEND%';

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;

Exemplo 5: Utilização da função COUNT para contar o número de empregados na tabela.


SELECT COUNT(NVL(PERC_COMISSAO, 0)) 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;

As cláusulas do comando Select são avaliadas na seguinte ordem:

Se o comando SQL contém a cláusula WHERE, o SGBD seleciona as linhas candidatas.


O SGBD identifica os grupos especificados pela cláusula GROUP BY.
A cláusula HAVING restringe os grupos resultantes que não estão de acordo com os critérios especificados
nesta cláusula.

A recuperação de dados com subconsultas (Subqueries)

Uma subconsulta é um comando SELECT embutido em uma cláusula de outro comando SQL.

Quando e como utilizar:

Escreva subconsultas para recuperar dados baseados em critérios desconhecidos.


Pode ser muito útil quando se necessita selecionar linhas de uma tabela com uma condição que depende
dos dados que estão na própria ou em outra tabela.
Subconsultas não podem conter a cláusula ORDER BY.
Duas classes de operadores de comparações são utilizadas em subconsultas:
Operadores de uma única linha: >, =, >=, <, <=, <>
Operadores de multiplas linhas: IN e NOT IN.
Como uma subconsulta é processada?
Primeiramente é executado o comando SELECT aninhado.
Em seguida o resultado é utilizado em uma condição da consulta principal.

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.

SELECT NOME, SALARIO FROM EMPREGADOS WHERE NUMERO_DEPT = (SELECT NUMERO_DEPT


FROM EMPREGADOS WHERE NOME = 'JOSE');

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.

SELECT NOME, NUMERO_DEPT, SALARIO FROM EMPREGADOS WHERE NUMERO_DEPT = (SELECT


NUMERO FROM DEPARTAMENTOS WHERE LOCAL = 'RIO' OR NOME = '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.

SELECT NOME, NUMERO_DEPT, SALARIO FROM EMPREGADOS WHERE NUMERO_DEPT IN (SELECT


NUMERO FROM DEPARTAMENTOS WHERE LOCAL = 'RIO' OR NOME = 'VENDAS');

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.

SELECT NUMERO_DEPT, AVG(SALARIO) FROM EMPREGADOS GROUP BY NUMERO_DEPT HAVING


AVG(SALARIO) >= (SELECT AVG(SALARIO) FROM EMPREGADOS WHERE NUMERO_DEPT = 30);

A recuperação de dados com subconsultas correlacionadas

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.

SELECT NOME FROM EMPREGADOS WHERE 2 IN ( SELECT NUMERO_PROJ FROM TRABALHAM


WHERE NUMERO_EMP = E.NUMERO);

Exemplo 2: A mesma query acima sem a utilização de subconsulta correlacionada.

SELECT NOME FROM EMPREGADOS E, TRABALHAM T WHERE E.NUMERO = T.NUMERO_EMP AND


T.NUMERO_PROJ = 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.

Exemplo 1: Obter o nome dos empregados que trabalham no projeto nº 2.

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, TRABALHAM T WHERE E.NUMERO = T.NUMERO_EMP AND


T.NUMERO_PROJ = 2;

Exemplo 2: Obter o nome dos empregados que não trabalham no projeto nº 2.

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;

União (Union e Union All)

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.

Já o exemplo nº 2 retornará os números de projeto 2, 3 e 3.

Qualquer número de SELECTS pode ser unido pelo UNION.

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).

Programação de Banco de Dados - parte 4

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:

Exemplo 1: Consideremos duas Tabelas uma Tabela FUNCIONARIOS (idfuncionario,nome) e outra


DEPENDENTES (idfuncionario,nome,grauparentesco). Onde o idfuncionario na tabela FUNCIONARIOS é a
chave primária e na tabela DEPENDENTES é a chame estrangeira, a chave primária da tabela
DEPENDENTES seria a concatenação de idfuncinario + nome. Sendo que a tabela DEPENDESTES é tabela
fraca de FUNCIONARIOS ( isso quer dizer que só existe um registro em Dependentes a partir da tabela
funcionário, isso também ocorre quando na chave primaria da tabela participa a chave primária de outra
tabela), bem voltando ao caso, suponha que queremos retornar todos os funcionários e caso o funcionário
tenha dependente retornamos também. A consulta com WHERE ficaria assim

SELECT F.Idfuncionario , F.nome,D.Nome From FUNCIONARIOS as F, DEPENDENTES as D Where


F.Idfuncionario= D.Idfuncionario

Bem nesta consulta somente iria retornar os funcionários que possuem dependentes, os que não tivessem
dependentes ficaria de fora.

Exemplo 2: Consideremos ainda a tabela FUNCIONARIO e outra tabela


TELFUNCIONARIO(idfuncionario,numero), muito comum , pois o campo telefone é multivalorado ( permite
vários telefones) por isso foi criado uma tabela somente para os telefones. Bem neste caso temos o
mesmo problema os funcionários sem telefone não iriam ser retornados usando a clausula WHERE.

A operação JOIN combina colunas de duas tabelas se as linhas possuírem campos de correspondência.
Sua sintaxe é:

FROM tabela1 INNER JOIN tabela2 ON tabela1.col = tabela2.col


As duas colunas não precisam ser correspondidas com o operador igual, embora este seja o método mais
comum de correspondência de várias tabelas.Você pode usar qualquer um dos operadores relacionais
( >,>=,<,<= e <>). Além disso você pode combinar várias retrições com operadores lógicos.Por
exemplo:
FROM tabela1 INNER JOIN tabela2 ON tabela1.col1 = tabela2.col1 AND tabela1.col2 <> tabela2.col2

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

Problema: ver os pedidos de cada cliente:

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.

SELECT DISTINCT Vendedor.Nome


FROM Vendedor INNER JOIN Pedido
ON Vendedor.Cod_Vendedor=Pedido.Cod_Vendedor
INNER JOIN ItemPedido
ON pedido.num_pedido = itempedido.num_pedido
INNER JOIN Produto
ON itempedido.Cód_produto= Produto.Cod_Produto
WHERE Quantidade > 5 AND
Produto.Descrição='CHOCOLATE'
ORDER BY Vendedor.Nome

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.

SELECT cidade,COUNT (nome_cliente),


FROM Cliente INNER JOIN Pedido
ON Cliente.Cod_Cliente=Pedido.Cod_Cliente
INNER JOIN Vendedor
ON pedido.Cód_Vendedor = vendedor.Cód_Vendedor
WHERE Cidade In('Rio de Janeiro','Niteroi') AND
Data BETWEEN #01/01/2004# AND #31/01/2004#
GROUP BY Cidade

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.

O Outer Join possui 3 tipos:

LEFT OUTER JOIN - são incluidas todas as linhas da primeira tabela na expressão.

RIGHT JOIN - são incluídas todas as linhas da segunda tabela na expressão.


FULL OUTER JOIN - são incluidas todas as linhas de ambas as tabelas, as que satisfazem a expressão e
as que não satisfazem.

INSERT, UPDATE E DELETE

Adicionado Registro na Tabela - INSERT

Sintaxe: INSERT INTO nome Tabela (nome das colunas ) VALUES ( valores )

Exemplo:

INSERT INTO Clientes (nome,endereco) VALUES ('LUCIANA', 'AV ATLANTICA').


Podemos omitir a lista dos nomes das colunas, neste caso a lista de valores deve estar na mesma ordem
que na tabela no banco, e a tabela não pode ter nenhum campo AutoNumeric, pois não pode ser omitido
nenhum valor.
Outro mecanismo para passar valores de colunas para a instrução INSERT é seleciona-las de outra tabela.
Exemplo:
INSERT INTO PhoneBook Select ContactName,Phone,Fax From Customers.
Esse tipo de inserção permite a inclusão de várias linhas de uma só vez.

Alterando Registros - UPDATE


Sintaxe: UPDATE nome_tabela SET coluna1=valor1, coluna2=valor2... Where condição
Problema: Alterar o valor unitário do produto 'parafuso'
UPDATE Produto
Set val_unit = 2.00
Where Descrição= 'parafuso'
Problema: atualizar o salario fixo de todos os vendedores em 30% mais bonificação de 100
UPDATE Vendedor
Set Salário = (Salário * 1.30) + 100
Neste comando não foi usado a clausula WHERE neste caso todos os registros da tabela Vendedor foram
atualizados.
Problema: Acrescentar 2,5% ao preço dos produtos que estejam abaixo da média dos preços.
UPDATE Produto
Set Val_Unit = Val_Unit * 1.25
Where Val_Unit < (Select AVG(Val_unit) From Produto)

Apagando Registros - DELETE


Sintaxe: DELETE From nome_tabela Where condição.
Exemplo: DELETE FROM Cliente Where IdCliente = 1.
Se omitir a clausula WHERE todos os registros da tabela são deletados.
OBS: Tanto no comando UPDATE como no DELETE na clausula WHERE pode-se usar SELECT com todos as
suas possibilidades.
Terminamos aqui de falar sobre os comandos DML, passaremos agora a abordar os comandos DDL mais
precisamente o comando CREATE TABLE.

CRIAÇÃO DE TABELAS - CREATE TABLE


Estruturas de dados que podem ser criadas com um SGBD.
Estruturas de Dados Descrição
Tabela Armazena dados.
Visão Representa logicamente subconjuntos de dados de uma ou mais tabelas.
Seqüência Gera valores de chaves primárias.
Índice Melhora o desempenho de algumas consultas.
Tabelas
• Uma tabela pode ser criada a qualquer momento.
• Não é necessário especificar seu tamanho, no momento da sua criação, embora seja possível.
• A estrutura de uma tabela pode ser modificada a qualquer momento, sem a necessidade de se tirar
o banco do ar.
• Quando uma tabela é criada sua definição é armazenada no dicionário de dados.
• Para se poder criar tabelas é preciso ter o privilégio de CREATE TABLE e o direito de utilizar algum
espaço em disco, alocado para o banco de dados.

Quem concede estes direitos para os usuários do banco é o Administrador de Banco de Dados. (DBA)

Comando Create Table

Exemplo:

CREATE TABLE FORNECEDORES


(NUMERO NUMBER(2) PRIMARY KEY,
NOME VARCHAR2(25) NOT NULL,
TELEFONE CHAR(7),
ENDERECO VARCHAR2(20),
VALOR_FORNEC NUMBER (8,2));

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 1: Constraints Primary Key e Not Null.


CREATE TABLE FORNECEDORES
(NUMERO NUMBER(2)
CONSTRAINT FORNECEDORES_NUMERO_PK PRIMARY KEY,
NOME VARCHAR2(25)
CONSTRAINT FORNECEDORES_NOME_NN NOT NULL,
TELEFONE CHAR(7)
CONSTRAINT FORNECEDORES_TELEFONE_NN NOT NULL,
ENDERECO VARCHAR2(20),
VALOR_FORNEC NUMBER (8,2));

Exemplo 2: Constraints Primary Key e Not Null.


CREATE TABLE DEPARTAMENTOS
(NUMERO NUMBER(2)
CONSTRAINT DEPARTAMENTOS_NUMBER_PK PRIMARY KEY,
NOME VARCHAR2(14)
CONSTRAINT DEPARTAMENTOS_NOME_NN NOT NULL,
LOCAL VARCHAR2(13));
Exemplo 3: Constraint Check e Integridade Referecial com a própria tabela de Empregados e com a tabela
de Departamentos.
CREATE TABLE EMPREGADOS
(NUMERO NUMBER(4)
CONSTRAINT EMPREGADOS_NUMBER_PK PRIMARY KEY,
NOME VARCHAR2(10),
SOBRENOME VARCHAR2(10),
CPF CHAR(11)
CONSTRAINT EMPREGADOS_CPF_UN UNIQUE,
CARGO VARCHAR2(9),
NUM_SUPERVISOR NUMBER(4)
CONSTRAINT EMP_EMP_NUM_SUPERVISOR_FK
REFERENCES EMPREGADOS (NUMERO),
DT_ADMISSAO DATE,
SALARIO NUMBER(7,2),
PERC_COMISSAO NUMBER(4,2)
CONSTRAINT EMPREGADOS_PERC_COMISSAO_CK
CHECK (PERC_COMISSAO IN (10, 12.5, 15, 17.5, 20)),
NUMERO_DEPT NUMBER(2)
CONSTRAINT EMPR_DEPARTAMENTOS_NUMERO_DEPT_FK
REFERENCES DEPARTAMENTOS (NUMERO)
ON DELETE CASCADE);

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));

Observações sobre a Constraint Primary Key:


- A constraint Primary Key é uma combinação das constraints Unique e Not Null.
- Um índice único é automaticamente criado.

Observações sobre a Constraint Unique:


- Designa uma coluna ou uma combinação de colunas de tal forma que duas linhas não possam ter o
mesmo valor.
- Valores nulos são aceitos.
- Automaticamente é criado um índice único para a(s) coluna(s) especificada(s).

Observações sobre a Constraint Foreign Key:


- Estabelece um relacionamento com a chave primária ou única da mesma ou de outra tabela.
- Deve referenciar um valor existente na tabela pai ou ser nulo.
- Chaves estrangeiras são baseadas em dados e são puramente lógicas, isto é, não são ponteiros
físicos.
- Uma chave estrangeira, parte de uma chave primária, não pode ser nula pois uma chave primária
não pode ser nula, nem parcialmente nula.
- Havendo a cláusula ON DELETE CASCADE, uma deleção na tabela pai causa a deleção das linhas
relacionadas na tabela filho.

Outras Formas de se Validar uma Restrição de Integridade


- Triggers
- Procedimentos ou funções armazenados no servidor de banco de dados
- Através do código na própria aplicação.
Como Criar uma Tabela Através de uma Subconsulta
CREATE TABLE EMPREGADOS_VENDAS
AS SELECT *
FROM EMPREGADOS
WHERE CARGO = 'VENDEDOR';
Observação:
- A tabela Empregados_Vendas é criada contendo todos os empregados no cargo de vendedores.
- Apenas a constraint NOT NULL é copiada.
Bem o assunto SQL não termina aqui, não abordamos os comando DCL responsável pelo acesso e
permissão de usuários.Também não falamos sobre Trigers, Procedures, Function e View. Devido a pedidos
para falarmos logo do componente ADO nos próximos artigos vamos "cair" em cima da prática com acesso
a banco de dados pelo ADO, com exemplos de códigos realizando: consultas, inserções, deleções,
alterações, transações, carregamento de listas, controle de concorrência, backup e etc, enfim tudo com
exemplos e feito via código, não usarei componentes para acesso como o Data Control, que considero
muito ruim e limitado.Até a próxima.

Programação de Banco de Dados - parte 5

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.

A utilização do ADO basicamente consiste em declarar 2 variáveis.Uma responsável unicamente pela


conexão, e a outra pelas consultas do banco e para isso usamos duas sub-classes da classe ADO que são
"Connection" e "Recordset" respectivamente.

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.

Bem na programação com Banco de Dados consistem basicamente em:

• Abrir Conexão com Banco.


• Executar consultas, Inserções, Alterações e Deleções.
• Fechar Conexão com Banco.

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.

Declaração das Variáveis do ADO


Neste caso basta declarar uma única variável de conexão e uma única variável de consulta (Recordset).
Declare as variáveis do banco em um Modulo da seguinte forma:
Public pVarConBanco as New ADODB.Connection (Variável de conexão)
Public pVarRegBanco as New ADODB.Recordset (Variável de Consulta)
Além de efetuar a conexão a variável do tipo ADODB.Connection (abordada a partir de agora somente por
Connection) também executa comandos SQL com exceção do comando SELECT, pois essa variável não é
do tipo Collection, não tem propriedades para tratamento de dados.No entanto não vamos usar a variável
Connection para execução de comandos SQL, a variável connection somente será usada para a conexão
com o banco.

Conexão com o Banco (CONNECTION)


Pela propriedade "ConnectonString do objeto connection informa a string de conexão que difere de banco
para banco. Neste caso um projeto bem planejado a migração de um banco para um mais potente é
extremamente fácil, bastando mudar um linha de código que é a string de conexão.
String de Conexão para Access

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)

As variáveis são o seguinte:


PathBanco = ao caminho do banco de dados.
NomeBanco= ao nome da banse de dados.
SenhaBanco= senha do banco de dados.

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.

Public Sub MSG_ERRO(Procedimento As String)


MsgBox "O seguinte erro aconteceu : " + Str(Err) + Chr(10) + Error(Err) + Chr(10) + "Comunique ao
Suporte
Técnico." + Chr(10) + "Rotina " + Procedimento, vbCritical, Cabecalho
End Sub

Manipulando o Banco de Dados (RECORDSET)


Através da variável do tipo Recordset podemos manipular banco de dados.Pelo evento "OPEN" é possível
executar qualquer comando SQL. Ao usar o Recordset na verdade esta se criando um cursor no banco
responsável pela manipulação e recuperação dos dados. Se utilizar somente o evento "Open" sem
especificar suas propriedades então todas as propriedades serão default e o código funciona
normalmente.Mas se tratando de manipulação do banco é necessário criar o cursor certo para cada tipo de
comando SQL e para que e como você vai manipular os dados de retorno.Para aplicações multiusuario o
controle de concorrência se da pela configuração dessas propriedades.
Antes de começarmos a ver a pratica da classe Recordset vamos ver suas propriedades.

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

pVarRegBanco.CursorLocation = adUseServer (definido do lado do servidor).


pVarRegBanco.CursorLocation =adUseClient (definido do lado do cliente).

OBS: No access a maior parte do processamento se da ao lado do servidor independente da propriedade


CursorLocation, pois o Access não é banco cliente servidor.Com isso aplicações em rede com Access
dependendo do fluxo de acesso não são aconselháveis, pois o fluxo de dados na rede é grande podendo
comprometer a banda de rede.Mas isso se for um número muito alto de dados. Mesmo assim a definição
do CursorLocation no lado do Servidor é importante mesmo para o Access devido ao controle de
concorrência visto a seguir.

CursorType

Propriedade que define o tipo de cursor a ser aberto:

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.

Controle de Concorrência (LockType)

Propriedade que define o tipo de bloqueio do registro:

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.

AdLockBatchOptimistic= Alguns aplicativos podem descarregar um conjunto de registro para o cliente,


cortar a conexão com o servidor, atualizar várias linhas e depois tentar gravar todas as alterações no
banco de dados estabelecendo nova conexão.Este método é usado com conjuntos de registros
desconectados.

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

Public Const pConsNomeBanco As String = "Banco.mdb" 'Nome do Banco de Dados


Public Const pConsSenhaBanco As String = "minhasenha" 'Senha do Banco de Dados
Public pVarConBanco as New ADODB.Connection
Public pVarRegBanco As New ADODB.Recordset
Public pVarPathBanco As String 'Caminho do Banco de Dados
Na função Main ou no Load do primeiro Form da aplicação
If Not Conexao_Banco(pConsNomeBanco,pVarPathBanco,pConsSenhaBanco) Then End

Exemplo para Leitura:


With pVarRegBanco
.CursorLocation = adUseServer
.Open "Select * from Grupo_Produto order by IdGrupo",pVarConBanco, adOpenStatic,
adLockReadOnly
End With

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.

Programação de Banco de Dados - parte 6

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.

Primeiro o que seria uma transação?

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:

'Objeto de Conexão com Banco


Public pVarConBanco As New ADODB.Connection

pVarConBanco.BeginTrans 'Comando de inicio de transação


pVarConBanco.CommitTrans 'Comando de término de transação
pVarConBanco.RollbackTrans 'Comando que cancela a transação.
Os comandos do ADO para transação se resume nesses. Agora um exemplo de uma transação:
Private Function NovaConta(ByVal IdMovimento As Long) As Long
'Retorna Id da Nova Conta(IdVenda)
Dim lVarIdVenda As Long

On Error GoTo T_Erro


pVarConBanco.BeginTrans
On Error GoTo T_Transacao
With pVarRegBanco
.CursorLocation = adUseServer
.Open "Insert Into Venda Values(……)", pVarConBanco, adOpenStatic, adLockOptimistic

On Error GoTo T_Consulta


'Pega Id da Venda
.Open "Select @@Identity From Venda", pVarConBanco, adOpenStatic, adLockReadOnly
lVarIdVenda = pVarRegBanco(0)
.Close

On Error GoTo T_Transacao


'Relaciona Conta com Mesa
.Open "Insert into ContaMesa Values(………)", pVarConBanco, adOpenStatic, adLockOptimistic

On Error GoTo T_Alteracao


'Altera Status Mesa
.Open "Select Status From Mesa Where IdMesa = " & Me.IdMesa & "", pVarConBanco, adOpenStatic,
adLockOptimistic
!Status = EnStatus.Reservada
.Update
.Close
End With
pVarConBanco.CommitTrans

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

Temos um exemplo completo de uma transação que envolve:


- Adicionar uma venda na tabela VENDAS.
- Pegar o código autonumerico do registro que acabamos de adicionar.
- Inserir esse código da venda na tabela CONTAMESA
- Alterar o status da mesa colocando ela como ocupada.
Logo para criar uma venda deve ter todas as alterações acima, se uma falhar vai quebrar a regra do
negócio.
Comentando o código:
» pVarConBanco.BeginTrans 'Inicio da transação.
Para cada alteração da transação vamos efetuar pela classe Recordset abordada no artigo cinco.
» Inclusão do registro na tabela Vendas, repare que o objeto de conexão dessa linha é o mesmo que
iniciou a transação. Usando Insert pelo variável Recordset não é necessário fechar o cursor
pVarRegBanco.Open "Insert Into Venda Values(……)", pVarConBanco, adOpenStatic, adLockOptimistic
» Esta consulta com o campo especial @@Identity vai retornar o código autonumeric do registro que foi
adicionado durante esta transação.Esse consulta só funciona se usar transação.
pVarRegBanco.Open "Select @@Identity From Venda", pVarConBanco, adOpenStatic, adLockReadOnly
lVarIdVenda = pVarRegBanco(0) 'Atribui a variável local o valor retornado pela consulta
pVarRegBanco.Close 'Fecha o cursor
» Aqui fazemos outra inclusão, sendo que agora na tabela ContaMesa, usando o valor da variável
lvarIdVenda.
PVarRegBanco.Open "Insert into ContaMesa Values(" & lvarIdVenda & ")", pVarConBanco, adOpenStatic,
adLockOptimistic
» Fazemos agora uma alteração no Status da Mesa na tabela Mesa.
A consulta trazendo o registro desejado.
PVarRegBanco.Open "Select Status From Mesa Where IdMesa = " & Me.IdMesa & "", pVarConBanco,
adOpenStatic, adLockOptimistic
PVarRegBanco!Status = EnStatus.Reservada'Atribuição do novo valor ao campo.
PVarRegBanco .Update'Confirmar a alteração.
PVarRegBanco.Close 'Fecha o cursor.
» Por fim confirmamos a alteração.Somente neste ponto que todas as alterações serão confirmadas.
pVarConBanco.CommitTrans
Tratamento dos Erros
» Ao fazer uma transação deve ter uma atenção maior ao tratamento de erro. Repare que antes de
chamar o BeginTrans tenho um tratamento de erro, onde não faz nada somente chama um procedimento
padrão que exibe uma mensagem de erro para o usuário. Esse tratamento é necessário caso a conexão
com o banco tenha sido perdida.
On Error GoTo T_Erro
pVarConBanco.BeginTrans
» Após o BeginTrans temos outro tratamento de erro (T_Transação) , onde colocamos o comando
pVarConBanco.RollbackTrans. Não pode colocar esse comando no tratamento T_Erro, pois vai ocorrer um
erro runtime, pois não pode chamar RollBack sem ter uma transação iniciada, logo a aplicação vai abortar
pois deu erro dentro do tratamento de erro.
» Antes da consulta do código autonumeric alteramos novamente a referencia do erro (T_Consulta). Para
pode chamar o comando .Close do Recordset.
» Depois retornamos o tratamento de erro para T_Transação, pois o próxima ação é um Insert usando a
classe recordset. Logo no tratamento de erro dessa ação não pode estar o comando Close senão gera erro
runtime.
» Depois temos outro tratamento diferenciado antes da atualização do status mesa. Isso para poder
cancelar a atualização pelo comando CancelUpdate. Repare que neste tratamento temos também o
comando Rollback, pois já foi iniciada a transação e ocorrendo qualquer tipo de erro devemos cancelar.
O que dá mais trabalho em uma transação é fazer o tratamento de erro como vocês podem ver, pois a
própria transação em si se resume em três comando do ADO. Um comando interessante que vimos foi a
consulta usando o campo especial @@Identity, onde retorna o código gerado na inclusão. Sendo que só
funciona usando transação.
Assim fico por aqui, fechando o assunto transação. Espero que este artigo ajude mais um pouco a vocês.
No próximo artigo vou falar como executar triggers e procedures pelo ADO. Até a próxima.

Vous aimerez peut-être aussi