Académique Documents
Professionnel Documents
Culture Documents
Esta Apostila é apenas uma breve iniciação ao Servidor de Banco de Dados Firebird.
Não tenho a intenção de esgotar o assunto Firebird/Sql pois trata-se de um sistema
muito complexo. Mesmo assim espero que esta apostila seja útil para quem a for lê-la,
ou mesmo que seja um ponto de partida para um estudo mais aprofundado no
Servidor/Linguagem.
Seu uso é recomendado para pequenas e médias empresas, onde o número de acessos
simultâneos ao servidor não supere 150. Não tenho informações se esse número
aumentou na versão 1.5.
Com meu crescente interesse e estudo do servidor Firebird, pude nos últimos meses
atualizar esta apostila, de modo a torná-la o mais próxima possível das mais atuais
informações acerca do SGDB (Gerenciador de bando de dados).
Atenção: para que você possa criar ou se conectar a um banco de dados, tanto pelo
Isql quanto pelo IBConsole, é necessário que o servidor Firebird esteja rodando.
Algumas pessoas me reportam o seguinte erro, ao tentarem acessar um banco de
dados: unavailable database. Dentre outras causas, a falta do servidor rodando
pode informar este tipo de erro. O arquivo chama-se ibserver.exe (fbserver.exe na
versão 1.5) e está na pasta bin da instalação. Você pode também definir que o
servidor seja iniciado automaticamente na inicialização do sistema operacional no ato
da instalação.
Sumário
2
Parte 1. Introdução aos comandos utilizados para criar/alterar/apagar
objetos a partir do prompt Isql
Para iniciar o prompt de comando já tendo acesso como usuário master, escreva:
Mas para podermos trabalhar em uma pasta específica do disco sem termos que
executar e criar nosso banco na pasta Bin da instalação, adicione o caminho do
executável Isql à variável de ambiente PATH do sistema:
Lembre-se que no caso do linux, você tem que ter permissão para executar o Isql e ter
uma pasta de trabalho com permissão exclusiva de seu usuário para criar e apagar
arquivos, caso não esteja logado como root.
CON>
significa continuação, ou seja, você esqueceu de algo ou o comando ainda não foi dado
por completo.
3
_____________________________________________________________
no Windows:
no Linux:
Você tem um banco de grande capacidade, onde você guarda informações de clientes,
fornecedores, cheques, estoque, contas e finanças de uma grande loja, e a base de
dados esteja instalada em uma plataforma FAT32. Como sabemos, este sistema de
arquivos tem um limite para o tamanho dos arquivos (aproximadamente 2Gb). Com o
aumento de volume de dados nas tabelas do banco, esse limite será facilmente
atingido, impossibilitando então a inclusão de mais dados nas tabelas. Para resolver
este problema, você poderia adaptar seu sistema para outra plataforma, que fosse
mais flexível quanto ao tamanho dos arquivos. Mas esta pode ser uma solução pouco
prática em alguns casos. E mesmo que você esteja utilizando um sistema de arquivos
que aceite arquivos de grandes dimensões, é sábio por questões de performance,
dividir o banco em mais de um arquivo, tendo assim um acesso mais rápido a eles.
______________________________________________________________
no Linux:
4
**inclua quantos arquivos forem necessários**
Atenção: caso você esteja na pasta em que for criar o banco, não é necessário
especificar o caminho.
_____________________________________________________________
Domains são especificações de tipos de dados. Pela lógica, os domains devem ser
criados antes das tabelas, ou quaisquer outros elementos que os usarem. Escreva no
prompt:
______________________________________________________________
Ao criar a tabela você deve especificar, no ato da criação, os campos, seus tipos, as
chaves primárias da tabela, as chaves estrangeiras e em que arquivo a tabela será
criada (tudo em uma linha só). No modelo relacional, cada linha da tabela deve ser
preferencialmente única, tendo para isso, um atributo que a diferencie das demais, não
podendo ser repetido. Caso um único atributo/coluna não seja suficiente para definir a
linha com única, deve-se usar um conjunto de atributos. Este é o conceito de chave
primária. Uma chave estrangeira é uma referência a um campo de outra tabela do
banco de dados. Mais uma vez, por questão de lógica, se você for incluir um campo
que for chave estrangeira na tabela, a tabela originária do campo associado já deverá
existir. Vamos ao exemplo:
Para definir se a tabela a ser criada estará em outro arquivo do banco de dados,
escreva:
Exemplo:
5
SQL>
O atributo Not Null define que o campo não poderá receber valores nulos.
Uma constraint é uma restrição que aplicamos a uma ou mais colunas da tabela.
Quando esta for UNIQUE, significa que a coluna não poderá receber valores
duplicados. A coluna em questão deverá ser obrigatoriamente Not Null. Exemplo:
______________________________________________________________
Índices são objetos com a função principal de aumentar a performance geral do banco
de dados. Também é utilizado para garantir a integridade dos dados gravados nas
tabelas. Quando se tem um índice relacionado com um determinado campo de uma
tabela, a busca por informações nesta coluna pode se tornar mais rápida. E quando se
tem um índice relacionado com campo que seja chave primária ou chave estrangeira, o
índice auxilia para que valores já existentes não sejam adicionados novamente à
coluna e mantém a integridade dos valores existentes em outras tabelas
respectivamente.
Exemplo:
Neste índice, a cláusula Unique garante que nenhum valor duplicado seja inserido na
tabela, index_codigo é o nome do index, Pessoa é a tabela e Código o campo em
questão.
Índices devem ser definidos para colunas que façam parte de consultas com as
cláusulas Join (para junção de tabelas) e Order By e para colunas de chave primária ou
estrangeira.
______________________________________________________________
6
1.5 - Criando generators:
Trigger é um código que é executado quando ocorre uma ação na tabela. Escreva:
set term $ ;
create trigger T_Pessoa for Pessoa before insert as
begin
new.codigo = gen_id(Inc_Pessoa,1);
end$
set term ; $
O comando Set Term define um novo caractere ou conjunto de caracteres como novo
terminador, com o objetivo de não confundir o término da trigger com o término de
um simples comando dentro da mesma.
A linha create trigger T_Pessoa for Pessoa before insert as define como T_Pessoa o
nome da trigger, Pessoa como sendo o nome da tabela à que a trigger deve atuar e,
before insert define que a trigger atuará antes que um novo registro seja inserido na
tabela.
New é uma variável que armazena o novo valor a ser inserido na coluna. Você pode
recuperar o valor de um campo apagado usando a variável Old no lugar de New.
Nesta trigger, utilizei before insert, mas ainda temos as seguintes opções:
Considerando-se que você crie duas ou mais triggers para a mesma tabela, é
necessário informar a ordem em que as triggers serão executadas. Isso se faz com a
cláusula Position, como nos exemplos:
set term $ ;
create trigger T_Pessoa for Pessoa before insert position 0 as
begin
7
new.codigo = gen_id(Inc_Pessoa,1);
end$
set term ; $
set term $ ;
create trigger T_Pessoa2 for Pessoa before insert position 1 as
begin
if (new.nome is null) then
begin
new.Nome = 'Nome não informado.';
end
end$
set term ; $
_____________________________________________________________
Como exemplo, vamos criar uma stored procedure que inclui dados em uma
tabela( uma procedure de execução):
set term $ ;
create procedure ProcPessoa(VarNome varchar(60), VarCpf
varchar(30)) as
begin
insert into Pessoa(Nome, Cpf) values(:VarNome, :VarCpf);
end$
set term ; $
Com o uso desta procedure, é mais fácil incluir um registro na tabela Pessoas,
simplesmente executando o comando:
8
set term $ ;
create procedure UpPessoa(Codigo integer, Nome varchar(60), Cpf
varchar(30)) as
begin
update pessoa set
Nome = :Nome,
Cpf = :Cpf
where Codigo = :Codigo;
end$
set term ; $
Criaremos agora uma procedure para apagar um registro da tabela, tendo como
parâmetro, a chave primária da tabela:
set term $ ;
create procedure DelPessoa(Codigo integer) as
begin
Delete from Pessoa
where Codigo = :Codigo;
end$
set term ; $
set term $ ;
create procedure SelectPessoa(Codigo integer) as
begin
Select Nome, Cpf from Pessoa
where Codigo = :Codigo;
end$
set term ; $
_______________________________________________________________
9
1.8 - Criando Exceções
Exceções são definições de mensagens de erros criadas pelo usuário. São utilizadas em
triggers e procedures. Quando uma exceção é encontrada, a execução da trigger ou da
procedure é imediatamente interrompida e todas as alterações realizadas até o
momento são desfeitas. Exemplo de criação:
Uso:
set term $ ;
create trigger T_Pessoa for Pessoa before insert as
begin
if (new.codigo = 0) then
Exception Codigo_Invalido;
else
new.codigo = gen_id(Inc_Pessoa,1);
end$
set term ; $
______________________________________________________________
1.7.1 – Char(n)
O tipo Char armazena caracteres com o máximo de 32767, 32k. O tamanho da coluna
é definido com o ordinal passado na criação da tabela, Ex.:
Este tipo de dado é utilizado quando se sabe o tamanho exato da coluna, e não é
variável. Se você criar uma coluna de 5 caracteres, e armazenar 3, sobrarão 2
caracteres vazios na coluna.
1.7.2 - Varchar(n)
Neste caso, diferentemente do tipo Char, se você não ocupar todos os campos da
coluna, gravando por exemplo apenas 5 caracteres, os 45 restantes não ficarão
consumindo memória a toa, sendo desconsiderados.
1.7.3 - Date
O tipo Date armazena Datas, e seu tamanho é de 32 bits inteiros longos. Ex.:
1
SQL> create table Pessoa(DiaInicio Date);
1.7.4 - Time
O tipo Time armazena a hora, e seu tamanho é de 32 bits inteiros longos. Ex.:
1.7.5 - TimeStamp
A diferença entre eles é o conceito de precisão, que para o tipo Decimal é de até 18
dígitos, e para o tipo Numeric é de exatamente 18 dígitos.
1.7.7 - Smallint
O tipo Smallint armazena dígitos inteiros com o limite de: -32768 a 32767. Serve para
armazenar dados numéricos pequenos. Ex.:
1.7.8 - Integer
O tipo Integer é um valor inteiro de 32 bits e armazena dígitos inteiros com o limite
de: -2.147.483.648 até 2.147.483.648. Ex.:
1.7.9 - Float
O tipo Float armazena valores de ponto flutuante, mas, com precisão simples de 7
dígitos. Ex.:
1
1.7.10 - Double Precision
O tipo Double Precision armazena valores de ponto flutuante mas com precisão de 64
bits. Ex.:
1.7.11 - Blob
O tipo de Dado BLOB, tem o tamanho variável, mas, o limite do campo Blob que está
na documentação do InterBase, é de 64k por segmento. Este tipo de dado é indicado
para armazenar Textos Longos, Fotos, Gráficos, Ícones, ou qualquer outra informação.
Campos Blob não podem ser indexados. Quando se define uma coluna do tipo Blob,
deve-se informar o subtipo desejado. Os subtipos 0 e 1 são os mais utilizados, e
significam:
Ex.:
Caso você não indique nenhum subtipo, é adotado o subtipo 0 para a coluna.
______________________________________________________________
Assim como a tabela, você pode apagar domains, stored procedures, triggers, etc.,
utilizando-se sempre do comando drop [componente] [nome do componente];
1
______________________________________________________________
______________________________________________________________
Somente o que pode ser alterado nos generators é seu valor, mas não é utilizando o
comando Alter. Veja o exemplo:
1
SQL> set generator Inc_Pessoa to 0;
Isto fará com o que o generator retorne se valor para 0 (Zero). Qualquer número pode
ser colocado no lugar do ordinal. Mas é necessário tomar cuidado ao se alterar o valor
do generator, pois, se ele estiver sendo usado para emular um campo autonumerado,
podem ocorrer problemas de violação de chave primária, ou seja, a tentativa de gravar
uma chave primária que já existe.
__________________________________________________________________
set term $ ;
alter procedure DelPessoa(Codigo integer) as
begin
if ( :Codigo <> 0 ) then
begin
Delete from Pessoa
where Codigo = :Codigo;
end
end$
set term ; $
______________________________________________________________
SLQ> alter table Cliente drop Cpf, add Profissao varchar (30);
SQL>
SQL> alter table pessoa add constraint codigo check( codigo > 0
and codigo < 99999 );
Aqui, eu adicionei uma validação para o campo Codigo. Para a constraint deve ser
passado o nome do campo onde aplicar a restrição:
constraint codigo check ( codigo > 0 ...
1
______________________________________________________________
Você pode alterar o corpo da trigger, apenas copiando o código utilizado na criação, e
alterando o que for necessário:
set term $ ;
alter trigger TPessoa before insert as
begin
new.codigo = gen_id(Inc_Pessoa,1);
update pessoa set Profissao = 'funcionario'
where codigo > new.codigo;
end$
set term ; $
Quando a trigger estiver Inactive, ela não será executada quando ocorrer a ação na
tabela para a qual foi especificada.
______________________________________________________________
Talvez o único ponto negativo do Isql para criação de bancos, seja a de não possuir
uma estrutura versátil para você poder criar/alterar os componentes do banco. É muito
cansativo e improdutivo você ter que escrever toda a estrutura do componente caso
tenha errado, ou ter que alterar a estrutura do componente caso tenha esquecido de
alguma coisa. Isso porque alterar a estrutura de um componente pode acarretar em
problemas, como referências a componentes já criados, dificultando bastante o
serviço. Mas há uma maneira de resolver esse tipo de problema: utilizando arquivos
externos para a criação do banco. Esses arquivos são arquivos do tipo texto que você
mesmo cria, contendo a estrutura dos componentes a serem criados, também
conhecidos como scripts. Por exemplo:
Salve o arquivo. Em seguida, se conecte a um banco existente pelo Isql. Caso não
tenha nenhum, crie um banco simples e escreva no prompt do Isql:
1
SQL>
Lembre-se que no caso do Linux, há diferença entre caixa alta e caixa baixa, ou seja,
minúsculas e maiúsculas diferem.
Você pode utilizar este método para criar praticamente todas as estruturas que o
banco conterá, inclusive o próprio banco, bastando para isso, passar o comando que
você normalmente digitaria no prompt em um arquivo, e depois incluí-lo com o
comando input no Isql.
Após o comando input Banco.sql ser dado, o Isql perguntará Commit current
transaction (y/n)? para confirmar se você quer gravar as informações atuais, ou seja,
efetivar a criação do banco de dados. Digite y(Yes) e o banco será criado com base nas
informações contidas no arquivo.
Este é um método bastante versátil, mas você não precisa escrever toda a estrutura
do banco em um único arquivo. Você pode por exemplo, escrever as estruturas das
tabelas em um arquivo chamado Tabelas.sql, a estrutura dos domains em um arquivo
chamado Domains.sql, e assim por diante, organizando muito melhor seu banco de
dados. E para ligar todos os arquivos, e criar o banco a partir de um único comando,
faça o seguinte:
1
Mas este método também não é um método perfeito, apesar de ser eficiente e
produtivo, pois se você quiser alterar as estruturas de um banco existente, será
necessário apagar o banco que você deseja alterar, perdendo assim, os registros
gravados.
Por isso, considere no caso de você necessitar alterar a estrutura de um banco que já
esteja em uso o comando Alter, para não perder as informações já armazenadas.
______________________________________________________________
ou
Exemplo:
A cláusula Show aplicada a um domínio, mostra o tipo do dado; aplicada a uma Stored
Procedure ou a uma Trigger, mostrará o código da mesma, e aplicada a um Generator
mostrará o seu valor atual.
______________________________________________________________
1
Parte 2. Introdução à sintaxe de consulta Sql
Talvez o maior poder da linguagem Sql seja o de extrair os dados das mais variadas
opções possíveis, com segurança e rapidez.
A consulta mais simples possível é conseguida com o comando:
Para colocar uma restrição a uma consulta, é usada a cláusula Where, como a seguir:
Neste exemplo, todos os registros cujo campo Codigo seja igual a 1 serão mostrados
na consulta.
Já neste caso, apenas o campo Nome dos registros cujo campo Codigo seja igual a 1
serão mostrados na consulta.
Todos os registros cujo campo Nome seja igual a 'primeiro' serão mostrados na
consulta.
1
Todos os registros cujo campo Nome contenha a letra n minúscula, em qualquer
posição serão mostrados na consulta.
Atenção: o Firebird é sensível a caixa, ou seja, se for passado n minúsculo, o select
procurará por n minúsculo, e ignorará N maiúsculo.
O Like aceita frases inteiras, como '%Comunicacao de dados%'.
SQL> select nome from pessoa where nome starting with 'A';
Esta consulta retornará todos os registros cujo campo Nome comece com o A
maiúsculo. A Consulta também se aplica a campos de outros tipos:
O resultado será o campo Nome dos registros cujo campo Codigo inicie-se com o
ordinal 2.
SQL> select nome from pessoa where codigo starting with 2 and nome
like '%t%';
SQL> select codigo, nome from pessoa where codigo < 10;
Onde serão mostrados os campos Codigo e Nome de todos os registros, cujo campo
Codigo tenha o valor menor que 10.
Você ainda pode definir um texto a ser mostrado quando a consulta for realizada, para
determinado campo ou valor. Por exemplo, como resultado do último comando
apresentado (select count(*) from pessoa), teríamos:
Count(*)
--------------
10
SQL>
Total de registros
------------------
10
1
SQL>
Você pode definir qualquer texto para qualquer coluna apresentada na consulta, como
por exemplo:
Para ordenar uma consulta por ordem alfabética pelo campo Nome por exemplo:
Suponhamos que temos 5 registros cujo nome da pessoa seja Jose; faremos a
seguinte consulta:
NOME Numero
============================ ============
Jose 5
A cláusula Having desempenha a mesma função que o Where, mas com campos
calculados. Por exemplo:
2
SQL> select codigo, nome, valor from pessoa;
Sum() é uma função que calcula a soma dos campos da tabela, e Having restringe a
busca para os cálculos cujo resultado derem maior que 2, e sendo agrupados pelo
campo Nome. Neste caso, Group By vai agrupar todos os registros que tiverem o
mesmo conteúdo para o campo Nome, e a função Sum() vai atuar no somatório desses
registros iguais, gerando um único valor para o agrupamento.
Caso você omita a string "Valor agregado", não será gerado um erro, o que aparecerá
será sum(valor) como nome da coluna.
Neste ponto podemos montar uma consulta mais complexa como a seguir:
Nesta consulta, serão retornados apenas 5 registros, e dois deles serão ignorados, ou
seja, aparecerão 3 registros como resultado.
2
First faz retornar o número de registros especificados com o ordinal passado, enquanto
que o Skip ignora o número de registros representados pelo ordinal passado. No
exemplo acima, 2 dos 5 registros encontrados foram ignorados.
São comandos úteis principalmente para reduzir o tráfego na rede, evitando que
muitos dados sejam retornados à toa. E é claro, para restringir mais a consulta, podem
ser utilizados em conjunto com Where, Group By ou Having.
______________________________________________________________
2
Parte 3. Gravando e desfazendo alterações
SQL> commit;
SQL> rollback;
Quando você grava um novo registro em uma tabela, altera ou exclui um registro
existente, o registro é alterado em memória, ou seja, a gravação não é efetivamente
gravada no arquivo de banco de dados. Isso é extremamente útil em casos onde a
integridade dos dados é crítica.
Vou exemplificar:
Você necessita gravar dados em várias tabelas, e uma gravação depende do sucesso
da gravação de outra tabela, como campos calculados, chaves estrangeiras etc.. No
caso de uma gravação em uma tabela que não foi feita corretamente, devido a
inúmeros contratempos, a gravação nas outras tabelas ficará prejudicada, gerando
valores errados e desatualizados. É aí que entra o Rollback. Ele irá desfazer todas as
atualizações feitas no banco, que ainda estejam em memória, desde que não tenham
sido efetivadas com o Commit. Neste caso o Rollback não funcionará.
E caso todas as alterações no banco tenham ocorrido corretamente, o commit efetivará
a gravação dos dados no arquivo.
2
Parte 4. Utilização do IBConsole
Como eu já havia dito no início desta apostila, o Firebird não tem nenhuma interface
gráfica para manipulação de dados nativa. Porém aplicativos com este fim podem ser
encontrados na internet. Estarei mostrando brevemente a utilização do programa
IBConsole, da Borland, que é Open Source, e pode ser obtido no endereço:
http://info.borland.com/devsupport/interbase/opensource/ . A versão disponível é a
1.0.0.320.
2
Neste ponto, você pode definir o tamanho das páginas de dados do banco, bem como
o Character Set e o Dialeto. O dialeto deve ser o 3, já o tamanho das páginas e o
Character Set pode ser definido segundo as necessidades do banco.
Depois de tudo configurado dê OK. O banco será criado, e o alias adicionado a guia
Databases.
Com o banco já criado, agora podemos criar as tabelas, procedimentos, domains, e
demais estruturas necessárias. Para tanto, vá ao menu Tools – Interactive SQL.
Uma tela com a seguir será mostrada:
Nesta tela você pode entrar com quaisquer comandos pertinentes a linguagem SQL,
tanto para criação dos objetos do banco, como na figura, como para extração de dados
com o comando Select e inclusão/alteração/exclusão.
No caso da extração de dados, os resultados são mostrados na guia Data na parte de
baixo da tela.
2
Dê OK, e o banco estará disponível para utilização.
Vimos que através do menu Tools – Interative SQL podemos informar comandos SQL
válidos, tanto para alteração e criação de objetos como para dados do banco.
Todos os comandos anteriormente passados, para serem executados em linha de
comando, podem ser executados aqui. Mas no caso de consultas e alterações de dados
nas tabelas do banco, o IBConsole abre uma transação para o processo, transação esta
que pode ser finalizada tanto com Commit como com Rollback, ambos já discutidos.
O Firebird sempre utilizou transações em todos os processos, mas isso não se torna
transparente para o usuário, pois é ele quem controla isso. No momento que você
executa um comando Update pelo Isql por exemplo, o Firebird automaticamente inicia
uma transação(caso não exista nenhuma ativa), que deve ser finalizada com Commit
ou Rollback. Agora no Interactive SQL do IBConsole, ele informa que uma nova
transação foi iniciada, escrevendo Transaction is ATIVE na barra de status.
2
O recurso de transação não existe em bancos Desktop(Paradox por exemplo), apenas
em bancos Cliente/Servidor, e tem a função principal de manter a integridade e
segurança dos dados, principalmente onde existem muitos clientes acessando as
mesmas informações ao mesmo tempo.
Quando você inicia uma nova transação, e executa um Select, os dados retornados,
serão os dados mais atuais gravados na(s) tabelas(s).
==========================================
Espero que este pequeno tutorial tenha sido proveitoso, que tire suas principais
dúvidas com relação ao banco de dados apresentado, ou que seja um ponto de partida
para o estudo deste sistema complexo que é um banco de dados relacional.
É isso! (;-)