Você está na página 1de 186

TABELA DE CONTEÚDO

Introdução
Instalando MySql
Primeiros passos com MySql
Lista de contatos
Populando uma tabela
Pesquisando uma tabela
Controle de vendas
Atualizando e apagando registros
Índices
Junções
Clínica Odontológica
Melhorando consultas - agrupando e mais
Visões
Mais funções
Transações
Normalização
Introdução

Neste livro vamos apresentar a linguagem SQL (Structured Query


Langue), utilizando o sistema gerenciador de bases de dados (SGBD)
MySql.

Sendo um livro introdutório, iremos apresentar os aspectos


fundamentais da SQL, mas não iremos abordar todos os tópicos
relacionados a bancos de dados, como segurança, otimização, entre
outros.

Apesar de utilizarmos o MySql, a linguagem SQL é a mesma (com


algumas pequenas exceções) para qualquer SGBD.

Às vezes, um SGBD específico possui algumas extensões à


linguagem não encontradas em outros, mas estaremos tratando neste livro
da SQL em geral, que pode ser utilizada em qualquer SGBD e, caso
alguma característica seja específica do MySql, faremos tal resalva.

Você ficará surpreso ao perceber como a linguagem SQL é simples


e, ao mesmo tempo, poderosa.

Com alguns poucos comandos - CREATE, SELECT, INSERT,


DELETE, UPDATE - se consegue fazer quase tudo o que é necessário
para se criar, manipular e consultar uma base de dados.

Este não pretende ser um livro acadêmico, não vamos dar


definições precisas do que seja uma base de dados, uma tabela, uma
relação, etc, mas sim nos preocupamos em apresentar tais conceitos e
mostrar como se aplicam, na prática, na criação e uso de uma base de
dados.

Não falaremos também da história dos bancos de dados, dos vários


modelos existentes, das vantagens e desvantagens em se usar um banco
de dados, pois acreditamos que o interesse do leitor se concentra em
aprender como utilizar a SQL para manipular dados em um SGBD real
atual.

Também não iremos nos aprofundar na teoria envolvida no modelo


relacional, que é o modelo utilizado por praticamente todos os SGBDs
modernos.

Apresentaremos, certamente, os conceitos fundamentais para o


entendimento do modelo, mas estaremos mais preocupados com os
aspectos práticos necessários ao seu uso na criação e consulta de bases
de dados.

Apresentaremos vários exemplo práticos para que o leitor possa ir


construindo e evoluindo seu conhecimento de SQL, na mesma medida em
que incrementamos o nível de complexidade dos exemplos dados.

Estaremos trabalhando com SQL diretamente no MySql, pois é um


SGBD muito popular - principalmente, em conjunto com a linguagem PHP,
para construção de websites -, além de não ser muito pesado, podendo
ser instalado em um computador pessoal atual comum, não requerendo
um servidor, e, também, por ser livre para download.

Este não é um livro de administração de bancos de dados, apesar


do que apresentaremos alguns comandos administrativos do MySql,
necessários para um melhor uso da ferramenta.

Além de apresentar a linguagem SQL em detalhes, iremos mostrar,


de maneira superficial, formas de representação de bases de dados,
utilizando modelos visuais, como as derivadas do modelo entidade
relacionamento (ER) e da engenharia de informação.

Por ser um livro introdutório, não entraremos em detalhes em


relação a procedimentos dentro de bases de dados (STORED
PROCEDURES), pois este tópico envolveria o conhecimento de
programação de computadores.

Ao final do livro, o leitor deverá ser capaz de criar uma nova base de
dados, manipulá-la (inserir, apagar e modificar dados) e fazer consultar à
mesma, utilizando a linguagem SQL.
Instalando MySql

Neste livro, usaremos a versão 5.6 do MySql, que pode ser baixada
gratuitamente no site mysql.com.

Entre em dev.mysql.com/downloads/ e baixe a versão Community


do servidor MySql, que é gratuita.

Será preciso fazer login com uma conta do site da Oracle e, caso
você não possua uma, poderá criá-la ao escolher fazer download.

Durante a instalação, sugerimos que você crie uma senha para a


sua conta administrativa (root).

Para evitar ter o MySql sempre rodando no computador, selecione a


opção de início manual do servidor.

Você pode facilmente iniciar e parar o servidor, quando desejar,


usando o MySql Workbench ou o ícone MySql Notifier, que deverá ser
instalado na barra de tarefas do Windows.
Primeiros passos com MySql

Você pode usar o MySql através de um console de linha de


comando ou pelo programa MySQL Workbench.

O MySql Workbench será usado em praticamente todo este livro por


fornecer, em nosso entender, mais recursos e maior facilidade de uso para
o usuário iniciante.

A primeira coisa que devemos fazer é criar um usuário, para


utilizarmos o SGBD.

Antes, porém, caso o servidor não estiver rodando no computador,


será preciso iniciá-lo, o que você pode fazer utilizando sua conta de
administrador do MySql (root), indo em Startup/Shutdown em Instance,
dentro do Navigator do WorkBench.

Não é aconselhável utilizar o usuário root, que tem privilégios de


administrador, para tarefas que não sejam administrativas, pelas mesmas
razões que não se deve utilizar o usuário administrador ao se logar no
sistema operacional.

Em especial, um usuário root pode danificar, sem querer, de forma


irreversível, uma base de dados.
Além disso, na prática, você nem sempre terá acesso de
administrador (root) a uma base de dados e, utilizando um usuário real, ao
aprender SQL, você ficará mais familiarizado com os privilégios
necessários para a manipulação da base, que são mais limitados para um
usuário comum em relação ao root.

Abra o WorkBench e se logue como root.

Entre em Users and Privileges, que se encontra em Management


no Navigator.

Escolha um Login Name e um Password, depois clique em Apply.

Você também pode criar um usuário através da ferramenta de


console.

Primeiro, entre no console como root.


Ao entrar no MySql utilizando o programa mysql.exe, você deve
fornecer o usuário (-u ) e, caso este usuário tenha senha, deve usar a
opção para fornecimento de uma senha (-p) a qual não deve ser colocada
na linha de comando (para evitar que seja descoberta), mas será
solicitada, em seguida, pelo programa.

Depois, use o comando CREATE USER, conforme abaixo.

Precisamos definir os privilégios para o usuário criado.

No Workbench, clique na aba Administrative Roles para fornecer


as permissões (ou privilégios) mínimas necessárias para o usuário.

Um usuário de um SGBD deveria ter apenas os privilégios


necessários para o uso que o mesmo faz das bases de dados.

No nosso caso, o usuário terá privilégios globais (global privileges)


CREATE, CREATE TABLESPACE, DROP e FILE, que permitem criar e
eliminar bases de dados, além do uso de arquivo para inserir dados na
base ou salvar os dados em disco.
O mesmo pode ser obtido com o comando GRANT <privilégios>
on *.* to <usuario>@<host>:

No comando acima, *.* indica que o privilégio é global e "%"


significa em qualquer host.

Cada vez que alteramos os privilégios de um usuário, tal usuário


deverá se reconectar ao servidor, para garantir que os novos privilégios
entrem em funcionamento, ou podemos, como root, utilizar o comando
FLUSH PRIVILEGES.

Se quisermos remover um privilégio, no WorkBench, basta


desmarcarmos a opção correspondente e, no console, podemos usar o
comando REVOKE:
Devemos adicionar privilégios para cada base de dados usada pelo
usuário, para garantir que ele só tera os privilégios mínimos necessários
para o uso daquela base.

Em nosso caso, vamos adicionar todos os privilégios para as bases


que criarmos.

Clique na aba Schema Previleges, clique em Add Entry... e


escolha Selected Schema.

Clique em uma base de dados e depois em OK.

É possível definir os privilégios para esta base um a um ou, como


desejamos, clicar em Select All.

Por fim, clique em Apply para que os privilégios sejam salvos.


Para adicionar/remover privilégios via console, usamos os
comandos GRANT e REVOKE, apenas substituímos *.* por <nome da
base de dados>.*.

É possível agora nos conectarmos ao servidor com o usuário criado,


para começarmos a usar o MySql.
Lista de contatos

Imagine que você possui uma lista de contatos, com os telefones


dos seus amigos, familiares, etc.

Além do telefone, a lista possui outras informações, tais como data


de aniversário, e-mail.

Você decide que o melhor é ter a lista em algum tipo de sistema


informatizado, assim você poderá consultá-la mais facilmente, além de
protegê-la contra o uso de outros, facilitar o acesso remoto da mesma, de
onde quer que você se encontre, sem precisar carregar consigo a lista.

Uma lista de contatos nada mais é que uma tabela, que guarda um
conjunto de informações relacionadas de alguma forma entre si.

Cada coluna dessa tabela corresponde a um tipo diferente de


informação que a tabela possui; chamamos cada coluna de campo da
tabela.

Essa tabela, junto com outras tabelas ou outras informações


relacionadas aos contatos, constituem uma base de dados.

Em nosso exemplo, temos uma base de dados "contatos".

Em nossa tabela, nome, telefone, e-mail, são colunas, ou seja, são


campos da tabela.

No campo nome podemos ter uma sequência qualquer de


caracteres (letras), em outras palavras, o nome de um contato.

No campo telefone, em geral, apenas números seriam aceitáveis.

Cada campo possui, por tanto, um conjunto de valores que são


permitidos, possui um domínio.

Cada campo, assim, terá um tipo de acordo com seu domínio.

O campo nome seria do tipo caracter, enquanto um campo idade


seria do tipo numérico.
Cada uma das linhas de nossa lista possui um conjunto de dados
(nome, telefone,...) de uma pessoa, de um contato.

Chamos uma linha de uma tabela pelo nome registro.

Nossa tabela, ao ser criada, estará vazia, sem nenhum registro.

À medida que colocarmos as informações de nossos contatos na


tabela (populamos a tabela), criaremos novos registros.

O número de registros de nossa tabela "lista de contatos" indica o


número de contatos que temos.

Por enquanto, tempos apenas uma tabela em nossa base de dados,


mas, na prática, uma base de dados possui várias tabelas, muitas das
quais se relacioam entre si.

Vamos guardar esta tabela num computador, utilizando para isso um


sistema gerenciador de bases de dados (SGBD).

Um SGBD é o programa (ou conjunto de programas, por isso


chamado sistema) que usamos para criar, manter e atualizar nossos
dados num sistema informatizado, ou, por simplicidade, num computador.

É necessário termos uma linguagem para nos comunicarmos com o


SGBD, de modo a dizer que queremos que seja criada uma tabela com
tais e tais campos, que desejamos incluir tais e tais registros nessa tabela,
etc.

Esta linguagem é, em geral, a SQL (Structured Query Language),


que, apesar do nome, serve não apenas para fazer consultas (queries).

Muitas vezes, em vez de SGBD chamamos este programa de


banco de dados.

Entretanto, também é comum chamar uma base de dados de banco


de dados.

Em nosso exemplo, temos uma base de dados "contatos" que, por


enquanto, possui uma única tabela: lista de contatos.

Um número de telefone (98392184), um e-mail (joao@aqui.com), o


nome de um contato (Daniel da Silveira), o grau de relacionamento
(parente, amigo, colega de trabalho) são todos exemplos de dados.

Muitas vezes, tenta-se criar uma distinção entre dado e


informação.

'28/02' é um dado.

Quando digo que '28/02' é o aniversário do meu amigo 'João', tenho


uma informação.

Informação, nesse entendimento, é dado com significado.

O mais comum, na prática, é o uso do termo dado.


Criando a base de dados contatos

Neste livro, usaremos o SGBD MySql para nos ajudar a transportar


nossos dados para o mundo digital.

Antes que possamos utilizar nossa base de dados, devemos defini-


la com mais precisão, saber qual a estrutura que terá nossa tabela, para
podermos criar e popular a tabela dentro do banco de dados.

É possível, mais tarde, modificarmos a estrutura da nossa base,


adicionando, eliminando ou modificando tabelas e outros elementos que
fazem parte de uma base de dados.

Abra o Workbench (ou o console MySql ) e crie uma nova base de


dados, chamada contatos.

Selecione a base de dados recém criada com o comando USE


CONTATOS.

Se você não estiver logado como root, lembre-se que para


conseguir criar uma nova base de dados o seu usuário precisa ter
privilégio global CREATE (explicado no capítulo introdutório do MySql).

Se você mudar as permissões do usuário enquanto conectado, será


preciso fechar e logar-se novamente no MySql para que os privilégios
tenham efeito.
Caso você queira, pode eliminar a base de dados com o comando
DROP DATABASE CONTATOS.

Para eliminar uma base de dados, será preciso ter privilégio global
DROP.

Se você estiver utilizando o console do MySql, lembre-se de colocar


";" no final dos comandos.

Caso você não saiba qual a base de dados em uso, pode usar o
comando SELECT database():

Observe que database() é uma função do MySql, que retorna o


nome da base de dados em uso.

Também é possível saber qual o usuário que estamos usando com


o comando SELECT user().
Definindo a tabela lista de contatos

Por hora, nossa base de dados constitui-se de apenas uma tabela -


lista de contatos.

Digamos que lista de contatos possui o nome do contato, seu


telefone, data de nascimento, e-mail e uma indicação do grau de
relacionamento (parente, amigo ou profissional).

Nome é um campo do tipo caracter, pois nomes são compostos por


letras, que chamamos caracteres, mas qual o tamanho desse campo?

Numa lista física cada nome fica limitado pelo espaço que temos no
papel, mas numa base de dados, teoricamente, não há limites para este
campo.

Na prática, devemos escolher um tamanho mais realístico, ou seja,


devemos imaginar qual o nome com maior número de letras que iremos
guardar.

Pode acontecer de escolhermos um valor muito pequeno, o que nos


faria ter de abreviar algus nomes, ou podemos escolher um valor
demasiado grande, o que seria um desperdício de espaço.

Enquanto estamos criando nossa base de dados, podemos mudar


de opinião sobre o tamanho justo para este ou outro campo de uma
tabela, por isso todo SGBD permite que façamos alterações na estrutura
de uma tabela.

Sempre é possível fazer alterações na estrutura de uma tabela, mas


deveríamos evitar fazer modificações depois que a mesma estiver em uso,
pois isto poderia nos trazer problemas práticos.

Digamos, por hora, que nosso campo nome tem um tamanho de 50


caracteres, ou seja, qualquer nome que tenha até 50 letras (os espaços
em branco também contam como caracter) pode ser inserido nesse
campo, sem maiores problemas.
Veja que para um SGBD um caracter pode ser algo além de uma
letra.

Um número, um espaço em branco, um ponto de exclamação, um


sinal de igual também são caracteres.

No momento, nossa definição de nome não impede que tenhamos


um nome como "João !!! 34 @ da Silva", o que, em geral, não corresponde
ao nome de uma pessoa.

Mais tarde, veremos como tratar este tipo de situação; por hora,
imaginemos que ao se preencher o nome (ou qualquer outro campo) se
utilizará apenas dados aceitáveis.

Passemos a telefone.

Telefones possuem apenas números, entretanto, em geral,


definimos um telefone como do tipo caracter em uma base de dados.

Existem muitas razões para essa escolha, que ficarão claras mais
adiante, por hora vamos aceitar o fato que telefone é do tipo caracter e
que tem um tamanho 11, ou seja podemos colocar 11 caracteres nesse
campo.

Com esse tamanho, podemos incluir qualquer telelefone e também


o código DDD do mesmo.

O leitor mais atento pode pensar que não haverá espaço para o
código internacional, o que impede adicionar um contato de outro país, o
que poderia ser desejável.

Poderemos alterar, mais tarde, a estrutura da tabela, caso


queiramos adicionar este código ao campo, aumentando o tamanho do
campo, ou criando um outro campo, código de país, o qual apenas
preencheríamos caso se trate de um telefone de outro país.

Nem sempre precisamos preencher (colocar um dado) em um


campo.

Podemos ter um amigo cujo e-mail não conhecemos, mas


queremos adicionar em nossa lista de contatos.
Deveríamos, neste caso, deixar o e-mail desse registro sem ser
preenchido.

Para indicar que um campo não tem, no momento, um valor, um


dado, usamos um valor especial chamano nulo ou null.

Entretanto, às vezes não desejamos que um campo tenha valor


nulo.

Em nosso exemplo, um registro cujo nome seja nulo não é


aceitável.

Podemos dizer ao SGBD que um campo deve ter um valor


(diferente de null), ao definirmos a estrutura da tabela.

Data de nascimento é um campo útil para saber se o aniversário de


algum dos contatos está próximo.

Todos SGBDs possui um tipo especial chamado date, que se utiliza


para guardar datas.

Outro campo de nossa tablea é e-mail.

Assim como nome, o tamanho desse campo não é preciso, depende


de uma escolha que faremos.

Vamos escolher 60 para o tamanho de e-mail, mas perceba que


este valor é completamente empírico.

O último campo de nossa tabela é relacionamento.

Este campo indica qual o grau de relação que temos com o contato.

Ele pode ser um parente, amigo ou um contato profissional.

Esses são os únicos valores permitidos (em nosso exemplo) para


esse campo, formam o domínio do mesmo.

Como explicamos em relação a nome, nada impede, por enquanto,


que se coloque outros valores nesse campo, pois ainda não fizemos nada
para impor (garantir) que estes sejam os únicos valores possíveis.
Vamos, por hora, definir relacionamento como do tipo caracter e,
baseado nos valores permitidos para esse campo, seu tamanho será 1, ou
seja, usaremos um caracter para definir o relacionamento do contato ( 'a'
para amigo, 'p' para parente e 'c' para contato profissional ).

Agora nossa tabela de dados está definida, conceitualmente.

Sabemos exatamente quais os campos que queremos, seus tipos e


também definimos uma restrição em relação a eles (campo nome não
pode ser nulo).
Criando a tabela lista de contatos

Vamos criar nossa tabela no MySql.

O comando que cria uma tabela, na sua forma mais simples, tem a
seguinte sintaxe:

CREATE TABLE <nome da tabela> ( <nome do campo> <tipo do


campo> , ... )

Em geral, colocamos comandos do SQL todo em maiúsuculas,


nomes de tabela começando em maiúsculas e campos, etc, em
minúsculas, mas o MySql não faz distinção, em geral, entre maiúsculas e
minúsculas (case insensitive).

Para facilitar nosso trabalho, uma vez que vamos fazer muitas
referências a essa tabela, iremos chamá-la simplesmente lista.

Após o parênteses, segue uma sequência de definições de campos,


separadas por vírgula.

Não existe uma hierarquia ou uma ordem entre os campos, mas, na


prática, colocamos os campos mais relevantes (ou importantes) primeiro.

O primeiro campo definido foi nome, o qual é do tipo caracter.


Veja que o tipo usado no MySql para nome foi VARCHAR(<tamanho
máximo>), que significa que este campo pode ter até 50 (tamanhho
máximo) caracteres.

O "var" em VARCHAR quer dizer que o SGBD irá usar o mínimo


possível de espaço para armazenar um dado dentro desse campo.

Se colocarmos, por exemplo, um nome como "João Fernandes",


que ocupa apenas 14 caracteres (lembre-se que o espaço também conta
como um caracter), o MySql só precisará de 14 caracteres de espaço e
não 50, para este nome.

Existe outro tipo que pode ser utilizado para dados do tipo caracter,
chamado CHAR(<tamanho>).

O tipo CHAR pode ter como <tamanho> até 255 caracteres,


enquanto VARCHAR pode ter para <tamanho máximo> até 65535
caracteres.

Um campo CHAR é armazenado com tamanho fixo, ou seja,


sempre se utiliza <tamanho> espaços para armazenar um dado, ainda
que, numa situação específica (para um registro específico) não sejam
necessários tantos espaços.

Ao definirmos nome, também definimos uma restrição, qual seja,


que este campo não pode ficar vazio, ou seja, quando for inserido um
registro nessa tabela não se pode deixar em branco (valor null) o mesmo.

Isso foi feito colocando NOT NULL ao final da definição do campo.

Em seguida, definimos telefone utilizamos o tipo VARCHAR(11).

Em geral, um telefone celular irá ocupar as 11 posições, enquanto


um telefone fixo ocupará apenas 10 posições.

Ao definirmos o campo seguinte, mudamos seu nome de data de


nascimento para nascimento.

Fizemos isso para facilitar nosso trabalho, pois um nome curto é


mais fácil de ser usado em consultas, etc.

Se quiséssemos chamar este campo data de nascimento,


poderíamos fazê-lo, bastando, para tanto, colocar o nome entre aspas
simples invertidas (`data de nascimento`).

Nomes de campos, tabelas, etc, são chamados identificadores.

Um identificador pode ser contituído de letras, números ou os


caractereres dólar ($) e underscore ( _ ).

Para evitar o uso das aspas, poderíamos ter um campo chamado


data_de_nascimento.

O inconveniente desse nome é o fato óbvio de ser muito grande, o


que pode ser entediante, quando estivermos fazendo referência ao
mesmo.

Vamos guardar a data de nascimento do contato neste campo, a


qual utilizaremos para saber se o aniversário do mesmo está próximo.

Este campo é do tipo DATE, o qual é capaz de guardar uma data


qualquer.

Existem outros tipos de campos para guardar uma data: DATETIME


e TIMESTAMP.

Ambos são capazes de guardar, além de uma data ( dia-mes-ano ),


uma hora do dia (hh:mm:ss), inclusive micro-segundos.

A principal diferença entre estes dois campos é que TIMESTAMP irá


converter a hora para a hora correspondente ao seu fuso horário,
enquanto DATETIME não faz tal conversão.

Também mudamos e-mail para email, pois "-" não é um caracter


válido para um identificador.

Por fim, relacionamento foi definido como sendo do tipo CHAR(1),


uma vez que sempre ocupará um e apenas um caracter.

Para ter certeza que a tabela foi criada, você pode usar o comando
SHOW TABLES, que irá mostrar todas as tabelas existentes na base de
dados em uso.
Usando DESCRIBE
Se você quiser ver a estrutura de uma tabela, pode usar o
comando que descreve sua estrutura: DESCRIBE <nome da tabela>.

Este comando retorna uma tabela, com os campos mostrados na


figura acima.

Cada registro dessa tabela contém as informações relativas à


definição de um campo na tabela sendo descrita: nome, tipo, é nulo, é
chave, default, extra.

O campo null indica se são permitidos valores nulos para aquele


campo.

Em nosso exemplo, apenas nome não pode possuir valores nulos.

O campo key indica se o campo é uma chave, o que será visto mais
tarde.

O campo default é usado para indicar o valor padrão para o campo,


caso tenha sido definido.

Finalmente, o campo extra apresenta outras informações sobre o


campo, que serão apresentadas mais tarde.
Modificando a tabela

Poderíamos desejar que, caso não seja especificado um valor para


um campo, um determinado valor padrão (default) seja utilizado para
preencher um campo.

Por exemplo, podemos dizer que, caso não seja especificado,


queremos que o campo relacionamento tenha o valor 'o' ( outros ).

Seria possível criar novamente a tabela com a alteração proposta,


através dos comandos seguintes:

Veja que após o tipo de relacionamento colocamos o modificador


DEFAULT seguido do valor padrão que queremos para o campo.

Como nosso campo é do tipo caracter, colocamos o valor entre


aspas ( 'o' ).

Observe que eliminamos a tabela (DROP TABLE) e a criamos


novamente.

Se a tabela já tivesse dados dentro da mesma, esta opção iria


apagar todos os dados existentes.

Em vez de recriar a tabela, devemos modificar a tabela existente,


com o comando ALTER TABLE.
Utilize o comando DESCRIBE para verificar a mudança na tabela.

Se mais tarde decidimos que não queremos mais um valor padrão


para relacionamento, podemos usar o comando ALTER COLUMN
relacionamento DROP DEFAULT:

Observe que COLUMN pode ser omitido.

Também poderíamos eliminar um campo que não desejássemos


mais com a opção DROP COLUMN <nome do campo>, dentro do
comando ALTER TABLE.

Outra opção útil é mudar o tipo de um campo existente.

Digamos que, mais tarde, decidimos que o tamanho do campo


nome não é apropriado e queremos mudá-lo para VARCHAR(80).

Podemos alcançar este objetivo com o comando MODIFY COLUMN


nome VARCHAR(80):

Se, em vez de 80, mudássemos o valor atual para, digamos, 30,


nomes com mais de 30 caracteres que já existissem na tabela seriam
cortados (truncados), ficando apenas os 30 primeiros.

Poderíamos também adicionar uma nova coluna, para guardar o


sexo do contato:

É possível fazer várias modificações num mesmo ALTER TABLE,


basta separar cada comando por vírgula.

Praticamente tudo o que fazemos ao criar uma tabela podemos


fazer alterando-a.
Na medida que aumentarmos nosso conhecimento do SQL,
falaremos de outras possibilidades para o comando.

É possível consultar o manual de referência para este e demais


comandos SQL no site do MySql.
Representando a tabela lista

Definimos nossa tabela lista de contatos e criamos a mesma dentro


de uma base de dados em um SGBD.

Digamos, agora, que queremos explicar a outra pessoa como é a


estrutura da nossa tabela, que tipo de dados se pode armazenar na
mesma.

Isto pode ser feito de várias maneiras.

Poderíamos ter a descrição detalhada da tabela, como fizemos


anteriormente.

Podemos apresentar uma descrição mais conscisa, também


chamada esquema lógico, ou poderíamos representar a tabela através
de uma linguagem gráfica.

Um esquema lógico possível para nossa tabela seria:

Lista(nome,telefone,nascimento,email, relacionamento, sexo)

Este esquema é muito básico e não diz muito sobre os campos da


tabela.

Poderíamos melhorá-lo um pouco:

Lista(nome:string(50),telefone:string(11),nascimento:date,email
:string(60),relacionamento:string(1),sexo:string(1))

Neste caso, adicionamos os tipos dos campos.


Em vez de usar VARCHAR para o tipo usamos STRING, que é uma
representação mais genérica de uma cadeia de caracteres.

Ainda estão faltando informações nesta representação, como o fato


de nome não poder ser nulo e que relacionamento possui um valor padrão
de preenchimento.

Nem todos os detalhes necessários para a criação da tabela são


importantes para algumas situações e, se for o caso, este esquema lógico
será uma forma de representação suficiente.

A vantagem dessa representação é a sua conscisão.

Outra forma de representar nossa tabela seria através de um


diagrama entidade-relacionamento, o qual serve como uma
representação gráfica do modelo entidade-relacionamento.

Neste modelo, uma tabela é uma entidade, a qual é representada


por um retângulo.

Os campos são chamados atributos e são representados por


círculos conectados ao retângulo.

Nossa tabela estaria assim representada:

Também esta representação não traduz completamente a estrutura


da tabela que criamos pois, mais uma vez, faltam informações, como
aconteceu com o esquema lógico, textual, apresentado anteriormente.

É possível representar mais que apenas tabelas com esse modelo.


Outro modelo de representação importante é o oriundo da
Engenharia da Informação, também conhecido como Erwin ou "pé de
galinha", que representa as tabelas e os atributos de uma forma mais
compacta.

Os atributos acima da linha dividindo o retângulo seriam aqueles


considerados mais importantes, que diferenciam um reigstro do outro - um
contato de outro, nesse caso ( não existem, imagina-se, duas pessoas
com o mesmo nome e o mesmo telefone).

À medida que nossa base de dados se tornar mais complexa,


apresentaremos outros elementos deste e de outros modelos.

Por fim, se quisermos criar novamente a tabela lista, exatamente


como ela foi criada em nossa base de dados, podemos recuperar a
informação completa, de forma textual, necessária para esta tarefa
utilizando o próprio MySql.

O comando SHOW CREATE TABLE <tabela> faz exatamente isso,


retornando uma tabela na qual um dos campos é o nome da tabela e o
outro o comando CREATE TABLE necessário para a sua criação.
Se você clicar com o botão da direita do mouse sobre o campo,
pode copiar seu conteúdo e colá-lo em um arquivo texto qualquer,
facilitando a leitura:

CREATE TABLE `lista` (


`nome` varchar(80) NOT NULL,
`telefone` varchar(11) DEFAULT NULL,
`nascimento` date DEFAULT NULL,
`email` varchar(60) DEFAULT NULL,
`relacionamento` char(1) DEFAULT 'o',
`sexo` char(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Este comando possuim mais detalhes do que utilizamos ao criar a


tabela.

Em primeiro lugar, todos os campos que não foram definidor como


NOT NULL tiveram o valor DEFAULT definido como null, com exceção de
relacionamento, pois seu valor default é 'o'.

Além disso, a tabela foi definida como do tipo InnoDB.

Existem dois tipos de tabelas no MySql, InnoDB ou MyISAM, sendo


o primeiro o tipo padrão (a partir da versão 5.6 ) e, teoricamente, o tipo
mais completo de tabela, pois permite, entre outras coisas, o uso de
transações, o que será explicado mais adiante.

Como este é o tipo padrão para novas bases criadas na versão 5.6
(e posteriores), não nos preocupamos em especificar este parâmetro ao
criar a tabela.

Outro comando que não usamos foi DEFAULT CHARSET.


O charset define o conjunto de caracteres disponíveis para campos
do tipo caracter e, para nossa tabela, foi definido como utf8.

Existem vários charsets disponíveis no MySql: binary, armscii8,


ascii, big5, cp1250, cp1251, cp1256, cp1257, cp850, cp852, cp866, cp932,
dec8, eucjpms, euckr, gb2312, gbk, geostd8, greek, hebrew, hp8, keybcs2,
koi8r, koi8u, latin1, latin2, latin5, latin7, macce, macroman, sjis, swe7,
tis620, ucs2, ujis, utf8, utf8mb4, utf16, utf16le, utf32.
Populando uma tabela

Nossa tabela lista está vazia, ou seja, não possui nenhum registro,
uma vez que não inserimos nenhuma informação na mesma.

Você pode contar o número de registros de uma tabela com uma


função count().

O comando SELECT (que será explicado mais adiante), pode ser


usado em conjunto com count() para verificarmos que, de fato, o número
de registros de nossa tabela é zero:

Na verdade, precisamos colocar o nome de um campo como um


parâmetro da função count.

Nese caso, colocamos *, que significa 'todos os campos', pois não


estamos interessados em um campo específico.
Inserindo registros manualmente

Vamos inserir alguns registros dentro de nossa tabela, o que


também é conhecido como popular a tabela.

O comando SQL para inserir um novo registro em uma tabela é


INSERT e existem três formas de usá-lo:

INSERT INTO <tabela> VALUES ( <lista de valores> )

Neste caso, vamos inserir um registro na tabela <tabela> e


informaremos todos os valores de todos os campos desse registro:

Observe que o formato usado para data é AAAA-MM-DD.

Veja que o MySql nos informa que 1 registro (row) foi inserido.
Aqui, a ordem dos campos deve ser a mesma que utilizamos no
momento em que definimos nossa tabela.

Se esquecermos esta ordem, podemos utilizar o comando


DESCRIBE, pois o mesmo mostra os campos como registros, na ordem
em que os mesmos foram criados.

Se quisermos inserir um registro, utilizando esta sintaxe, sem


colocar valor em algum campo, podemos inserir o valor NULL (exceto se o
campo tiver sido criado como NOT NULL).

Podemos também utilizar o comando INSERT com a seguinte


sintaxe:

INSERT INTO <tabela> ( <nome do campo1>,... ) VALUES


(<valor1>,..).

Neste caso, especificamos quais os campos para os quais


colocaremos valores.

Os outros campos (não especificados) terão valor nulo.

A ordem dos nomes dos campos deve ser igual à ordem dos valores
em VALUES, mas não precisa respeitar a ordem dada na criação da
tabela.
Você não pode, em nenhum caso, inserir um novo registro omitindo
o valor de um campo que foi marcado como NOT NULL, ou receberá uma
mensagem de erro.

Se tentarmos inserir um registro sem um valor para nome, teremos


a seguinte mensagem:

Isto porque nome foi definido como NOT NULL e nós não
estabelecemos um valor padrão (default) para o mesmo.

A terceira maneira de se utilizar o comando INSERT é a seguinte:

INSERT INTO <tabela> SET <campo>=<valor>,...


Este formato é menos compacto que os anteriores.

Até agora, inserimos 4 linhas ou registros em nossa lista de


contatos.

Podemos visualizar estes registros com o comando SELECT *


FROM lista.

Explicaremos mais sobre SELECT no próximo capítulo.

Se quisermos, podemos inserir mais de um registro com o mesmo


comando INSERT, basta colocarmos, separados por vírgulas e entre
parênteses, cada registro a ser inserido após VALUES.

No exemplo acima, decidimos inserir dois novos registros,


fornecendo apenas os dados dos campos nome e telefone.
Podemos inserir até 1000 registros de uma vez, com um único
comando INSERT.
Inserindo/salvando dados de/para um arquivo

O comando INSERT é muito útil quando se está adicionando


interativamente dados a uma base de dados.

Se, entretanto, após criarmos uma tabela ,quisermos populá-la com


uma quantidade grande de dados, podemos fazer isso de forma mais
eficiente se utilizarmos o comando LOAD DATA, que é utilizado na forma
seguinte:

LOAD DATA LOCAL INFILE <arquivo contendo dados> INTO


TABLE <tabela>

Devemos especificar o caminho completo do arquivo.

Usando a opção LOCAL, indicamos que o arquivo se encontra no


cliente, ou seja, no computador executando o comando; se esta opção
não estiver presente, o arquivo encontra-se no servidor, ou seja, no
computador em que o SGBD está.

Você pode experimentar criar um arquivo texto com alguns registros


de contatos e então aplicar um comando LOAD DATA.

Este arquivo precisa conter em cada linha um registro, em geral os


campos separados por virgula.

É possível especificar que os campos são separados por outro


caracter, usando a opção FIELDS TERMINATED BY <caracteres>.

Também podemos especificar como cada registro é separado um do


outro usando LINES TERMINATED BY <caracteres>.

Em vez de criar um arquivo do zero, vamos salvar os dados de


nossa tabela em um arquivo, alterá-los um pouco e então carregar estes
novos registros para a tabela.
Para salvar os registros de uma tabela para um arquivo usamos o
comando SELECT * INTO OUTFILE <arquivo> FROM <tabela>.

Poderíamos ter selecionado alguns campos em vez de todos (*),


caso desejado.

Salvamos os dados em um arquivo de texto localizado em c:\temp.

Observe que precisamos usar barras duplas (\\) para o nome do


arquivo, pois o caracter '\' tem um significado especial para o MySql, pois
é usado para gerar caracteres que não possuem uma representação
visual, como "nova linha" ou "tabulação".

Definimos o separador de campos (fields) como a virgula.

Para separar cada linha (registro), usamos uma sequência de dois


caracteres, representados por "\r\n".

Estes caracteres representam, no Windows, nova linha.

Usamos o nome completo da tabela, ou seja, <nome da base de


dados>.<nome da tabela>, pois assim não precisamos estar usando a
base de dados para executar o comando.

Sempre é possível usar o nome completo de uma tabela, caso


queiramos.

Você deve ter acesso ao local onde quer salvar o arquivo ou


receberá uma mensagem de erro ao tentar executar o comando:
O arquivo não deve existir, caso contrário o resultado também será
uma mensagem de erro.

Para executar este comando também é preciso ter privilégio global


FILE.

Agora que temos um arquivo com dados de nossa tabela, podemos


abri-lo e modificá-lo.

Ao abrir o arquivo, você notará que campos com valores nulos são
representados por \N.

Faça algumas modificações e salve o arquivo.

Podemos então usar o comando LOAD DATA para carregar estes


novos registros para a tabela.

Devemos utilizar os mesmo delimitadores ( campo e linha ) que os


utilizados ao criar o arquivo.

O privilégio FILE também é necessário, além de acesso ao arquivo.

Podemos verificar que nossa tabela cresceu, selecionando todos os


registros com o comando SELECT * from lista.
Pesquisando uma tabela

Provavelmente o comando mais utilizado em SQL, SELECT nos


permite fazer consultas a nossa base de dados.

O comando retorna zero, um ou mais registros de uma base de


dados, dependendo de como é usado.

Em outras palavras, este comando retorna parte da tabela


pesquisada.

Este é um comando bastante complexo, vamos apresentá-lo ao


poucos.

A forma mais simples de utilizar SELECT é:

SELECT <campos> FROM <tabela>

Onde <campos> é a lista de campos que desejamos recuperar (*


para todos) e <tabela> o nome da tabela alvo da nossa consulta.

Já utilizamos, anteriormente, SELECT * FROM lista.

Podemos selecionar apenas alguns campos de lista:


É possível limitar o número máximo de registros que desejamos que
sejam retornados pelo comando utilizando LIMIT, o que pode ser útil
quando a tabela é muito grande.

Esta sintaxe é específica do MySql, mas existem variações em


outros SGBDs, como a opção TOP no SQL Server, para chegar ao mesmo
resultado.

É possível especificar que não queremos ver resultados repetidos


com a opção DISTINCT.
Digamos, por exemplo, que desejamos ver todos os nomes em lista,
mas, se existirem nomes iguais, que eles sejam retornados apenas uma
vez, usaríamos:

Nossa tabela, atualmente, possui poucos registros e nenhum nome


repetido, mas se você usar o comando acima para o campo sexo verá que
serão apresentados apenas 3 resultados - 'm', 'f'' e null.

Se executarmos um comando como SELECT DISTINCT nome,


telefone FROM lista, apenas serão retornados os nomes e telefones de
registros diferentes, ou seja, se dois registros tiverem o mesmo nome e
telefone, apenas o primeiro será retornado.
Organizando os dados

Podemos organizar a ordem em que os registros retornados por


SELECT são apresentados.

Utilizamos, para isso, a opção ORDER BY <campos>.

Se quisermos, por exemplo, ver todos os nomes, telefones e sexos


de lista ordenados por sexo, podemos usar o comando na forma seguinte:

Os registros foram organizados por ordem alfabética do conteúdo


do campo sexo.

Observe que o valor null é considerado como vindo antes de


qualquer outro caracter.

Se o campo escolhido fosse nascimento, a ordem seria cronológica.

Podemos usar mais de um campo na cláusula ORDER BY.


A tabela retornada por SELECT está ordenada por sexo,
primeiramente, e depois, em caso de empate, por nome.

Compare as duas últimas tabelas retornadas para melhor


compreender a diferença.

Também é possível retornar a tabela em ordem contrária


(descendente) com a opção DESC:
Funções

É possível utilizar funções junto com um comando SELECT.

Já utilizamos, anteriormente, a função count(), mas existem


centenas de outras funções disponíveis, sendo fora do escopo desse livro
mostrar o funcionamento de todas.

Vamos apresentar as mais utilizadas, durante o desenvolvimento


dos nossos exemplos.

Existem funções para manipular datas, números, sequências de


caracteres (strings), etc.

Digamos, por exemplo, que queiramos listar os DDDs dos números


em nossa tabela lista.

Esta informação pode ser obtida dos dois primeiros caracteres do


campo telefone e, portanto, podemos utilizar a função LEFT, que retornar
um número n desejado de caracteres mais à esquerda de um argumento
dado.
A função LEFT precisa de dois parâmetros (ou argumentos): uma
sequência de caracteres e o número de caracteres à partir da esquerda a
retornar.

Nesse caso, usamos telefone, que é um campo do tipo caracter


(VARCHAR), como primeiro argumento, mas poderia ser qualquer
expressão do tipo caracter.

Poderíamos, por exemplo, ter, como primeiro argumento para LEFT


uma outra função, que retorne uma string:
Nesse comando, usamos nome como argumento de uma função
UPPER, a qual retorna o seu argumento em maiúsculas.

Esta expressão do tipo caracter (ou string) é então usada como


primeiro argumento para a função LEFT.

Poderíamos ter expressões mais complexas, a única restrição é que


a mesma deve ser do tipo string.

Observe na tela mais acima que quando o telefone era nulo a


função LEFT simplesmente retornou nulo.

O segundo argumento, em ambos os casos acima, foi um número


(um valor literal), mas poderia ter sido qualquer expressão numérica.

Vejamos algumas outras funções de string muito usadas.

A função CONCAT junta (concatena) todos os seus argumentos em


uma única expressão do tipo caracter.

Observe que, no exemplo, foram dados três argumentos, dois


campos e um literal.

Também é possível que um dos argumentos seja de outro tipo (data,


numérico), sendo que os mesmos serão convertidos para string e
concatenados como se strings fossem.

Veja que quando algum argumento dado é null o resultado da


concatenação é também null.

Ordenamos o resultado da consulta usando a expressão criada pela


função.

Para tanto, criamos um nome (ou alias) para o resultado da


expressão (nometel), usando a opção AS.

A função INSTR retorna qual a posição de uma string dentro de


outra.

No exemplo acima, retornamos a posição da primeira ocorrência do


caracter 'A' dentro de nome.

Observe que a função não diferencia maiúsculas de minúsculas.

Se quisermos que a função diferencie entre maiúsculas e


minúsculas (case sensitive), devemos usar a cláusula BINARY.
Compare os dois comandos anteriores e veja a diferença do número
retornado pela função INSTR.

A cláusula BINARY pode ser usada não apenas em funções, mas


sempre que se queira forçar o comportamento de diferenciação entre
maiúsculas e minúsculas.

A função LENGTH retorna o número de caracteres do seu


argumento.

LOWER converte uma string para minúsculas.

MID retorna uma string contida dentro de outra string, seu primeiro
argumento é a string de origem, o segundo é a posição do primeiro
caracter dentro dessa string e o terceiro o número de caracteres a ser
retornado.
A função RIGHT retorna os últimos n caracteres de uma string.

A função REPEAT retorna a string contida no primeiro argumento


repetida o número de vezes indicado no segundo argumento.

A função REPLACE busca pela string contina no segundo


argumento dentro da string dada como primeiro argumento e, cada vez
que a encontra, a substitui pela string no seu terceiro argumento.

UPPER transforma a string dada como argumento em maiúsculas.

Todas as funções apresentadas retornarão null caso tenham um


argumento null.

Se um argumento numérico for dado na forma de string ("3" em vez


de 3, por exemplo), a função tentará converter o mesmo para um número
e, se não conseguir, trata-lo-á como se fosse nulo.

Utilizamos campos mas, como dito anteriormente, qualquer


expressão pode ser utilizada como argumento, basta atentarmos para o
tipo de expressão solicitado pela função ( string, numérico, etc).
Variáveis

Podemos usar variáveis para guardar valores, enquanto estamos


trabalhando com uma base de dados.

Por exemplo, se quisermos guardar a hora em que começamos a


trabalhar em nossa base de dados, usaríamos:

A função NOW() retornada a data e hora atuais.

Nossa variável se chama inicio, usamos @inicio para que o MySql


saiba que fazemos referência a uma variável e não a um campo.

SET é usado para dizer que queremos guardar um valor na variável.

Mais tarde, podemos recuperar este valor com um SELECT.

Podemos guardar o resultado de uma consulta em uma variável.


Repare que usamos := para atribuir um valor à variável.

Se usarmos = , MySql fará uma comparação entre o valor da


variável e o campo nome, o que não é o que desejamos acima.

Mais tarde, podemos usar esta variável dentro da consulta, como se


fosse um valor.
Cláusula WHERE

Até o momento, vimos como selecionamos todos os registros de


uma tabela, mas isto não é muito útil.

Como fazer se quisermos saber, por exemplo, qual o telefone do


contato cujo nome é "AMARO ASSUNÇÃO"?

Ou então, quantos dos nossos amigos são do sexo masculino?

Para responder a tais questões, precisamos poder limitar os


resultados que queremos que sejam retornados pelo comando SELECT e,
para tanto, utilizamos a cláusula WHERE, que nos permite aplicar um filtro
aos resultados retornados.

Seu uso mais geral obedece a seguinte sintaxe:

WHERE <expressão> <operador> <expressão2>

Em <expressão> temos um campo ou uma expressão resultado da


combinação de campos e funções.

Para <operador> podemos ter qualquer um da tabela abaixo:

= igualdade
<> desigualdade
> maior que
>= maior ou igual
< menor que
<= menor ou igual
BETWEEN compara faixa de valores
IN compara com uma lista de valores
LIKE compara com um padrão
AND, && operação E lógica
OR, || operação OU lógica
& E binário
| OU binário
IS NULL compara com nulo
IS NOT NULL compara com 'não nulo'

A <expressão2> será uma expressão do mesmo tipo que


<expressão> - um literal, outro campo ou qualquer expressão que resulte
do uso de funções.

Podemos também usar funções dentro de <expressão> e/ou


<expressão2>.

Para responder à primeira pergunta feita no início desta seção,


poderíamos ter um comando SELECT como abaixo:

A cláusula WHERE faz com que o comando SELECT filtre os


registros, baseado na condição estabelecida e apresente apenas
resultados que satisfaçam à condição.
É possível perceber que a pesquisa feita pelo nome 'AMARO
ASSUNÇÃO' encontrou apenas um registro compatível e o comando
SELECT retornou uma tabela contendo o que foi solicitado para o registro,
neste caso, o telefone contido naquele registro.

Observe que o filtro não distingue minúsculas de maiúsculas, a


menos que usemos a opção BINARY.

Neste caso, nenhum registro seria retornado, pois inserimos o nome


"Amaro Assunção" e não "AMARO ASSUNÇÃO".

Para sabermos quantos de nossos amigos são do sexo masculino


precisamos usar a função count(), não na cláusula WHERE, mas na parte
inicial do comando SELECT.

Podemos fazer consultas mais complexas, utilizando mais de um


delimitador na cláusula WHERE, separando-os por AND ou OR.
O exemplo acima seleciona todos os registros cujo sexo não seja "f"
e cujo telefone começe com um "9".

Se quisermos ver todos os registros cujo telefone começam com "9"


ou "3" e cujo sexo esteja preenchido (não nulo), podemos executar o
seguinte comando:

A cláusula WHERE possui dois filtros, combinados com um AND.

O primeiro filtro usa uma expressão, resultado da função LEFT e


verifica se a mesma é encontrada (IN) dentro de um conjunto de valores
("9" e "3").

O segundo filtro testa se o campo sexo contém um valor não nulo


(IS NOT NULL).

Ambas as condições devem ser verdadeiras para que o registro


correspondente faça parte do resultado.
Podemos consultar quais registros possuem o campo
relacionamento não nulo ou possuem nascimento entre 01/01/2001 e
31/12/2014:

Temos duas condições separadas por um operador OR.

Para deixar mais clara a cláusula WHERE, utilizamos parênteses


separando cada lado do operador OR.

Observe que o AND na segunda condição faz parte do operador


BETWEEN, que retorna verdadeiro se uma expressão (nesse caso,
nascimento) está entre (between) uma faixa de valores, que vai desde o
segundo argumento até (AND) o terceiro, que são datas literais, nesse
exemplo.

Quando queremos usar uma data literal (como 01/01/2001) numa


expressão em um comando SELECT utilizamos o formato AAAAMMDD e
colocamos a data entre aspas, como se fosse uma string.
Buscando padrões

Uma das mais poderas opções de que dispomos para consultas em


uma tabela é LIKE e seu opsto, NOT LIKE.

Utilizamos LIKE para buscar por campos (ou expressões) que


atendam um determinado padrão.

Se quisermos pesquisar, por exemplo, todos os contatos cujo


telefone sejam do DDD 11, poderíamos usar:

Poderíamos escolher todos os contatos com telefones começando


com um nove com a mesma facilidade:
Caso queiramos listas todos os contados que possuam " de " em
seu nome:

Como você já deve ter percebido, usamos "_" (underscore) para


substituir um caracter qualquer na consulta e "%" (percentual) para
substituir qualquer número de caracteres.

Poderíamos facilmente usar NOT LIKE se quisermos retornar


registros que não atendem um padrão.
Além de LIKE podemos fazer uma consulta que busca padrões
através de uma expressão regular, utilizando REGEXP, mas este tópico
vai além do conhecimento básico de SQL a que nos propomos neste livro,
pois o uso de expressões regulares engloba, em si mesmo, todo um
universo de possibilidades.

A título ilustrativo, poderíamos pesquisar por nomes começando


com A, B ou C, através de expressões regulares, com o comando abaixo.

Como dissemos, há muitas opções de uso para o comando


SELECT, mas já sabemos o bastante para fazer consultas poderosas à
nossa tabela.
Ao longo do livro, iremos apresentar mais formas de se utilizar este
comando.
Controle de vendas

Vamos criar uma nova base de dados.

Esta base de dados terá como objetivo guardar informações de


vendas de uma loja.

Guardaremos as informações dos clientes, dos produtos e das


compras efetudas pelos clientes.

Teremos, portanto, a princípio, três tabelas: clientes, produtos,


compras.

Inicialmente, podemos definir estas bases como segue:

Cliente(nome, telefone, email)


Produto(descrição, preço,quantidadeEmEstoque)
Compra(cliente, produto, data,valorPago).

Cliente terá os campos nome, telefone e email, que serão


cadastrados na primeira compra e atualizados sempre que o mesmo fizer
uma nova compra.

Produto terá a descrição do mesmo, seu preço de venda e a


quantidadeEmEstoque daquele item.

O preço será atualizado sempre que necessário e a


quantidadeEmEstoque sempre que se vender um item daquele produto, o
que poderá ser usado para saber quando fazer novos pedidos para aquele
produto.

A tabela Compra guarda informações das compras feitas por um


cliente de um produto em uma determinada data, junto com o valorPago
por aquele produto (o preço do produto pode mudar ao longo do tempo e
não ser o mesmo em duas compras do mesmo).

Vamos definir graficamente nossas tabelas, usando o modelo Erwin:


Os campos acima da linha, dentro de cada triângulo, são aqueles
que identificam unicamente um registro na tabela correspondente.

Na figura acima, estamos dizendo que não existem dois clientes


com o mesmo nome e telefone, não existem dois produtos com a mesma
descrição e não existem duas compras feitas pelo mesmo cliente do
mesmo produto na mesma data.

Ou seja, estes campos, ou atributos, das tabelas (ou entidades) são,


em conjunto, capazes de diferenciar unicamente um registro de uma
tabela, diferenciá-lo de qualquer outro registro da mesma tabela - motivo
pelo qual estão acima da linha divisória do triângulo.

Um campo ou conjunto de campos deste tipo são chamados


chaves.

Podemos também representar textualmente, através de um


esquema lógico, esta propriedade, sublinhando os campos chave:

Cliente(nome, telefone, email)


Produto(descrição, preço,quantidadeEmEstoque)
Compra(cliente, produto, data,valorPago).

Este conceito de chave vem do Modelo Relacional (MR), que é o


modelo adotado pela maioria dos SGBDs modernos, incluindo MySql.

Qualquer campo capaz de identificar unicamente um registro é uma


chave.
Numa tabela com dados de veículos, poderíamos dizer que
número_de_chassi e placa são chaves, pois identificam unicamente um
veículo.

Na prática, escolheríamos um campo (ou conjunto de campos, se


necessário) e identificaríamos este campo como chave primária.

Para uma pessoa, uma possível chave primária seria o número do


CPF.

Para um empresa, seu número de CNPJ.

Uma tabela, nesse modelo, também pode ser chamada de relação.

Observe que na tablea Compra os campos cliente e produto


identificam um registro nas tabelas respectivas, Cliente e Produto.

Ou seja, uma compra é feita por um cliente e este escolhe um


produto.

Assim, mais tarde, podemos perguntar ao SGBD, através de uma


consulta (SELECT), por exemplo, quais os produtos comprados por um
determinado cliente ou quais clientes compraram um determinado produto.

Em outras palavras, existe um relacionamento entre Compra e


Produto e também entre Compra e Cliente.

Ou também podemos dizer que Compra relaciona um Cliente a um


Produto.

Compra faz referência a um determinado cliente e a um


determinado produto em cada um de seus registros.

Podemos representar graficamente este relacionamento através de


um diagram ER:
Este diagrama pode ser lido como "cliente compra produto" ou
"produto é comprado por cliente".

Este é um modelo conceitual, no qual Compra representa um


relacionamento entre duas entidades (tabelas).

Na prática, em uma base de dados, Compra é uma tabela, como


Cliente e Produto, que guarda informações sobre este relacionamento
entre as duas outras tabelas.

No modelo da Engenharia da Informação (EI), a representação fica


mais próxima da realidade da base de dados, com três tabelas definidas e
o relacionamento entre estas:

Tenha em mente que a notação varia um pouco de acordo com a


ferramenta de desenho (software) utilizada.

A chave ao lado de certos campos indica que estes campos fazem


parte da chave primária da tabela.

No modelo acima, as linhas representam os relacionamentos entre


as entidades (tabelas).

Um cliente pode fazer várias compras, mas uma determinada


compra é feita por um e só um cliente, por isso, do lado do Cliente, existe
uma barra cortando a linha de relacionamento e, do lado da tabela
Compra, existem várias linhas chegando na tabela ( estas várias linhas
'lembram' um 'pé de galinha', o que dá o nome popular pelo qual este
modelo é conhecido ).
De forma similar, um produto pode ser vendido várias vezes mas,
como em relação a Cliente, uma compra é de um produto e apenas um
produto.

Você pode estar pensando que um cliente pode comprar vários


produtos de uma vez.

Entretanto, registramos cada produto comprado individualmente,


como um registro de Compra.

Não poderiámos ter uma tabela compra da seguinte forma:

Compra(cliente, produtos, data, valor)

Em uma tabela assim, produtos conteria uma lista dos produtos


comprado por um cliente, todos num mesmo registro de uma tabela.

Este tipo de campo é chamado multivalorado, pois guardaria vários


valores (vários produtos), mas seu uso é completamente vetado em uma
base de dados relacional.

Em seu lugar, guardamos vários registros, cada um contendo a


informação de um único produto (monovalorado).
Criando as tabelas

Nossa representação conceitual, gráfica ou textual, ainda não está


completamente de acordo com o uso prático em um SGBD relacional real,
como o MySql.

A tabela Compra possui dois campos cliente e produto, mas o que


conterá estes campos?

Em outras palavras, como vai Compra referenciar a tabela Cliente


e a tabela Produto, indicando assim qual o cliente e o produto envolvidos
em uma determinada compra.

Estes campos precisam indicar (referenciar) um regisrto específico


nas tabelas de clientes e de produtos.

Sabemos que a chave destas tabelas são capazes de identificar


unicamente um registo nas mesmas.

Entretanto, um registro em Cliente é identificado por nome e


telefone, o que torna não prático usá-los como referência (precisaríamos
guardar estes dois campos ) e Produto tem como chave descrição, que é
um texto, que pode ser longo, o que significa guardar a mesma informação
em duas tabelas, além do que o texto descrevendo um produto pode
mudar.

Também pode mudar o nome de um cliente (caso se case, por


exemplo).

Podemos adicionar o campo CPF e utilizá-lo como chave primária


para Cliente.

Em geral, toda pessoa possui um e apenas um número de CPF.

Para Produto, poderíamos utilizar o códido que identifica o produto


(aquele acima do código de barras), pois trata-se de algo que não mudará
e é mais compacto que a descrição do mesmo.

Assim, Compra fará referência ao cpf de um cliente no campo


cliente e ao código de um produto no seu campo produto e, dessa forma,
é possível saber quem foi o cliente que comprou determinado produto em
uma determinada compra.

Vamos criar, primeiro, uma nova base de dados, a qual


chamaremos Loja.

Caso não esteja conectado como root, não esqueça de criar os


privilégios para o usuário em uso para a base de dados criada.

Depois, criamos as tabelas Cliente e Produto com as chaves


primárias e demais campos já descritos.

Ao criarmos Cliente, adicionamos o campo cpf.

Como um número de cpf sempre tem 11 caracteres, usamos o tipo


CHAR em lugar de VARCHAR.

Observe também que definimos este campo como sendo nossa


chave primária na última linha (6) de definição da tabela, através da
cláusula PRIMARY KEY (<campos>).
Se não tivéssemos o campo cpf, poderíamos criar uma chave
primária com nome e telefone, apesar de ser menos conveninente para
fazermos relacionamentos com outras tabelas: PRIMARY KEY ( nome,
telefone ).

Neste caso, seria interessante criar um nome para a chave primária,


pois a mesma é composta de mais de um campo, de modo que ao fazer
referência à chave primária de Cliente usaríamos este nome, em vez dos
nomes dos campos que a compõem.

Utilizamos, para tanto, a opção CONSTRAINT <nome>:

CONSTRAINT idCliente PRIMARY KEY(nome,telefone).

Uma chave primária, assim como a opção NOT NULL e outras


ainda não vistas, não deixa de ser uma restrição (constraint) em um
campo (ou conjunto de campos), pois determina que não podemos ter dois
registros em nossa tabela com os mesmos valores na chave.

Podemos usar DESCRIBE para inspecionar a estrutura da tabela


recém criada:

Note que o registro referente ao campo cpf indica em key que o


mesmo é uma chave primária (PRI).
Também podemos notar que este campo não pode ser nulo, mesmo
que não tenhamos utilizado explicitamente a restrição NOT NULL.

Isto porque, uma vez que cpf identifica unicamente cada registro na
tabela Cliente, não pode ter valores nulos, caso contrário poderíamos ter
dois clientes com o mesmo valor (nulo) na chave primária e, assim, não
poderíamos identificá-los unicamente por meio desta chave.

Na prática, ao se desenhar uma base de dados como cliente, muitas


vezes utilizamos um campo "artificial" para identificar os registros (em
geral, chamado id), pois pode acontecer que uma pessoa não possua
CPF.

Este tipo de campo não agregaria nenhuma informação a Cliente,


seria apenas útil para identificar os registros desta tabela em
relacionamentos com outras tabelas, pois poderíamos garantir que sempre
haverá um valor único para cada registro.

Quando utilizamos este tipo de campo, em geral os fazemos do tipo


INTEGER, que significa que guardarão um número inteiro (sem ponto
decimal) e o definimos com o modificador AUTO_INCREMENT, o que
significa que o SGBD, ao inserir um novo registro na tabela,
automaticamente irá atribuir um valor para o campo, que será diferente
dos já atribuídos.

Vejamos abaixo como seria uma tabela Cliente, se definida com um


campo deste tipo.

Quando inserimos valores em um tabela Cliente como definida


acima, não devemos dar nenhum valor para id (ou podemos dar o valor
nulo, que o SGBD entenderá como sendo não dar valor ), uma vez que
cabe ao SGBD colocar um novo número, diferente dos demais, nesse
campo.

Verificamos que o SGBD automaticamente colocou valores em id.

Se nós fornecermos um valor para o campo id, o que é geralmente


desaconselhável, teremos que ter cuidado para não colocar um valor já
usado pelo SGBD, ou teremos um erro.
Para o resto deste exemplo, contudo, iremos continuar com a
definição anterior de Cliente, que utiliza o cpf como chave primária e sem
o uso de um campo id.

Passemos à tabela Produto:

Definimos todos os campos como NOT NULL, uma vez que a chave
primária não pode ser nula e também não faz sentido ter um produto sem
descrição, quantidade ou preço.

Veja que preço foi definido como sendo do tipo DECIMAL.

Os números entre parênteses indicam o número de dígitos do


campo e quantas casas depois da vírgula.

Podemos, portanto, ter valores entre 00,00 até 99,99, o que nos
parece adequado no momento.

Em vez de decimal, poderíamos utilizar o tipo NUMERIC, que é


sinônimo.
O campo quantidade em estoque (quantidade) foi definido como um
número inteiro, do tipo SMALLINT, que é um subtipo do tipo INTEGER (ou
INT) e ocupa menos espaço.

Os tipos de campos inteiros possuem faixas de valores diferentes


em seus domínios e também ocupam espaços diferentes na base de
dados (bytes), conforme indicado na tabela abaixo.

Tipo Tamanho (bytes) Menor valor Maior valor Maior valor


(com sinal) (com sinal) (sem sinal)
TINYINT 1 -128 127 255
SMALLINT 2 -32768 32767 65535
MEDIUMINT 3 -8388608 8388607 16777215
INT 4 -2147483648 2147483647 4294967295
BIGINT 8 -9,22E+018 9223372036854775807 18446744073709551615

Para todos os tipos, o menor valor com sinal é zero.

Os tipos inteiros podem ser definidos com ou sem sinal, sendo por
padrão com sinal e, se quisermos sem sinal (só números maiores ou
iguais a zero) devemos colocar o modificador UNSIGNED após o tipo
escolhido.

Poderíamos ter definido quantidade como sendo SMALLINT


UNSIGNED, uma vez que não precisamos de números negativos, em
nosso exemplo.
Chaves estrangeiras

Precisamos agora definir a tabela Compra.

Esta tabela guarda informações das compras feitas pelo cliente,


relacionando um cliente a um produto.

Seus campos irão apontar para os registros correspondentes nas


tabelas de mesmo nome (não é necessário que o nome do campo seja
igual ao da tabela, apenas é útil para lembrarmos para que servem).

Utilizamos vários campos (cliente, produto, dataCompra) como


chave primária dessa tabela.

Não pode haver a compra de um mesmo produto pelo mesmo


cliente numa mesma data.

Se um cliente comprar vários produtos de uma vez, cada produto


comprado estará em um registro independente.

Poderia acontecer, no entanto, de um cliente comprar duas


quantidades de um mesmo produto.

Para permitir isto, deveríamos mudar o tipo de dataCompra para


DATETIME, assim podemos armazernar não só o dia mas o momento em
que o registro é inserido na tabela, ou poderíamos acrescentar um campo
quantidadeComprada.
Poderíamos também resolver este dilema criando um outro campo,
idCompra ( de tipo inteiro, provavelmente ).

Cada nova compra receberia uma identificação (um número) e,


assim, se, em uma mesma compra, um cliente adquire vários produtos,
todos os registros correspondentes compartilhariam desse mesmo
identificador.

Como se vê, existem várias abordagens possíveis para a solução


de um mesmo problema.

Uma vez que nossa chave primária é constituída por vários campos,
poderíamos considerar dar um nome à mesma, para facilitar a referência
futura à chave.

O mesmo é verdade para as chaves estrangeiras, ainda que sejam,


como em nosso caso, constituídas de apenas um campo.

Note que umaCompra não é um campo da tabela Compra, mas


apenas um alias, uma forma de se referir à chave primária.

O mesmo é verdadeiro para oCliente e oProduto, são apenas alias


das chaves estrangeiras.

Se você comparar duas tabelas criadas com ou sem o uso de


CONSTRAINT para designar um alias para as chaves, verá que suas
estruturas são as mesmas.
Analisando os campos cliente e produto, observamos que estes
campos são do mesmo tipo das chaves primárias das tabelas
correspondentes: cpf e código.

Isto se deve ao fato que estes campos apontam ou fazem referência


a um registro nestas tabelas.

Utilizamos o modificador FOREIGN KEY, quando criamos a tabela,


para tornar explícito este fato.

Estamos criando o que chamamos de chave estrangeira, indicando


que este campo faz referência a outro campo em outra tabela.

Quando nossas chaves são compostas por vários campos, torna-se


ainda mais importante utilizamos o modificador CONSTRAINT para dar
um nome à chave e assim utilizar este alias ao utilizar FOREIGN KEY.

Se você não criar um nome (alias) para uma chave estrangeira


usando CONSTRAINT, o MySql irá criar um nome padrão
automaticamente, o qual podemos ver se usarmos o comando SHOW
CREATE TABLE .

O alias criado serve, entre outras coisas, para eliminarmos, no


futuro, a restrição (constraint) de chave estrangeira.

Poderíamos remover a chave oCliente alterando a estrutura da


tabela, utilizando ALTER TABLE.
Devemos indicar, ao criar uma chave estrangeira, qual o campo de
nossa tabela estará apontando para um campo em outra tabela.

Seguido do nome do campo (ou campos) que forma(m) nossa


chave, utilizamos REFERENCES e indicamos, em seguida, a tabela e o
campo (ou campos) que está(ão) sendo referenciado(s).

A sintaxe utilizada em outros SGBDs, como Oracle e MS SQL, para


esta cláusula é um pouco difernte da usada pelo MySql.

Em nosso caso, estamos referenciando, para cliente, o campo cpf


em Cliente, que é também a chave primária daquela tabela.

Veremos mais adiante que o SGBD cria automaticamente um índice


para chaves primárias, que é um mecanismo usado para acelerar a
consulta em bases de dados.

Se quisermos fazer referência, em nossa chave estrangeira, a um


campo que não é uma chave primária, teremos que criar um índice para
este campo, ou o MySql não nos deixará criar a chave estrangeira em
nossa tabela.

Falaremos de índices com detalhes mais adiante.

Lembre-se que a sua chave estrangeira (o campo ou campos em


sua tabela) deve ser do mesmo tipo que o campo (ou campos) que você
está fazendo referência.
Populando as tabelas

Em nível conceitual, o que fizemos foi criar um relacionamento entre


as tabelas.

Vamos criar alguns registros para as tabelas, para entendermos


melhor este relacionamento.

Inicialmente, podemos criar alguns clientes.

Se tentarmos inserir um outro cliente com o mesmpo cpf de outro já


existente, MySql nos dará uma mensagem de erro, pois não podemos ter
o mesmo valor na chave primária de dois registros.

Devemos criar também alguns registros de produtos.


De formar semelhante a Cliente, se tentarmos adicionar dois
regisrtos com o mesmo código, o SGBD irá retornar um erro.

Observe que preço, que foi definido como DECIMAL(4,2), pode ter
no máximo duas casas decimais, mas pode ser inserido como um número
sem casas decimais (devemos sempre respeitar o limite de algarismos,
neste caso 4 no total e 2 após a vírgula) e que usamos ponto como
separador da pate inteira e decimal (em vez da vírgula).
Criando instâncias de relacionamentos

Agora vamos criar algumas compras.

Separadamente, podemos dizer que um cliente (uma pessoa) e um


produto (uma mercadoria, um objeto ) são totalmente diferentes.

Não existe uma relação necessária entre uma pessoa e um sapato,


por exemplo.

Mas certamente podemos pensar em várias relações entre uma


pessoa e um sapato: poderia ser meu sapato, em uma base de dados do
meu guarda-roupas, pode ser uma compra feita por uma pessoa, como no
nosso exemplo, pode ser uma arma de um crime usada por um criminoso,
numa base de dados criminal.

Relacionamentos expressam uma ligação não necessariamente


óbvia mas importante (ao menos para quem os criou) entre entidades
(tabelas) que podem ser tão diferentes como pessoas e calçados.

Em nosso exemplo, estamos lidando com as compras efetuadas por


uma pessoa (cliente) de um produto.

Os registros nesta tabela expressam o relacionamento entre um


cliente específico com um produto específico.

Para conseguirmos distinguir uma compra de outra, adicionamos o


campo dataCompra, mas poderiamos pensar em outras alternativas, como
vimos anteriormente.

Se nosso relacionamento se baseasse apenas em cliente e produto


não conseguiríamos identificar duas compras do mesmo produto pelo
mesmo cliente.

Ou seja, faltariam elementos para a chave primária da tabela, por


isso incluiu-se a data da compra.

O preço do produto numa compra específica não é essencial para


identificar a compra, mas é útil se quisermos consultar, por exemplo,
quanto um cliente gastou em um determinado mês.
Cada compra que inserimos em nossa tabela, cada registro, é uma
instância desse relacionamento, uma referência concreta a um
relacionamento.

Digamos que no dia 21/02/2015 o cliente João Pereira comprou um


sapato infantil e uma saia adulta.

Nossa tabela Compra agora possui dois registros, que relacionam


um cliente (pelo seu CPF) com dois produtos distintos (pelos seus
códigos), como podemos ver ao consultar o conteúdo da tabela.

Se você tentar adicionar o mesmo cliente, produto e data na tabela,


MySql retornará um erro, pois haveriam dois registros com a mesma
chave primária.

A novidade, em relação às outras tabelas criadas - Cliente e


Produto -, é que você também não pode inserir um registro na base
Compra caso o cliente (CPF) ou o produto (CÓDIGO) inseridos não
existam nas tabelas Cliente e Produto.

Ou seja, quando você insere um registro em Compra o SGBD


verifica se as chaves estrangeiras nessa tabela (cliente e produto) estão
apontando para um registro que existe nas tabelas para as quais estas
fazem referência.
Mais a respeito de chaves estrangeiras

E o que acontece se mudarmos o cpf do cliente na base Cliente,


depois de termos adicionado um registro na base Compra usando esse
CPF no campo cliente? Ou se retirarmos um produto da base Produto
porque não vendemos mais aquele produto?

Não podemos ter um registro de compra apontando para um


produto que não existe mais em nossa base ou para um cliente utilizando
um CPF que já não é mais o utilizado por aquele cliente, pois isto violaria
o que chamamos integridade referencial da nossa base de dados.

Existem formas de se lidar com estas situações, definindo o


comportamento que o SGBD deve adotar quando algo assim acontecer,
de modo a manter a base de dados num estado coerente (íntegro
referencialmente).

Quando criamos uma chave estrangeira, podemos dizer que, caso o


valor do campo (ou campos) para o(s) qual(is) aquela chave faz referência
mude(m), queremos que o SGBD automaticamente atualize a chave em
nossa tabela com o novo valor (CASCADE) ou que mude o valor da chave
estrangeira para nulo (SET NULL) ou também podemos impedir esta
mudança (RESTRICT, que é a ação padrão).

Digamos que tentemos mudar o valor do CPF do cliente João


Pereira na base Cliente.

De acordo com a definição atual da tabela Compra, MySql irá


impedir esta mudança.
O comando UPDATE, do qual falaremos no próximo capítulo, é
usado para modificar valores de campos em registros já existentes numa
tabela e, como visto acima, devido à existência de uma chave estrangeira
em outra tabela, que faz referência ao campo cpf na tabela sendo
modificada, não será bem sucedido.

Isto porque não definimos uma cláusula ON UPDATE ao criarmos a


chave estrangeira, que serve para controlar o comportamento do SGBD
nestes casos.

Se quisermos que uma alteração no cpf do cliente seja propagada


para a tabela Compra, devemos especificar este comportamento ao criar a
chave.

Como nossa tabela já foi criada e já está populada, em vez de criar


a tabela novamente, iremos modificá-la, usando ALTER TABLE.

Primeiro apagamos a chave estrangeira (DROP) e depois a


adicionamos novamente, com a cláusula ON UPDATE CASCADE.
Agora, se tentarmos modificar o cpf na base Cliente, veremos esta
alteração refletida (propagada) na base Compra.

Se você estiver usando o MySql Workbench, para ser capaz de usar


o comando UPDATE acima, deverá retirar a opção "safe updates" no
menu Edit -> Preferences -> SQL Editor e deverá se reconectar ao
servidor MySql.

Para podermos alterar o código de um produto na tabela Produto,


temos que fazer uma modificação semelhante na chave estrangeira
produto em Compra.

O que, como vemos a seguir, nos permite fazer atualizações em


códigos de produtos.
Outra opção seria mudar o valor da chave estrangeira para nulo
(null), em caso de mudanças nos campos referenciados.

Em nossa tabela isto não seria possível porque tanto cliente quanto
produto fazem parte da chave primária de Compra e não são permitidos
valores nulos dentro de uma chave primária.

Como dissemos antes, o comportamento padrão, de negar


mudanças, pode ser conseguido com a cláusula RESTRICT.

Podemos adicionar uma cláusula ON UPDATE RESTRICT ou


podemos simplesmente deixar a chave estrangeira sem uma cláusula ON
UPDATE.

Ainda temos a cláusula ON DELETE que pode ser usada para


controlar o comportamento em caso de apagarmos um registro em uma
tabela referenciada por nossa chave estrangeira.

Se utilizarmos ON DELETE CASCADE, caso se elimine um registro


em uma tabela sendo referenciada pela chave estrangeira, qualquer
registro contendo a chave estrangeira também será eliminado.
Observe que podemos ter ambas as cláusulas (UPDATE e
DELETE) em nossa chave estrangeira.

Vamos criar um cliente e inserir uma nova compra deste cliente.

Podemos ver que um registro novo foi adicionado em cada tabela.

Se eliminarmos este cliente da tabela Cliente, a compra será


eliminada automaticamente da tabela Compra.
O comando DELETE, que será visto mais adiante, elimina um ou
mais registros de uma tabela.

Caso nossa chave estrangeira tivesse uma cláusula ON DELETE


RESTRICT, ou nada seja especificado (comportamento padrão), ao tentar
eliminar um registro referenciado pela mesma a ação seria impedida pelo
SGBD.

Caso utilizemos ON DELETE SET NULL o valor da chave


estrangeira seria modificado para nulo, mas, como antes, em nosso
exemplo, uma vez que a mesma faz parte de uma chave primária na
tabela Compra, tal campo não pode ter valor nulo.

Existe ainda uma opção SET DEFAULT, tanto para ON UPDATE


quanto para ON DELETE, a qual deveria fazer o valor da chave
estrangeira assumir seu valor padrão, mas, conforme consta da própria
documentação do MySql, não é permitida a criação de uma chave
estrangeira com esta opção, o que podemos constatar na prática:
Este comportamento é específico do MySql e não necessariamente
de todos os SGBDs existentes.
Visualizando melhor os dados de relacionamentos

Até agora, utilizamos SELECT * FROM compra para visualizar os


registros desta tabela, mas a informação retornada não é muito
interessante.

Quem é que realizou a compra? Qual produto foi comprado?

Desejamos saber o nome do cliente e não seu cpf, assim como a


descrição e não o código do produto.

Podemos juntar várias tabelas em um mesmo comando SELECT e


visualizar dados oriundos de todas estas tabelas.

Note que selecionamos campos de diversas tabelas diferentes


baseados em um critério (WHERE).

Este critério está baseado nos relacionamentos existentes entre as


tabelas.

Quando indicamos cliente.cpf = compra.cliente estamos dizendo


que queremos registros de ambas tabelas mas apenas aqueles que
possuem o mesmo valor para estes campos.

O mesmo é válido para produto.codigo = compra.produto.


Estamos fazendo um tipo de junção entre as várias tabelas e
filtrando os resultados por meio deste critério.

Falaremos detalhadamente sobre junções mais adiante neste livro.

Observe que, para maior claridade, ao nos referirmos a um campo


de uma tabela utilizamos a notação <nome da tabela>.<nome do campo>
(cliente.cpf, por exemplo).

Como os nomes dos campos nas tabelas são diferentes entre si,
esta notação mais completa não seria necessária ( poderíamos, por
exemplo, ter escrito cpf=cliente), mas seu uso é sempre recomendado
para evitar ambiguidade, deixando a leitura do comando SQL mais clara.

Poderíamos também utilizar alias para os nomes das tabelas, como


fizemos a seguir.

Uma vez que definimos alias para o nome de uma tabela, devemos
sempre usar este alias ao se referir à tabela (naquele comando em que o
alias foi definido).

Não podemos ter um comando SELECT como o seguinte, que gera


um erro:
É possível utilizar alias para apenas algumas das tabelas
envolvidas, assim como utilizar o nome completo para campo
(tabela.campo) em apenas alguns campos:

O comando utilizado acima mostra todas as compras realizadas,


mas poderíamos estabelecer um critério dentro da cláusula WHERE para
filtrar os resultados desejados, como fizemos no capítulo em que falamos
sobre SELECT.
Podemos aplicar funções nos resultados de nossa consulta.

Uma função interessante é DATE_FORMAT(<data>,<formato>),


que podemos utilizar para mudar o formato da data apresentado
(americano) para o brasileiro ( dia/mês/ano ).

Aproveitamos e criamos um alias para o campo através da opção


AS "Data da Compra".

O primeiro argumento da função é uma data, o segundo é uma


string com o formato desejado, onde %d será substituído pelo dia, %m
pelo mês e %y pelo ano.
Atualizando e apagando registros

No capítulo anterior, vimos que é possível utilizar UPDATE e


DELETE para modificar e apagar registros, respectivamente.

Vejamos os detalhes desses comandos.

Antes, vamos inserir alguns outros registros na base de dados Loja,


para tentermos maior diversidade de dados com os quais montar nossos
exemplos.

Criamos alguns clientes e produtos novos.

E também algumas compras novas.


Atualizando com UPDATE

Este comando tem a sintaxe geral da seguinte forma:

UPDATE <tabela> SET <campo>=<valor>,... WHERE <condicao>

Podemos mudar o valor de um ou mais campos com a cláusula


SET, separando-os por vírgula.

Caso o campo tenha sido definido com um valor padrão, podemos


usar SET <campo> DEFAULT para colocar o valor padrão no mesmo.

Começaremos por diminuir em 20% o preço de todos os produtos


cujo valor seja superior a R$30,00.

Como se ve, é possível aplicar qualquer expressão como sendo o


novo valor do campo, desde que o resultado seja do mesmo tipo do
campo.

É possível ver a mudança nos preços, comparando dois SELECT,


um antes e outro depois de utilizarmos o comando UPDATE acima.
O MySql, ao executarmos este comando, nos informa quantos
registros foram alterados.

Neste caso, foram 6 registros modificados.

Além disso, temos uma mensagem de alerta (warning), pois em


alguns casos a operação preco*0.80 gerou um número com mais que
duas casas decimais, o que obriga o MySql a fazer uma truncagem do
número, para que o mesmo caiba no formato do campo, que é
DECIMAL(4,2).

É possível ver as mensagens de alerta com o comando SHOW


WARNINGS.
O comando retorna uma tabela contendo o nível, código e a
mensagem de aviso.

O nível pode ser Error, Warning, Note, dependendo da gravidade do


aviso.

Se o tipo da expressão não for o mesmo do campo que estamos


atualizando, o MySql tentará fazer a conversão e, caso não consiga,
rejeitará a mudança.

Por exemplo, o seguinte comando não irá gerar um erro:

Apesar de código ser do tipo caracter, e a função LEFT retornar


também um tipo caracter, este será um algarismo, o qual MySql pode
converter para o tipo numérico e somar ao preço.

Esta operação, obviamente, não faz sentido na prática, apenas


exemplifica o processo de conversão.
Se o caracter retornado pela função não fosse um algarismo, a
conversão falharia e o SGBD retornaria um erro.

Observe também que não utilizamos a cláusula WHERE no


exemplo acima e, portanto, o mesmo afetará todos os registros da tabela
Produto.

Devemos ter muito cuidado para ter certeza que realmente


queremos aplicar uma mudança em todos os registros de uma tabela.

Digno de nota também o fato de que se a soma acima gerasse um


número maior do que aquele capaz de ser armazenado pelo campo preço,
o comando retornaria um erro.

Por exemplo, se tentarmos aumentar o preço do "Brinco de pérolas"


em R$50,00:

O comando retorna um erro, pois o valor da expressão preco + 50 é,


nesse caso, 126.35, sendo que o maior valor que podemos colocar no
campo é 99,99.

Imagine agora que queremos atualizar todas as compras feitas do


produto "Brinco de pérolas", diminuindo seu valor em 10%.

Como fazer isso se não soubermos o código do produto ?


Podemos ter um SELECT dentro da cláusula WHERE, o qual
retornará o código do produto desejado e que usaremos para definir quais
comprar o valor será atualizado.

Veja que, neste caso, o SELECT irá retornar apenas um registro


contendo apenas um campo, código, caso contrário teríamos um erro,
como mostrado abaixo.

Se quiséssemos, poderíamos usar WHERE produto IN (SELECT...)


e, assim, comparar o campo com um conjunto de valores.

Podemos usar um SELECT dentro da cláusula WHERE de


qualquer comando, inclusive de um outro SELECT.
Neste caso, selecionamos as compras com produtos cujos preços
(preço do produto, não valor da compra) são maiores que 20.

Desta vez, o SELECT dentro da cláusula WHERE retorna um


conjunto (set) de valores (códigos), que são comparados com o campo
produto de cada registro de Compra, através da operação IN, sendo
retornados todos os registros cujos produtos forem encontrados dentro
deste conjunto.

É possível usar a cláusula LIMIT para especificar o número máximo


de registros que serão modificados por uma operação UPDATE.

Ainda que existam mais de dois produtos cujos preços sejam


menores que R$50,00, somente dois registros serão afetados pela
operação acima.
A ordem com que você insere os registros na tabela determina a
ordem dos mesmos na tabela.

Podemos mudar esta ordem natural usando uma cláusula ORDER


BY.

Por exemplo, o comando a seguir irá alterar apenas dois registros,


mas antes de escolher os registros os mesmos são ordenamos por
descrição, de modo que agora a ordem em que foram inseridos na tabela
não importa para o comando UPDATE.

Você pode ver a ordem natural com que os registros foram inseridos
numa tabela simplesmente usando um comando SELECT sem especificar
uma ordem (ORDER BY).
Apagando com DELETE

Quando desejamos remover um ou mais registros de uma tabela,


recorrermos ao comando DELETE, que tem a seguinte sintaxe:

DELETE FROM <tabela> WHERE <condições>

Tome muito cuidado para não usar este comando sem a cláusula
WHERE, caso contrário todos os registros da sua tabela serão apagados.

Caso você realmente queira eliminar todos os registros de uma


tabela, o comando TRUNCATE TABLE <tabela> é, em geral, mais rápido.

As condições na cláusula WHERE para DELETE respeitam as


mesmas regras para qualquer outro comando que use tal cláusula, como
os já conhecidos SELECT e UPDATE.

Poderíamos, por exemplo, apagar os registros de todos os clientes


que não realizaram, até o momento, nenhuma compra.

O comando SELECT acima retorna um conjunto com todos os


campos cliente diferentes (DISTINCT) na tabela Compra, que então é
usado negativamente (NOT IN) como critério para seleção.

Podemos verificar que dois registros foram apagados da tabela


cliente, a qual, antes do comando acima, possuía quatro registros.
Podemos utilizar também a função row_count() para saber quantos
registros foram afetados por uma operação INSERT, UPDATE ou
DELETE.
Índices

Quando consultamos uma tabela, em geral fazemos certos tipos de


pesquisa mais que outros.

Por exemplo, é mais comum pesquisarmos a tabela cliente por


nome ou cpf que por e-mail ou por sexo.

Em geral, buscamos os dados de um cliente com um certo nome ou


cpf, ainda que possamos fazer qualquer tipo de consulta que quisermos.

No caso de produtos, poderíamos buscar por preço ou faixa de


preço.

Em compras, poderíamos estar interessados em pesquisas quantas


compras foram feita em um determinado período (data).

Consultar uma base de dados desta maneira pode ser muito


demorado, caso nossa tabela tenha muitos registros.

Uma maneira de otimizar este processo é criarmos índices.

Um índice especifica qual campo (ou conjunto de campos) que


pesquisamos mais frequentemente.

Assim, o SGBD guarda certas informações que permitem, em teoria,


retornar mais rapidamente os resultados de uma pesquisa.

Dissemos anteriormente que o MySql, como qualquer SGBD, cria


um índice automaticamente para a chave primária de uma tabela.

Ou seja, ainda que não tenhamos definido explicitamente, existem


três índices em nossa base de dados Loja.

Há um índice para Cliente, que guarda informações dos cpfs dos


clientes, um para Produto, utilizando produto e um para Compra, utilizando
cliente, produto e dataCompra.

O SGBD não pode permitir que dois registros em uma tabela


possuam os mesmos valores em suas chaves primárias e por isso cria
automaticamente um índice para tais chaveis, pois assim, cada vez que
for inserir um novo registro, pode verificar rapidamente se já existem
valores iguais na chave primária de outro registro.

Entretanto, podemos ter outros campos que acessamos


frequentemente, até mais que a própria chave primária e, por tanto, existe
a possibilidade de criarmos nossos próprios índices.

O comando para criar um novo índice é muito simples e possui a


seguinte sintaxe:

CREATE INDEX <nome do índice> ON <tabela> ( <campos> ).

Podemos facilmente criar um índice para o campo nome da tabela


cliente.

Como temos poucos registros em nossa tabela, não vamos sentir


nenhuma diferença na velocidade de execução de um comando de
seleção (SELECT), mas se tivéssemos milhões de registros, como bases
de dados reais costumam ter, a importância de um índice se tornaria
evidente.

Podemos ver quais os índices existentes em uma tabela com


SHOW INDEX.
Observe que existem dois índices para Cliente, aquele que
acabamos de criar e aquele criado automaticamente pelo MySql.

Se mais tarde quisermos eliminar o índice que criamos, podemos


utilizar DROP INDEX.

Podemos criar um índice utilizando mais de um campo ou apenas


partes de um campo.

Digamos que os 7 primeiros caracteres do código de um produto


são os que definem o produto, sendo os demais referentes ao país de
origem, fabricante, etc.

Poderíamos criar um índice baseado apenas nestes caracteres,


imaginando que faríamos consultas utilizando apenas esta parte de
código.
Esta forma de índice é disponível no MySql mas não
necessariamente em todos os SGBDs.

Podemos verificar que a definição deste índice inclui uma


informação de que o mesmo será criado baseado apenas em parte do
campo (veja sub_part).

Podemos criar um índice ao mesmo tempo que criamos uma tabela,


basta inserirmos uma cláusula INDEX <nome do índice> (<campos>)
dentro do nosso comando CREATE TABLE.

Também é possível adicionar (ADD INDEX) ou remover (DROP


INDEX) um índice mais tarde, com o comando ALTER TABLE:

ALTER TABLE <tabela> ADD INDEX <nome do índice>


(<campos>).
ALTER TABLE <tabela> DROP INDEX <nome do índice>.

Você pode usar KEY no lugar de INDEX, nas cláusulas acima.

Como dissemos, índices são muito importantes, uma vez que


agilizam a consulta à base de dados, otimizando o tempo de resposta.

Você poderia pensar, por que não criar um índice para todas as
colunas da tabela?

A resposta é que isso pode tornar operações de inserção e


modificação na tabela extremamente lentas.

Imagine uma tabela com 50 campos, sendo vários desses campos


textos (VARCHAR).

Manter índices para tantos campos seria um desperdício de


processamento e poderia tornar a tabela inviável

O que devemos fazer é analisar quais as consultas que serão mais


comuns em nossa base de dados e verificar a conveniência de criarmos
um índice.

Quando a base de dados está em funcionamento e o número de


registro cresce muito, naturalmente sentiremos um aumento no tempo de
resposta às consultas.

Poderíamos então criar um índice e verificar se há uma melhora


significativa no desempenho.

O melhor, contudo, é sempre fazermos um bom planejamento


durante o desenvolvimento da base de dados e só recorrer a este tipo de
opção como um recurso extraordinário.

Uma forma de verificar a necessidade de criação de índices para


uma tabela e qual índice exatamente criar é utilizando o comando
EXPLAIN ao fazer uma consulta (SELECT).
Este é um procedimento de otimização que vai além do escopo
deste livro, por isso não o trataremos aqui.
Junções

Num capítulo anterior, nós produzimos uma consulta utilizando


campos originários de várias tabelas.

Existe um mecanismo mais apropriado e mais poderoso para fazer


este tipo de junção entre dados pertencentes a diversas tabelas,
utilizando a opção JOIN dentro do comando SELECT.
O que o comando acima faz é juntar todas as tabelas envolvidas e
então filtrar os registros que atendem à condição na cláusula ON,
retornando apenas resultados que atendam ao critério especificado.

Ou seja, inicialmente, o MySql gera uma grande tabela contendo


todos os campos selecionados de todas as tabelas (no caso acima,
Cliente.nome, Produto.descrição e Compra.dataCompra) e com todos os
registros destas tabelas.

Podemos chegar ao mesmo resultado retirando a cláusula ON do


comando de seleção.

Para melhor entendermos o que esta acontecendo, colocaremos


também os campos que fazem parte da declaração ON (cpf, código,
produto, cliente).

Não vamos reproduzir o resultado inteiro aqui, pois este comando


retorna 240 registros.

Por que 240 registros ?


A tabela Cliente possui 4 registros, Produto 6 e Compra 10.

Multiplicando estes números (4*6*10) chegamos a 240 registros.

Depois que esta grande tabela é criada, aplica-se o filtro ON.

Em outras palavras, é feita a comparação, em cada registro, se o


cpf=cliente e se produto=código.

Vejamos outro exemplo.

Na base de dados contato, tinhamos o campo relacionamento na


tabela lista.

Utiizamos apenas uma letra para definir o relacionamento, mas


assim podemos nos esquecer, no futuro, o que cada letra representa.

Vamos criar uma outra tabela, contendo a descrição destes


relacionamentos.
Podemos incluir algumas descrições de relacionamentos.

Vamos, então, juntar as duas tabelas, baseando a condição de


junção em que o código em relacionamento coincida com relacionamento
em lista.

Nem todos os relacionamento em Relacionamento estão presentes


nos registros de Lista.
Aqueles que não estão presentes na tabela não aparecerão no
resultado.

Este é o tipo mais comum de junção, que chamamos de INNER


JOIN, ou apenas JOIN.

Existem ainda outros três tipos de junções possíveis: LEFT JOIN,


RIGHT JOIN e FULL JOIN.

As junções LEFT e RIGHT podem ser conhecidas também como


LEFT OUTER e RIGHT OUTER.

Uma junção LEFT (OUTER) JOIN retorna todos os registros da


tabela da esquerda (left) e apenas os registros da direita que satisfazem o
critério.

Imagine que em nossa tabela Lista tivéssemos um contato com


relacionamento 'z', o qual não existe em Relacionamento.

Vamos modificar um registro de Lista para conseguirmos tal


resultado:

Agora, se aplicarmos um LEFT JOIN em vez de um INNER JOIN,


que foi usado no exemplo anterior, teríamos um resultado diferente:
Observe que no campo relação, que é um alias para descrição,
temos um valor nulo, pois não existe um registro em Relacionamento que
atenda ao critério relacionamento=código, uma vez que o código 'z' não
existe.

Se tivéssemos usado um INNER JOIN, este registro não apareceria


no resultado.

Claro que esta situação não existiria se, ao criarmos a tabela Lista,
tivéssemos especificado que relacionamento é uma chave estrangeira que
referencia código em Relacionamento, como fizemos quando criamos a
base de dados Loja.

Na prática, você poderá se deparar com junções entre tabelas em


que não foi especificada uma restrição do tipo chave estrangeira.
Nestas situações, o LEFT JOIN permite que se retorne registros
que, poderíamos dizer, estão incompletos.

A junção RIGHT é similar, apenas que retorna todos os registros da


base da direita e apenas aqueles da base da esquerda que atendam ao
critério.

Se modificarmos a última consulta, tornando a junção uma junção


RIGHT, por exemplo, receberíamos um registro com descrição
"profissional", o qual, contudo, não existe na tabela Lista.

O valor NULL é retornado nos campos quando não existe um valor


para retornar.

O último tipo de junção é chamado FULL JOIN e retorna os


registros de ambas as tabelas, exista ou não uma correspondência.

Este tipo é uma união entre uma junção LEFT e uma RIGHT.

Contudo, não existe tal tipo de junção no MySql (existe em outros


SGBDs).

Podemos simular o mesmo resultado, entretanto, usando uma união


entre o resultado de um LEFT e de um RIGHT JOIN.
Para tal, utilizamos o comando UNION, que une o resultado de dois
SELECT.

Esta não é uma forma eficiente de consulta, pois estamos


realizando dois SELECT em lugar de um, sendo mostrada aqui para que o
leitor veja o resultado e compreenda a diferença em relação a outros tipos
de junções.
Clínica Odontológica

Vamos criar uma base de dados com a finalidade de controlar as


consultas realizadas em uma clínica odontológica.

A seguir, o modelo da base de dados é fornecida utilizando notação


gráfica.

Este é, claro, um modelo simplificado, com propósito apenas


exemplificativo, não refletindo uma base real para uso prático.

Faltam várias tabelas e vários campos importantes, como, por


exemplo, o CRO do profissional e informações de Planos de Saúde,
Fornecedores, etc.

Este modelo foi criado no WorkBench (menu File, New Model,


opção Add diagram) e é possível observar alguma diferença em relação
aos modelos mostrados anteriormente.

Em geral, cada ferramenta utilizada para construir um modelo de


uma base de dados tem suas particularidades.

Observamos que o WorkBench mostra o tipo dos campos das


tabelas, se são chaves primárias (pequena chave ao lado do nome do
campo) ou se são chaves estrangeiras (losângo preenchido), além de
mostrar a relação de índices que devem ser criados para a tabela.

A linha tracejada indica que o relacionamento entre as tabelas não é


identificador, o que significa que a chave estrangeira não faz parte da
chave primária da tabela.

Os relacionamentos podem ser do tipo um para um, nos quais um


registro em uma tabela corresponde a um registro a outra tabela, ou
podem ser do tipo um para muitos (n), nos quais um registro em uma
tabela se relaciona com vários (um ou mais) registros em outra tabela.

Na linha do relacionamentos, indicamos "um" por um traço e


"muitos" pelos vários traços ("pé de galinha") junto à tabela.

Cada relacionamento entre duas tabelas pode ser lido de duas


maneiras, dependendo da ordem em que mencionamos as tabelas:

Um cliente faz várias consultas.


Uma consulta é feita por um cliente.

Um dentista é o responsável em várias consultas.


Uma consulta tem como responsável um único dentista.

Um consulta é paga em vários pagamentos.


Um pagamento é referente a uma consulta.

Em uma consulta são feitos vários serviços.


Um determinado serviço é feito em uma consulta.

Já um relacionamento um para um (1:1) é lido de uma só maneira:

Um serviço prestado é de um tipo de serviço.


Vamos criar nossa base de dados com base no modelo proposto
acima.

Iremos criar alguns modificadores que não aparecem no diagrama,


mas que consideramos úteis.

Nem sempre um esquema lógico, visual ou não, irá descrever


completamente a base de dados real no SGBD (que pode ser chamada
esquema físico).

ATENÇÃO: O comando SELECT, como já visto, é usado para consultar uma base de dados,
o que também é chamado fazer uma query ou uma pesquisa na base.

Como temos uma tabela em nosso exemplo cujo nome é Consulta e também vários campos
chamados consulta fazendo referência a esta tabela, neste capítulo e seguintes, ao falarmos
do comando SELECT, utilizaremos o termo pesquisa ou query, de modo a evitar confusão.

Começamos por criar a nova base de dados.

Não se esqueça de criar as permissões necessárias para o usuário.

Criaremos as tabelas em uma ordem que nos fascilita no hora de


criar as referências (chaves estrangeiras).
Definiremos, em todas as tabelas, o campo id como do tipo
AUTO_INCREMENT, pois assim o próprio MySql se encarrega de dar um
número único para o mesmo.

Ao definirmos Dentista, inserimos uma restrição do tipo CHECK,


que representa um teste que deve ser feito antes de se inserir um registro.

No caso acima, o telefone deve ter, no mínimo, 8 algarismos.

O problema é que o MySql, apesar de aceitar cláusulas CHECK na


definição da tabela, não realiza nenhum teste definido em tal cláusula.

Outros SGBDs realizam esta verificação, mas a sintaxe específica


deve ser verificada.

Uma forma de fazer uma verificação como esta, ou outras mais


complexas, antes de se inserir um registro em uma tabela é utilizando-se
TRIGGERS, procedimentos que são disparados (trigered) em certas
condições.

Existem vários tipos de procedimentos disparados (triggers) em um


SGBD.

O uso de tais procedimentos envolve o uso de uma linguagem de


programação, o que vai além do escopo de um livro introdutório em SQL.

A título exemplificativo, para conseguir um efeito semelhante ao


desejado com o CHECK definido acima, poderiamos definir um TRIGGER
a ser executado antes da inserção de um novo registro (BEFORE
INSERT).

O procedimento verifica se o tamanho do campo telefone no novo


(NEW) registro é menor que 8 e, caso afirmativo, envia um erro (SIGNAL)
para o SGBD, impedindo que o registro seja inserido e com a mensagem
de texto definida em message_text.

Podemos verificar que o procedimento funciona, tentando inserir um


registro com um número de telefone com menos de oito algarismos.

É possível verificar quais triggers existem em uma base de dados


com o comando SHOW TRIGGERS.
Continuando com o nosso exemplo, podemos criar a tabela
Consulta.

Esta tabela fará referência a um cliente e a um dentista.

Observe que, dessa vez, utilizamos o tipo DATETIME para guardar


a data da consulta, pois é importante saber também o horário da mesma.

Criamos, para esta tabela, além das restrições de chave


estrangeira, que definem os relacionamentos entre as três tabelas -
Cliente, Dentista e Consulta -, uma restrição UNIQUE, que define que dois
registros de Consulta não podem ter os mesmos valores para cliente,
dentista e dataConsulta.

Uma restrição UNIQUE funciona, neste sentido, como uma restrição


PRIMARY KEY.

Como escolhemos para primary key id, o que facilita referências a


Consulta por outras tabelas, decidimos inserir a restrição UNIQUE para
evitar que sejam criados dois registros para a mesma consulta.

Restrições UNIQUE e CHECK podem ser adicionadas, mais tarde,


através de ALTER TABLE:

ALTER TABLE ADD CONSTRAINT <nome> CHECK <condição>.


ALTER TABLE ADD CONSTRAINT <nome> UNIQUE
(<campos>).

Para remover uma restrição CHECK usamos DROP CHECK (em


outros SGBDs, DROP CONSTRAINT).

Para remover uma restrição UNIQUE, utilizamos DROP INDEX, pois


esta restrição nada mais faz que criar um índice para a tabela.

Note que não guardamos o valor da consulta nesta tabela, pois o


valor pago pela consulta pode ser calculado com base nos serviços
realizados.

Em geral, devemos evitar ter um campo que pode ser calculado,


pois isto pode levar a inconsistências na base de dados.

Na prática, contudo, para evitar termos de calcular o valor da


consulta, somando todos os serviços realizados, poderíamos guardar este
valor como um campo na tabela Consulta, para agilizar futuras pesquisas.

Teríamos que ter o cuidado de manter o valor atualizado, pois se o


valor guardado neste campo for diferente do valor da soma de todos os
serviços realizados nesta consulta, temos uma inconsistência na base de
dados, o que não é aceitável.

Vamos criar alguns registros para as tabelas já criadas.

Primeiro, criamos alguns clientes.

Observe que, no campo id, colocamos null para indicar que não
iremos preencher este campo, que é de incremento automático.
Na linha 4, entretanto, colocamos o valor 10 para este campo,
indicamos que desejamos que este seja o valor utilizado ao inserir o
registro.

O MySql aceita a inserção do registro e, como podemos ver ao


analisar o conteúdo da tabela, passa a utilizar este valor para os futuros
incrementos automáticos, razão pela qual o registro seguinte terá id 11.

Observe também que nas linhas 2 e 3 inserimos registros de um


modo bem pouco usual.

Utilizamos expressões aritméticas para o campo idade.

Fizemos isto apenas para demonstrar que podemos utilizar qualquer


expressão (incluindo funções) e não apenas valores literais dentro de
INSERT.

Na sequência, podemos criar alguns registros de dentistas.

Temos agora três registros na tabela, como esperado.


Uma vez que não fornecemos valores para id, estes receberam os
valores em sequência, começando de 1.

Podemos, caso desejado, definir qual o valor de id para o próximo


registro a ser inserido na tabela, utilizando ALTER TABLE
AUTO_INCREMENT = <novo valor>:

Novos registros seguirão a partir de 101, caso não modifiquemos


novamente o valor de auto_increment.

Não podemos, contudo, utilizar novos valores menores que o último


(maior) valor de id existente na tabela.

Podemos, agora, criar alguns registros de consultas.


Observe o uso de SELECT para descborir o id do cliente na linha 2,
que é usado para o valor do campo cliente em Consulta; também usamos
SELECT na linha 4, para preencher o valor do campo dentista.

É mais fácil sabermos o nome de um cliente ou dentista que o seu


id.

É preciso ter cuidado para o caso de dois clientes (ou dentistas)


possuirem o mesmo nome.

Na prática, usaríamos o CPF ou o CRO para ter certeza de


estarmos recuperando o id correto.

Agora que temos a tabela Consulta, podemos criar a tabela


Pagamento.
Na realidade, um pagamento é feito por um cliente para pagar uma
consulta, mas não precisamos dessa informação (cliente) na tabela
Pagamento, uma vez que a mesma já está na tabela Consulta.

Na tabela Pagamento, o campo formaPagamento irá indicar se o


pagamento foi realizado em dinheiro (d), cartão (c), ou cheque (x).

Poderíamos utilizar uma cláusula CHECK para garantir que


somente estes valores sejam aceitos como formaPagamento, mas
sabemos que o MySql não irá, de fato, fazer nenhuma verificação (o
mesmo não é verdade para outros SGBDs).

Para não precisarmos criar um TRIGGER, que envolve


programação, poderíamos criar uma tabela DescricaoFormaPagamento
com os campos codigo e descrição, a qual preencheríamos com os três
registros correspondentes às formas de pagamento que desejamos.

Criaríamos, na sequência, uma nova chave estrangeira em


Pagamento, para não permitir outros valores no campo formaPagamento.
Dessa forma, conseguimos o mesmo efeito que teria uma restrição
do tipo CHECK, além do que agregamos informação à base de dados,
pois, como resultado de uma pesquisa, saber que o pagamento foi feito
com 'cartão' é mais compreensível que simplesmente 'c'.

Podemos inserir alguns registros de pagamentos em nossa tabela.

Com os valores inseridos até agora, já podemos fazer consultas


bastantes complexas.
Observe que utilizamos o formato Tabela.campo para nos referimos
aos nomes dos campos pois, além de maior clareza, é necessário para o
cado de campos com o mesmo nome (id).

Para facilitar, criamos um alias para a tabela


DescriçãoFormaPagamento, o qual deve ser usado em qualquer
referência aos seus campos, nesta pesquisa.

Ainda falta criar as tabelas TipoServiço e ServiçoRealizado.

Veja que um campo do tipo AUTO_INCREMENT deve sempre fazer


parte de uma chave - chave primária, em nosso caso.
Lembre-se que o tipo de um campo que é chave estrangeira deve
ser compatível com aquele do campo ao qual a chave faz referência.

Se, por exemplo, definirmos tipo sem ser UNSIGNED o SGBD


retorna um erro, pois poderíamos ter valores negativos em tipo enquanto
id em TipoServiço não pode.

Observe que guardamos em ServiçoRealizado o valor do serviço,


apesar de guardamos seu preço em TipoServiço, pois o mesmo pode
mudar ao longo do tempo.

Não devemos guardar o mesmo dado em duas tabelas diferentes,


mas neste caso não se trata exatamente do mesmo dado.

Podemos inserir alguns tipos de serviços e alguns serviços


realizados.

Iniciamos por TipoSeriço, uma vez que faremos referência a estes


registros quando inserirmos registros para ServiçoRealizado.

O resultado pode ser visto selecionando-se os registros da tabela.


Agora, podemos inserir alguns registros em ServiçoRealizado.

Na figura acima, podemos ver que utilizamos variáveis para guardar


o resultado de um comando SELECT.

Na linha 3, a variável restauro ( que referenciamos sempre com um


@ na frente do nome), guardou o valor retornado pelo comando de
seleção.

Este valor foi usado na própria linha, como valor a ser utilizado pelo
campo tipo daquele registro sendo inserido e também foi utilizado na linha
seguinte para inserir outro registro (do mesmo tipo de serviço).

Preste atenção para o fato de que utilizamos := para atribuir um


valor à variável e não o sinal de igualdade (=), pois em tal caso o MySql
faria uma comparação entre o valor da variável e o valor retornado pelo
comando de seleção.

Temos, agora, cinco registros na tabela ServiçoRealizado.


Podemos consultar os Serviços realizados nas Consultas.
Melhorando consultas - agrupando e mais

Ao analisarmos o modelo de nossa base de dados Consultório,


podemos observar que Pagamento não está diretamente relacionado a
um serviço realizado, mas indiretamente sim.

Ou seja, um pagamento é feito porque um serviço foi realizado


durante uma consulta, entretanto não estabelecemos uma relação direta
entre aqueles dois.

Podemos saber quais serviços foram realizados em uma consulta


(que geram um total a ser pago) e quais quantias foram pagas (podemos
ter um parcelamento dos valores), mas não identificamos exatamente qual
pagamento se refere a qual serviço.

Isto porque podemos ter situações em que um pagamento poderia


cobrir um valor maior que um serviço, ou, em outros casos, apenas uma
fração do serviço e não nos interessa fazer esta vinculação direta.

Indiretamente, contudo, podemos, somando os valores dos


serviços, saber quanto falta para ser pago, diminuindo deste total os
pagamentos feitos para aquela consulta.
GROUP BY

Como somamos todos os valores pagos relativos a uma consulta


específica ?

Devemos agrupar os registros por consulta e somar os valores do


campo valorPago.

Usamos a cláusula GROUP BY <campo> para agrupar registros e


podemos usar a função SUM para conseguir a soma desejada.

No capítulo anterior, vimos como ver todos os pagamentos feitos


pelos clientes para todas as consultas.

Vejamos agora como usar agrupamento nesta pesquisa e ter o total


pago por cliente por consulta:

Usamos a função SUM(valorPago) porque estamos interessados


em somar os valores, mas poderíamos usar outra função de agrupamento
(aggregate functions) como AVG para ter uma média, por exemplo.

A cláusula GROUP BY acima diz que queremos agrupar a pesquisa


por cliente e data da consulta (usamos a data da consulta para
identificar cada consulta feita por um cliente).

Se quiséssemos somar todos os valores pagos por cliente no total,


ou seja, em todas as consultas feitas, agruparíamos apenas por cliente
(usando nome, no nosso caso): GROUP BY Cliente.nome.

Observe que, para efeito de nosso exemplo, em nossa base de


dados, os nomes identificam unicamente o cliente, mas, na prática,
usaríamos CPF ou outro campo que realmente cumpre esta função, pois
poderíamos ter dois clientes distintos com o mesmo nome.

Observe também que não faz sentido selecionarmos campos que


não sejam utilizados em GROUP BY ou que não estão sendo agrupados
através de uma função qualquer, pois o MySql retornaria o valor deste
campo no primeiro registro encontrado na tabela para cada agrupamento.

Por exemplo, se adicionarmos dataPagamento no comando acima,


para cada grupo Cliente/Consulta o SGBD retornará, para este campo, o
primeiro registro que achar na base de dados.

O uso do campo dataPagamento na pesquisa acima é permitido no


MySql mas é considerado um erro em SQL padrão, por isso não
necessariamente será correto em todos os SGBDs.

Se estivéssemos interessados em retornar resultados de uma


consulta específica, ou de um cliente em particular, bastaria adicionar uma
cláusula WHERE para filtrar o resultado retornado.
Existem várias funções de agregação, entre elas SUM, AVG, MAX,
MIN, COUNT.

Você pode variar o uso da função no comando acima para entender


o que cada uma faz.

Outros SGBDs podem ter outras funções de agregação, sendo


necessário verificar a documentação do banco de dados específico.

Veja que podemos usar tais funções sem usar uma cláusula
GROUP BY, nesse caso temos o resultado para todos os registros da
tabela, como fizemos, em exemplos anteriores, ao usar COUNT para
contar o número de registros em uma tabela.
HAVING

Como faríamos se quiséssemos filtrar o resultado do comando


SELECT que usamos para ver os valores pagos pelos clientes, de modo
que apenas clientes cujos pagamentos, por exemplo, sejam iguais ou
superiores (no total) a, digamos, R$100,00 apareçam no resultado do
comando ?

Poderíamos pensar em usar uma cláusula WHERE


SUM(valorPago) >= 100, mas, se tentarmos realizar tal pesquisa,
veremos que a mesma não é válida.

A cláusula WHERE não aceita o uso de funções de agrupamento,


como SUM.

Precisamos, para ter o resultado desejado, usar a cláusula HAVING.


Podemos ter uma pesquisa com ambas as cláusulas, usando
WHERE para filtrar campos e HAVING para filtrar o resultado de funções
de agregação.

Tenha em mente que a ordem das cláusulas no comando SELECT


é relevante, ou seja, devemos usar WHERE antes de GROUP BY, que
deve vir antes de HAVING.

Quando cometemos um erro em um comando do SQL, como


SELECT , muitas vezes a mensagem de erro retornada não ajuda muito a
entender o que fizemos de errado.

Se trocarmos a ordem das cláusulas, por exemplo, "You have an


error in your SQL syntax" não é uma mensagem muito esclarecedora.

O melhor que fazemos, nestes casos, é procurar na internet, usando


o código de erro gerado, por alguma explicação para o erro, em foruns de
discussão, onde outros usuários podem ter enfrentado a mesma situação.
EXISTS

Imagine que queremos listar as consultas para as quais não foram


realizados nenhum pagamento.

Podemos verificar se uma pesquisa retorna um ou mais registros


com a função EXISTS.

É possível usar NOT EXISTS para verificar se uma pesquisa retorna


zero registros.

Poderíamos, desse modo, pesquisar consultas que não possuam


nenhum pagamento na tabela Pagamento, da seguinte forma:

Se você pesquisar a tabela Pagamento, verá que não existe


nenhum pagamento feito para a consulta cujo id é 4.

O que fizemos acima foi fazer esta pesquisa dentro de outra


pesquisa, mas em vez de usarmos um valor literal, utilizamos o campo id
de cada registro de Consulta, e o MySql retornou um registro desta
tabela sempre que não encontrou nenhum registro em Pagamento que
atenda ao critério WHERE consulta=C.id, onde consulta é o campo em
Pagamento que faz referência à Consulta.
O uso de um alias dentro da pesquisa dentro de outra pesquisa é
obrigatório, caso contrário o SGBD retornará um erro.

Se não definirmos um alias e usarmos, por exemplo, Consulta.id


em vez de C.id, o MySql iria procurar Consulta na lista de tabelas da
cláusula FROM do SELECT aninhado e, não encontrando-o, retornaria um
erro.

Obviamente, poderíamos escolher Consulta como alias para


Consulta, mas na prática se escolhe um nome diferente.

Veja que aproveitamos, na pesquisa acima, e calculamos a soma de


todos os serviços executados nesta consulta, retornando este valor junto
com a data da consulta e seu id.

O uso de EXISTS segue a mesma sintaxe explicada acima para


NOT EXISTS.
Pesquisa aninhadas

Utilizamos, até agora, em vários pontos deste livro, pesquisas


aninhadas.

Você viu que podemos ter um SELECT dentro de outro, seja na lista
de campos a serem retornados, numa cláusula WHERE, enfim,
basicamente em qualquer parte da pesquisa.

Não estamos limitados a apenas uma pesquisa dentro de outra.

Imagine que desejamos listar as consultas para as quais faltam


valores a serem pagos.

Precisamos somar todos os valores pagos para determinada


consulta e verificar se o valor é menor que o total de serviços executados
nesta consulta.

Há um problema, entretanto, com a consulta acima, pois se não


houver nenhum pagamento na tabela Pagamento, o comando SELECT
respectivo retornará um valor nulo e não 0, de modo que a comparação
feita na cláusula WHERE irá falhar, para esta consulta, e o registro
correspondente não sera mostrado.

Em SQL, comparar qualquer valor com nulo sempre retorna nulo.

Comparações com valores nulos, em geral, geram resultados


indesejados.

Uma forma de resolver a questão é incluir um filtro NOT EXISTS à


cláusula WHERE.

Esta não é, necessariamente, uma pesquisa eficiente, uma vez que


estamos fazendo duas pesquisas na tabela de Pagamento quando uma
pesquisa deveria ser o suficinete.

Os SGBDs modernos possuem vários otimizadores internos, que


poderiam, no caso acima, verificar que estamos pesquisando o mesmo
campo com o mesmo critério na mesma tabela, mas podemos criar outras
pesquisas que não dependam deste tipo de otimização, sobre a qual não
possuímos nenhum controle.
Uma forma de construr a pesquisa para evitar a situação acima
seria a mostrada a seguir:

Neste pesquisa, totalPago e aPagar são campos calculados e não


campos reais de qualquer tabela, os quais, contudo, além de serem
retornados pela pesquisa, podem ser utilizados pela cláusula HAViNG
para aplicar os filtros desejados, quais sejam que ou não existe nenhum
total pago ( campo é nulo ) ou o total pago é menor que o montante a
pagar.

Veja que utilizamos a cláusula HAVING e não WHERE, pois esta


não admite o uso de um campo calculado, da mesma forma que não
admitia uma função agregada, que não deixa ser um campo calculado
(ainda que sem nome).

Em contrapartida, observe que o teste para saber se o campo


totalpago é nulo é feito com uma função ISNULL, e não com a condição
IS NULL, pois esta só pode ser usada em uma cláusula WHERE.

A pesquisa acima possui a vantagem, em relação à anterior, de


permitir sabermos quanto falta pagar para cada consulta, pois poderíamos
gerar um terceiro campo calculado faltaPagar usando os dois anteriores
Gerar um campo calculado com base em outro requer uma sintaxe,
pelo menos no MySql, um pouco estranha, como mostramos a segur:

É possível ver que fizemos referência aos campos calculados


aPagar e totalPago usando outro SELECT.

Caso você tente referenciá-los diretamente, o MySql retornará uma


mensagem dizendo que não reconhece tais campos.

Este comportamento não necessariamente será o mesmo em


todos os SGBDs.

Observe que a pesquisa acima não retorna um valor correto para


FaltaPagar quando o campo totalPago é nulo.

Uma forma de conseguir retornar o valor desejado é utilizando-se


variáveis dentro do SELECT, como fizemos na pesquisa abaixo:
Neste caso, guargamos em @pago e @total os valores dos campos
e geramos outro campo Falta Pagar com o resultado de @pago-@total.

Utilizamos, ainda, três funções para gerar o valor correto no caso de


@pago ser nulo.

A função ISNULL, como visto anteriormente, retorna verdadeiro se


o seu argumento é nulo e falso em caso contrário.

A função CAST converte seu argumento para um tipo desejado.

Na pesquisa acima, @pago-@total retorna um número e CAST


garante que este número é formatado como um DECIMAL(6,2), que é o
tipo dos campos que guardam valor, em nossa base.

Poderíamos usar CAST(<expressão> AS CHAR(20) ) para


converter o valor para um string ou AS SIGNED INTEGER / AS
UNSIGNED INTEGER para descartar a parte decimal do número.

A função IF testa seu primeiro argumento e, caso seja verdadeiro,


retorna seu segundo argumento (@total), caso contrário retorna seu
terceiro argumento (@pago-@total).

Existem ainda dois outros operadores - ANY e ALL - que podem ser
usados com pesquisas aninhadas.
Em ambos, testamos uma expressão contra todos os registros
retornados por uma pesquisa.

No primeiro caso, caso qualquer (ANY) registro retornado faça a


expressão ser verdadeira, o resultado é verdadeiro.

Por exemplo:

SELECT * FROM PAGAMENTO WHERE valorPago > ANY


(SELECT valor FROM ServicoPrestado WHERE valor < 50 )

Retornaria todos os registros em pagamento para os quais o campo


valorPago for maior que qualquer valor do campo valor em qualquer
registro retornado pela pesquisa aninhada.

No caso de usarmos ALL (todos), seria necessário que o valor


fosse maior que todos os valores retornados pela pesquisa aninhada.

Observe que a pesquisa aninhada deve retornar um conjunto de


registros com um campo compatível com o tipo contra o qual está sendo
testado.

Também podemos utilizar uma pesquisa aninhada no lugar de uma


tabela, na cláusula FROM de um comando SELECT:
O uso apropriado de pesquisas aninhadas, também chamadas
subqueries, pode impactar na velocidade de execução da pesquisa, mas
este tópico, otimização, é extenso e complexo demais para ser tratado
em um livro introdutório em SQL, como se propõe ser o presente livro.
Visões

Imagine que vamos criar uma base de dados com os dados dos
funcionários de uma empresa.

Nossa base terá, no mínimo, uma tabela com os dados básicos do


funcionário, incluindo seu salário.

Parte das informações dos funcionários, como seu nome e ramal,


poderíam estar disponíveis para qualquer outro funcionário, mas outras
partes, como seu endereço e seu salário, por exemplo, deveríam ser
protegidas, apenas sendo consultadas por, digamos, pessoal de recursos
humanos ou gerentes.

É necessário um mecanismo que nos permita dar segurança aos


dados armazenados em nossa tabela, de modo a restringir o acesso dos
usuários a partes das tabelas.

Para isso, criamos visões de uma tabela, que são como tabelas
virtuais, pois não existem fisicamente, mas podem ser consultadas como
se fossem tabelas.

Em nosso exemplo, seria interessante criar uma visão Dados


Básicos, que poderia ser acessada por qualquer empregado e,
possivelmente, uma Dados Financeiros, que teria acesso mais restrito.

Uma visão é criada com CREATE VIEW e nada mais é que um


comando SELECT para o qual damos um nome (o nome da visão):

Podemos, agora, fazer consultas à visão como se fosse uma tabela:

Poderíamos fazer qualquer consulta à visão, selecionando campos,


aplicando funções, cláusulas WHERE ou GROUP BY, da mesma forma
como fizemos anteriormente com outras tabelas, utilizando o comando
SELECT.

O objetivo, com a visão acima, é criar acesso a uma parte da


tabela, protegendo informações sensíveis, como o salário ou o CPF de
um funcionário.

Em nosso SGBD, dariámos acesso restrito à tabela Funcionario


mas poderíamos dar acesso irrestrito à visão DadosBásicos.
Podemos dar acesso de pesquisa (SELECT) apenas para a visão
DadosBásicos para um usuário e nenhum outro acesso à base de dados,
de modo que ele não poderá consultar a tabela Funcionario.

Em linha de comando, usaríamos GRANT SELECT ON


Funcionarios.DadosBasicos TO <usuario>.

No WorkBench, como root, podemos criar um Schema Privilege


para o usuário, utilizando Schema matching pattern, onde colocaríamos
Funcionarios.DadosBasicos ao qual, depois de criado, selecionaríamos
SELECT e depois Apply.

Assim como podemos ver o comando necessário para a criação de


uma tabela, podemos fazer o mesmo em relação a uma visão com SHOW
CREATE VIEW:

Se quisermos mudar a definição de uma visão, ou seja, mudar o


conteúdo do SELECT que a visão representa, usamos CREATE OR
REPLACE VIEW :
Podemos excluir uma visão com DROP VIEW <nome da visão>.

Uma visão não precisa se restringir a uma tabela, podemos criar


visões tão complexas quanto qualquer SELECT pode ser.

Outro uso de visões é para esconder a complexidade do comando


SELECT.

Na base de dados Consultório, poderíamos criar visões para


facilitar consultas, quando as mesmas são muito complexas, como
algumas das criadas no capítulo anterior.

Com a visão criada acima, poderíamos realizar várias pesquisas,


tendo o resultado da visão como base.

Por exemplo, podemos pesquisar quais consultas cujos débitos são


maiores de R$100,00:
É possível criar uma nova tabela com o resultado de uma pesquisa
a uma visão (ou com o resultado de qualquer comando SELECT).

Se quisermos criar uma tabela chamada ConsultasComDebito com


o resultado de uma pesquisa à visão ConsultasEmAberto utilizamos
CREATE TABLE com a seguinte sintaxe:

Não precisamos selecionar todos os campos da visão e podemos


incluir uma cláusula WHERE se quisermos.

O resultado é que temos uma nova tabela, com dados copiados do


resultado da visão.
Esta tabela é independente das tabelas que formam o SELECT na
visão e não será atualizada caso façamos mudanças nas outras tabelas.
Mais funções

Existem centenas de funções disponíveis no MySql e outros


SGBDs, para usarmos junto com os comandos da linguagem SQL.

Vamos mostrar algumas funções úteis ao usarmos SQL, mas


recomendamos buscar a lista de todas as funções no manual de
referências, disponível no site do MySql.
CASE

Recordemos da nossa base de dados Loja.

Imagine que você quer verificar a quantidade de produtos em


estoque para tomar uma atitude para cada produto, dependendo da
quantidade em estoque.

Se houver menos de 10 itens em estoque, deve-se comprar mais.

Se a quantidade for maior que 30, devemos fazer uma promoção.

Caso contrário, não há nada a fazer.

Podemos usar CASE WHEN <teste1> THEN <valor1> ... ELSE


<valor> END para retornar um valor diferente em cada situação descrita
acima.
Executa-se cada teste por vez e, caso seja verdadeiro, retorna-se o
valor associado que está após THEN e a função termina.

A cláusula ELSE é opcional e não testa uma condição, mas retorna


um valor sempre que nenhuma outra condição for verdadeira.

Não somente com SELECT se pode utilizar tal função.

Podemos atualizar um campo utilizando CASE.

Para cada registro, utiliza-se o valor de quantidade naquele registro


para os testes.

Esta função pode ser encontrada também na seguinte forma: CASE


<valor> WHEN <compara1> THEN <retorno1> ... ELSE <retorno> END.

Neste caso, testamos se <valor> é igual a <compara1> e


retornamos o valor <retorno1>, caso contrário testamos, se houver
<compara2>... até encontrar, se houver, a cláusula ELSE ou não retornar
valor algum.
TRIM, LTRIM, RTRIM

A função LTRIM retorna uma string sem espaços em branco à


esquerda (até encontrar o primeiro caracter não espaço) enquanto RTRIM
faz o mesmo para espaços à direita.

TRIM retona a string sem espaços tanto à esquerda quanto à


direita.
FORMAT

Esta função permite formatar um número para uma determinada


quantidade de casas decimais.

Pode ser utilizada também para localização, ou seja, para que o


número utilize o formato de um determinado país.

É possível, por exemplo, mostrar número com vírgula (padrão


brasileiro) em vez de ponto (padrão americano) para divisor de ponto
decimal.
Funções matemáticas

A função ABS(<número>) retorna o número sem sinal, ou seja,


ABS(-5) = 5.

CEIL(<número>) retorna o próximo número inteiro maior que


<número>, caso este não seja um inteiro.

FLOOR(<número>), por outro lado, retorna o próximo inteiro menor


que <número>.

Já ROUND(<número>) retorna o número sem a parte decimal.

TRUNCATE(<número>, <decimais>) retorna <número> com


<decimais> casas decimais.

RAND( <semente> ) retorna um número aleatório maior ou igual a 0


e menor que 1.

O argumento <semente> é opcional e, se fornecido, é utilizado para


inicializar o gerador de números aleatórios, fazendo com que a mesma
sequência seja fornecida sempre que se utilizar o mesmo número como
<semente>.
Funções de manipulação de data

Estas funções são úteis para modificarmos e compararmos datas,


além de extrairmos pedaços de informação das mesmas (dia, mês, ano ).

ADDDATE( <data>, INTERVAL <quantidade> <unidade>)


adiciona a <data> um número <quantidade> de dias, meses, anos
(<unidade>).

ADDDATE( <data>, <dias>) adiciona um quantidade <dias> a


<data>.

A função DATESUB( <data>, INTERVAL <quantidade>


<unidade>) faz o oposto da função DATEADD.

A função DATEDIFF( <data1>, <data2> ) retorna o número de dias


entre <data1> e <data2>.
DAY(<data>) retorna o dia em <data>.

MONTH e YEAR fazem o mesmo em relação ao mês e ao ano.

DAYOFWEEK(<data>) retorna o dia da semana como um número,


sendo que segunda-feira é 1, terça é 2,...sábado é 6 e domingo
corresponde ao número 7.
Transações

Transações são um grupo de operações que devem ou todas serem


completadas ou nenhuma ter efeito.

O exemplo mais simples de transação é uma transação bancária.

Se você fizer uma transferência de X da sua conta para a conta de


outra pessoa, este valor será debitado de sua conta e o mesmo valor
creditado na conta da outra pessoa.

Caso exista algum erro no sistema bancário e esta transação não se


complete, o valor X deve ser devolvido para a sua conta.

Iniciamos uma transação com START TRANSACTION ou BEGIN.

Fazemos todas as alterações na base de dados que queremos.

No exemplo acima, fariamos uma subtração de X no saldo de uma


conta e a soma de X no saldo de outra.

Quando todas as operações foram realizadas, terminamos a


transação com o comando COMMIT.

Se algo anormal acontecer e desejarmos cancelar a transação,


utilizamos ROLLBACK.

Isto significaria que a base de dados retornaria ao que era antes de


utilizarmos START TRANSACTION.

A sintaxe utilizada em transações pode variar em outros SGBDs.

Por exemplo, na base de dados Funcionarios, vamos criar uma


transação dentro da qual iremos dar um aumento de 10% para todos os
funcionários na tabela Funcionario.
Podemos verificar que a tabela foi modificada com um SELECT.

Se utilizarmos o comando COMMIT terminaremos a transação e as


mudanças serão permanentes na tabela.

Se, ao contrário, utilizarmos ROLLBACK, a tabela irá voltar ao que


era antes de iniciarmos a transação.
Normalização

Normalização ou formas normais são regras que utilizamos para


nos ajudar a desenhar tabelas eficientes na nossa base de dados
reduzindo, entre outras coisas, a redundância de dados (guardar os
mesmos dados em várias tabelas).

No mínimo, devemos tentar adequar nossa base de dados para que


respeite as três primeiras normas formais ( NF1, NF2 e NF3 ), que
passamos a descrever.
Primeira Forma Normal

Uma tabela está na primeira forma normal se não possuir campos


multivalorados.

Falamos em campos multivalorados quando dissemos que cada


produto em nossa tabela Compra, na base de dados Loja, deve estar em
um registro diferente, em vez de termos vários produtos em um mesmo
campo da tabela.

A tabela Compra, portanto, está na primeira forma normal (NF1).

Em Contato, nossa tabela Lista contém um campo telefone.

Poderíamos desejar colocar em telefone, os números de celular,


trabalho e da casa do contato, o que o tornaria um campo multivalorado,
contrariando a NF1.

Neste caso, o melhor seria criarmos uma outra tabela Telefone e


criar um relacionamento entre esta e Lista, de modo que cada telefone de
um contato ocuparia um registro daquela tabela e faria referência a um
registro (um contato) desta.

Também não devemos ter tabelas aninhadas, para atender à NF1.

Imagine que na base Funcionarios guardamos em Funcionario, para


cada funcionário na tabela (cada registro) também os campos
departamento, chefeDoDepartamento e localização, contendo o nome do
departamento em que o funcionário trabalha, o nome do chefe daquele
departamento e o local físico onde o mesmo se encontra.

Neste caso, como vários funcionários trabalham em um mesmo


departamento, teríamos uma tabela aninhada a outra, o que é um erro.

O que devemos fazer, em tal caso, é criar uma tabela


Departamento, contendo estas informações e criar um campo
departamento em Funcionario que fará referência a um registro de
Departamento, de modo que separamos as duas tabelas que estavam
indevidamente juntas em uma.
Segunda Forma Normal

Uma tabela está na NF2 quando, além de estar na NF1, não possui
dependências parciais.

O que são dependências parciais?

Quando um campo da tabela é determinado por outro campo


(depende dele), sendo este campo parte da chave primária (sem ser a
chave inteira).

Na nossa base de dados Loja, cada compra na tabela Compra


possuía como chave primária cliente, produto e dataCompra.

Suponha que resolvemos criar um campo para guardar o desconto


dado ao cliente, que, na nossa loja, varia de acordo com o cliente.

Observamos que o valor de desconto depende apenas do valor de


cliente na tabela Compra, mas é independente do produto e data da
compra.

Existe uma dependência parcial em relação à chave primária.

Para cada cliente há um desconto específico, mas este desconto


não depende do resto da chave primária.

Neste caso, não estamos atendendo à NF2.

O que devemos fazer é retirar o campo desta tabela e colocá-lo em


Cliente.
Terceira Forma Normal

Uma tabela está na terceira forma normal quando, além de estar na


NF1 e NF2, não possui dependências transitivas.

Imagine que na tabela Compra em Loja guardássemos também a


informação do vendedor que realizou a compra, fazendo referência a uma
tabela Vendedor, que contém o nome, setor em que trabalha e outras
informações do mesmo.

Além disso, guardamos em Compra a comissão (%) que o vendedor


ganha.

Podemos notar que o valor no campo vendedor, numa compra,


determina o valor de comissão.

Ou seja, comissão depende de vendedor.

Entretanto, vendedor não identifica unicamente um registro em


Compra, ou seja, não é a chave primária de Compra.

Está contrário à NF3 que comissão dependa de vendedor, uma vez


que este não é a chave primária da tabela.

Obviamente que o valor em vendedor depende da chave primária


da tabela, ou seja para uma determinada compra ( cliente+produto+data)
temos um vendedor que a realizou, o que está correto.

No caso de comissão, o valor não depende da compra (da chave


primária da tabela) e sim do vendedor, que não é a chave primária,
devendo ser retirado da tabela.

Neste caso, devemos simplesmente colocar comissão em


Vendedor, pois precisamos da informação para calcular o valor que o
vendedor irá receber pela venda.

Você também pode gostar