SQL e modelagem com bando de dados

Propaganda
Alura
Sumário
Sumário
1Objetivosdocurso
1
1.1Oqueérealmenteimportante?
1
1.2Sobreosexercícios
1
1.3Tirandodúvidaseindoalém
2
2Meuproblema
3
2.1Criandoonossobancodedados
5
2.2Começandoumcadernonovo:criandoobanco
5
2.3Opadrãoutilizadonestecurso
6
2.4Atabeladecompras
6
2.5Conferindoaexistênciadeumatabela
8
2.6Inserindoregistrosnobancodedados
9
2.7Selecãosimples
10
2.8Aformataçãodenúmerosdecimais
10
2.9Achaveprimária
11
2.10Recriandoatabeladozero
13
2.11Consultascomfiltros
14
2.12Modelandotabelas
19
2.13Resumindo
21
2.14Exercícios
21
3Atualizandoeexcluindodados
23
3.1UtilizandooUPDATE
24
3.2Atualizandováriascolunasaomesmotempo
25
3.3Utilizandoumacolunacomoreferênciaparaoutracoluna
25
3.4UtilizandooDELETE
26
3.5CuidadoscomoDELETEeUPDATE
27
3.6Resumindo
27
4Alterandoerestringindooformatodenossastabelas
29
Sumário
Alura
4.1Restringindoosnulos
30
4.2AdicionandoConstraints
30
4.3ValoresDefault
31
4.4Evoluçãodobanco
31
4.5Resumindo
33
5Agrupandodadosefazendoconsultasmaisinteligentes
35
5.1Ordenandoosresultados
37
5.2Resumindo
41
6Juntandodadosdeváriastabelas
42
6.1Normalizandonossomodelo
47
6.2OnetoMany/ManytoOne
50
6.3FOREIGNKEY
50
6.4Determinandovaloresfixosnatabela
58
6.5ServerSQLModes
59
6.6Resumindo
60
7AlunossemmatrículaeoExists
62
7.1Subqueries
65
7.2Resumindo
70
8AgrupandodadoscomGROUPBY
8.1Resumindo
9FiltrandoagregaçõeseoHAVING
72
79
81
9.1CondiçõescomHAVING
83
9.2Resumindo
86
10MúltiplosvaloresnacondiçãoeoIN
10.1Resumindo
11Sub-queries
11.1Resumindo
87
93
94
101
12EntendendooLEFTJOIN
103
12.1RIGHTJOIN
109
12.2JOINouSUBQUERY?
111
12.3Resumindo
119
13MuitosalunoseoLIMIT
120
13.1Limitandoebuscandoapartirdeumaquantidadeespecífica
121
13.2Resumindo
123
Alura
Versão:19.4.23
Sumário
CAPÍTULO1
OBJETIVOSDOCURSO
que o aluno saia apto a utilizar qualquer sistema de banco de dados relacional (exemplos:
MySQL, Oracle, PostgreSQL, SQL Server). Para isso usamos sempre que possível o padrão
SQLquetodoselesaceitam.ParaacompanharaapostilasugerimosoMySQL,paraquepasse
pelosmesmosdesafiosesoluçõesqueencontramosnessecurso.Apósaprenderabaseatodos
eles,estudardetalhesespecíficosdecadabancopassaaserbemmaissimples.
salientarqueousodealgunsconceitos,comoasvantagensedesvantagensdamodelagem,são
entendidosporcompletodepoisdeumtempodeprática,alémdemudaremcomotempo.
mostrarquedecorarcomandosnãoéimportante.
1.1OQUEÉREALMENTEIMPORTANTE?
Muitoslivros,aopassardoscapítulos,mencionamtodososdetalhesdeumaferramentajuntamentecom
seus princípios básicos. Isso acaba criando muita confusão, em especial porque o estudante não
conseguedistinguirexatamenteoqueéprimordialaprendernoinício,daquiloquepodeserestudado
maisadiante.Essetipodeinformaçãoseráadquiridacomotempo,enãoénecessárionoinício:essetipo
de filtragem já fizemos e aplicamos aqui para você aprender a medida certa, e se aprofundar no
momentoquefizersentidocontinuandoseusestudosemoutroscursosnossosnoAlura.
Nestecurso,separamosessasinformaçõesemquadrosespeciais,jáquesãoinformaçõesextras.Ou
então, apenas citamos num exercício e deixamos para o leitor procurar informações se for de seu
interesse.
Porfim,faltamencionaralgosobreaprática,quedevesertratadaseriamente:todososexercíciossão
muitoimportantes.
Ocurso
Paraaquelesqueestãofazendoocursoonlinenowww.alura.com.br,recomendamosestudaremem
casaaquiloquefoivistoduranteaaula,tentandoresolverosexercíciosedesafiosapresentados.
1.2SOBREOSEXERCÍCIOS
1OBJETIVOSDOCURSO
1
Os exercícios do curso variam de práticos até pesquisas na Internet, ou mesmo consultas sobre
assuntosavançadosemdeterminadostópicosparaincitaracuriosidadedoaprendiznatecnologia.
1.3TIRANDODÚVIDASEINDOALÉM
Paratirardúvidasdosexercícios,oudedesenvolvimentoemgeral,recomendamosofórumdoGUJ
(http://www.guj.com.br/).OGUJfoifundadopordesenvolvedoresdoAluraedaCaelum,ehojeconta
commaisdeummilhãodemensagens.
Quandoterminaressaaventuraesperoencontrá-lonoAlura,ondepoderácontinuarcomdiversos
outroscursosnaáreadedesenvolvimentoetecnologia.
http://www.alura.com.br/
Seoquevocêestábuscandosãolivrosdeapoio,sugerimosconheceraeditoraCasadoCódigo:
http://www.casadocodigo.com.br
SOBREOCURSOBANCODEDADOSEMODELAGEM
BemvindoaocursodeBancodeDadoseModelagemdoAlura.Nessaapostilavocêvaiencontrara
transcriçãodocursoquepodeserencontradoemsuaversãomaisrecenteemwww.alura.com.br.
Comodesenvolvedorpasseimaisde15anosprocurandoexemplosreaisdeensinoparapoderajudar
meus colegas desenvolvedores que estagiavam na minha equipe, e junto da equipe de instrutores do
Alura,agrupeiessesexemplosnessecurso.
Nosso foco aqui é mostrar o que é um banco de dados relacional, como pensar a modelagem de
nossosdadosecomocriar,inserir,atualizarepesquisarinformaçõesemumabasededados.Tudoisso
usandoexemplosdomundoreal,nadadeabstraçõesgenéricasquenoslevamaquestionarousorealda
tecnologianodiaadia.
ComousamosoMySQLcomosoftwaredebancodedadosnestecurso,vocêpoderáexecutartodos
osexemplosnasuamáquinaemcasamesmo.
2
1.3TIRANDODÚVIDASEINDOALÉM
CAPÍTULO2
MEUPROBLEMA
Chegou o fim do mês e não sei direito onde meu dinheiro foi parar, no meu bolso não está. Nem na
contabancária.Éumproblemacomum,ecomopodemosfazerparacontrolarosgastos?
A maneira mais simples de entender onde o dinheiro está indo é anotar todos os nossos gastos,
sendoumadasmaistradicionaiseantigasescrevê-losemumcaderninho.Porexemplodia05/01/2016
gasteiR$20emumaLanchonete,escrevoissobonitinhoemumalinha.
R$2005/01/2016Lanchonete
Tiveumoutrogastodelanchonetenodia06/01/2016novalordeR$15,voueanotonovamente:
R$2005/01/2016Lanchonete
R$1506/01/2016Lanchonete
Porfim,minhaesposacomprouumguarda-roupaproquartoeeleaindanãochegou,entãoanotei:
R$2005/01/2016Lanchonete
R$1506/01/2016Lanchonete
R$915,506/01/2016Guarda-roupa(nãorecebi)
Reparaquedojeitoqueanotei,aslinhasacabamseassemelhandoaumatabelinha:
+---------+------------+--------------+--------------+
+R$20+05/01/2016+Lanchonete+recebida+
+R$15+06/01/2016+Lanchonete+recebida+
+R$915,5+06/01/2016+Guarda-roupa+nãorecebida+
+---------+------------+--------------+--------------+
Masoquesignificaaprimeiracolunadessatabelamesmo?Ovalorgasto?Ok.Easegunda?Éadata
dacompra?Eaterceira,éolugarquecompreiousãoanotaçõesrelativasaosgastos?Reparaquesemdar
nomeascolunas,minhascomprasficamconfusas,atabelaficaestranha,epossocadavezpreenchercom
algoqueachoquedevo,aoinvésdetercertezadoquecadacamposignifica.Comoidentificarcadaum
deles? Vamos dar um nome aos três campos que compõem a minha tabela: o valor, a data e as
observações:
+---------+------------+--------------+--------------+
+valor+data+observacoes+recebida+
+---------+------------+--------------+--------------+
+R$20+05/01/2016+Lanchonete+recebida+
+R$15+06/01/2016+Lanchonete+recebida+
+R$915,5+06/01/2016+Guarda-roupa+nãorecebida+
+---------+------------+--------------+--------------+
2MEUPROBLEMA
3
Possotambémsimplificarosdadosdacolunarecebidasomenteindicandoseelafoirecebida(sim)
ounão(não):
+---------+------------+--------------+--------------+
+valor+data+observacoes+recebida+
+---------+------------+--------------+--------------+
+R$20+05/01/2016+Lanchonete+sim+
+R$15+06/01/2016+Lanchonete+sim+
+R$915,5+06/01/2016+Guarda-roupa+não+
+---------+------------+--------------+--------------+
Para montar essa tabela em um caderno eu tenho que ser capaz de traçar linhas bonitinhas e eu,
pessoalmente,souhorrívelparaisso.Nomundotecnológicodehojeemdia,anotaríamosessatabelaem
umaplanilhaeletrônicacomooExcel:
Figura2.1:Planilhaexemplo
UMBANCOPARAOSDADOS
Porém, além de guardar as informações, eu quero manter um histórico, tirar média semestral ou
mensal,saberquantoqueeugasteicomlanchoneteoumercadoduranteumperíodoerealizartarefas
futuras como relatórios complexos. O Excel é uma ferramenta bem poderosa e flexível, porém,
dependendodaquantidadederegistros,amanipulaçãodosdadoscomeçaaficarumpoucocomplicada
emumaplanilhaeletrônica.
Parafacilitaronossotrabalho,existemsoftwaresparaarmazenar,guardarosdados.Essesbancosde
dados são sistemas que gerenciam desde a forma de armazenar a informação como também como
consultá-lademaneiraeficiente.
OschamadosSistemadeGerenciamentodeBancodeDados(SGBD),nospermitemrealizartodas
essastarefassemnospreocuparmoscomocaderninhoouacaneta.Claroquesevamosnoscomunicar
com alguém que vai gerenciar nossos dados precisamos falar uma língua comum, conversar em uma
linguagempadrãoqueváriosbancosutilizem.
Umpadrãoquesurgiuparaacessarmosepesquisarmosdadosarmazenadoseestruturadosdeuma
forma específica é uma linguagem de consultas estruturadas, Structured Query Language, ou
simplesmenteSQL.Paraconseguirmosutilizaressalinguagem,precisamosinstalarumSGBDqueseráo
nossoservidordebancodedados,comoporexemplooMySQL,OracleouSQLServer.
4
2MEUPROBLEMA
NestecursoutilizaremosoMySQL,umSGBDgratuitoquepodeserinstaladoseguindoasmaneiras
tradicionaisdecadasistemaoperacional.Porexemplo,noWindows,oprocessoérealizadoatravésdo
downloaddeumarquivo.msi,enquantonoLinux(versõesbaseadasemDebian),podeserfeitoatravés
de um simples comando apt-get, e no MacOS pode ser instalado com um pacote .dmg. Baixe o seu
instaladoremhttp://dev.mysql.com/downloads/mysql/
2.1CRIANDOONOSSOBANCODEDADOS
Durante todo o curso usaremos o terminal do MySQL. Existe também a interface gráfica do
WorkbenchdoMySQL,quenãoutilizaremos.Aoutilizarmosoterminalnãonospreocupamoscomo
aprendizadodemaisumaferramentaqueéopcional,podendonosconcentrarnoSQL,queéoobjetivo
destecurso.
Abraoterminaldoseusistemaoperacional.NoWindows,digitecmdnoExecutar.NoMaceLinux,
abraoterminal.Nele,vamosentrarnoMySQLusandoousuárioroot:
mysql-uroot-p
O -u indica o usuário root, e o -p é porque digitaremos a senha. Use a senha que definiu
duranteainstalaçãodoMySQL,notequeporpadrãoasenhapodeserembrancoe,nessecaso,basta
pressionarenter.
2.2COMEÇANDOUMCADERNONOVO:CRIANDOOBANCO
AgoraqueestamosconectadosaoMySQLprecisamosdizeraelequequeremosumcadernonovo.
Começaremos com um que gerenciará todos os dados relativos aos nossos gastos, permitindo que
controlemos melhor nossos gastos, portanto quero um banco de dados chamado ControleDeGastos,
podeparecerestranho,masnãoexisteumpadrãoparanomearumbancodedados,porissoutilizamoso
padrãoCamelCase.ParacriarumbancodedadosbastamandaroMySQLcriarumbanco:
mysql>CREATEDATABASE
Masqualonomemesmo?ControleDeGastos?Então:
mysql>CREATEDATABASEControleDeGastos
DouentereoMySQLficaesperandomaisinformações:
mysql>CREATEDATABASEControleDeGastos
->
AcontecequeemgeralprecisamosnotificaroMySQLqueocomandoquedesejamosjáfoidigitado
porcompleto.Parafazerissousamosocaracterepontoevírgula(;):
mysql>CREATEDATABASEControleDeGastos
->;
QueryOK,1rowaffected(0.01sec)
2.1CRIANDOONOSSOBANCODEDADOS
5
Agorasim,elepercebeuqueacabamosnossocomando(quecomeçounaprimeiralinha)eindicou
queopedido(aquery)foiexecutadacomsucesso(OK),demorou0.01segundoegerou1resultado(1
rowaffected).
Damesmamaneiraquecriamosessebanco,nãoéumaregra,masécomumterumbancodedados
paracadaprojeto.Porexemplo:
mysql>CREATEDATABASEcaelum
->;
QueryOK,1rowaffected(0.01sec)
mysql>CREATEDATABASEalura
->;
QueryOK,1rowaffected(0.01sec)
mysql>CREATEDATABASEcasadocodigo
->;
QueryOK,1rowaffected(0.01sec)
Agoraquetenhodiversosbancos,comopossodizeraoMySQLquequeremosusaraqueleprimeiro
de todos? Quero dizer para ele, por favor, use o banco chamado ControleDeGastos. Usar em inglês, é
USE,portanto:
mysql>USEControleDeGastos;
Databasechanged
2.3OPADRÃOUTILIZADONESTECURSO
Reparequeaspalavrasquesãodeminhaautoria,minhaescolha,deixamosemminúsculo,enquanto
as palavras que são específicas da linguagem SQL deixamos em maiúsculo. Esse é um padrão como
existem diversos outros padrões. Adotamos este para que a qualquer momento que você veja um
comandoSQLnestecurso,identifiqueoqueépalavraimportante(palavrachave)doSQLeoqueéo
nome de um banco de dados, uma tabela, uma coluna ou qualquer outro tipo de palavra que é uma
escolhalivreminha,comousuáriodobanco.
2.4ATABELADECOMPRAS
Agoraquejátenhoumcaderno,jáestouloucoparaescreveromeuprimeirogasto,quefoinuma
lanchonete.Porexemplo,pegueiumapartedomeucaderno,deverdade,de2016:
+---------+------------+--------------+--------------+
+valor+data+observacoes+recebida+
+---------+------------+--------------+--------------+
+R$20+05/01/2016+Lanchonete+sim+
+R$15+06/01/2016+Lanchonete+sim+
+R$915,5+06/01/2016+Guarda-roupa+não+
+...+...+...+...+
+---------+------------+--------------+--------------+
Ok.Querocolocaraminhaprimeiraanotaçãonobanco.Masseobancoéocaderno...comoqueo
6
2.3OPADRÃOUTILIZADONESTECURSO
bancosabequeexisteumatabeladevalor,dataeobservações?Aliás,atémesmoquandocomeceicomo
meucaderno,fuieu,Guilherme,quetivequedesenharatabelacomsuastrêscoluninhasemcadauma
daspáginasqueeuquiscolocarseusdados.
Então vamos fazer a mesma coisa com o banco. Vamos dizer a ele que queremos ter uma tabela:
descrevemosaestruturadelaatravésdeumcomandoSQL.Nessecomandodevemosdizerquaissãoos
campos(valor,dataeobservações)queserãoutilizados,paraqueelesepareoespaçoespecíficoparacada
um deles toda vez que inserimos uma nova compra, toda vez que registramos um novo dado (um
registronovo).
Paracriarumatabela,novamentefalamosinglês,porfavorsenhorMySQL,crieumatabela(CREATE
TABLE)chamadacompras:
mysql>CREATETABLEcompras;
ERROR1113(42000):Atablemusthaveatleast1column
Como assim erro 1113? Logo após o código do erro, o banco nos informou que toda tabela deve
conterpelomenosumacoluna.Verdade,nãofaleiascolunasquequeriacriar.Vamosolharnovamente
nossasinformações:
+---------+------------+--------------+--------------+
+valor+data+observacoes+recebida+
+---------+------------+--------------+--------------+
+R$20+05/01/2016+Lanchonete+sim+
+R$15+06/01/2016+Lanchonete+sim+
+R$915,5+06/01/2016+Guarda-roupa+não+
+...+...+...+...+
+---------+------------+--------------+--------------+
A estrutura da tabela é bem clara: são 4 campos distintos, valor que é um número com ponto
decimal,dataéumadata,observaçõesqueéumtextolivreerecebidaqueéousimounão.
Portanto vamos dizer ao banco que queremos esses 4 campos na nossa tabela, com uma única
ressalva, que para evitar problemas de encoding usamos sempre nomes que usam caracteres simples,
nadadeacentoparaosnomesdenossastabelasecolunas:
mysql>CREATETABLEcompras(
valor,
data,
observacoes,
recebida);
ERROR1064(42000):YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourM
ySQLserverversionfortherightsyntaxtousenear'
data,
observacoes,
recebida)'atline2
Ainda não foi dessa vez. Repara que o erro 1064 indica um erro de sintaxe no comando CREATE
TABLE.Naverdadeobancodedadosestáinteressadoemsaberqualotipodecadacampoejustamente
por isso devemos explicar campo a campo qual o tipo de cada coluna nossa. A coluna, campo, valor
recebeumvalorcompossíveiscasasdecimais.Paraissopodemosusarumtipoquerepresentanúmeros
2.4ATABELADECOMPRAS
7
decimais.Nonossocasosuportandoaté18casasantesdavírgulae2depoisdela:
valorDECIMAL(18,2),
Jáocampodataédotipodedata:
dataDATE,
A coluna observações é um texto livre, que costuma ser chamado por um conjunto variável de
caracteres, por isso VARCHAR. Assim como no caso do número decimal, devemos falar o tamanho
máximodessacoluna.Nonossocasoserãoaté255caracteres:
observacoesVARCHAR(255),
Porfimchegamosacolunarecebida,quedeveserrepresentadacomvaloresdotipoverdadeiro ou
falso,algocomosimounão.Nopadrãodebancorelacionaisnãoexisteumcampoqueaceiteosvalores
verdadeiro e falso, um campo booleano. O que fazemos então é utilizar um campo numérico com os
valores0pararepresentaronegativoe1paraocasopositivo.Umacolunacomumnumerozinho,um
TINYINT:
recebidaTINYINT
Sevamosutilizar0e1paraorecebida,nossosdadosficariamagora:
+---------+------------+--------------+----------+
+valor+data+observacoes+recebida+
+---------+------------+--------------+----------+
+R$20+05/01/2016+Lanchonete+1+
+R$15+06/01/2016+Lanchonete+1+
+R$915,5+06/01/2016+Guarda-roupa+0+
+...+...+...+...+
+---------+------------+--------------+----------+
Agoraquejásabemoscomodeclararcadacampo,vamosanovatentativadecriarnossatabela:
mysql>CREATETABLEcompras(
valorDECIMAL(18,2),
dataDATE,
observacoesVARCHAR(255),
recebidaTINYINT);
QueryOK,0rowsaffected(0.04sec)
Atabelacomprasfoicriadacomsucesso.
2.5CONFERINDOAEXISTÊNCIADEUMATABELA
Masvocêconfiaemtudoquetodomundofala?Vamosverserealmenteatabelafoicriadaemnosso
bancodedados.Sevocêpossuisseumcaderninhodetabelas,oqueeupoderiafazerparapedirparame
mostrarsuatabeladecompras?Porfavor,medescrevesuatabeladecompras?
mysql>desccompras;
+-------------+---------------+------+-----+---------+-------+
|Field|Type|Null|Key|Default|Extra|
+-------------+---------------+------+-----+---------+-------+
8
2.5CONFERINDOAEXISTÊNCIADEUMATABELA
|valor|decimal(18,2)|YES||NULL||
|data|date|YES||NULL||
|observacoes|varchar(255)|YES||NULL||
|recebida|tinyint(4)|YES||NULL||
+-------------+---------------+------+-----+---------+-------+
4rowsinset(0.01sec)
Eaíestánossatabeladecompras.Elapossui4colunas,quesãochamadosdecampos(fields). Cada
campoédeumtipodiferente(decimal,date,varcharetinyint)epossuiatémesmoinformaçõesextras
queutilizaremosnodecorrerdocurso!
2.6INSERINDOREGISTROSNOBANCODEDADOS
Chegouahoradeusarnossatabela:queremosinserirdados,entãocomeçamospedindoparainserir
algoemcompras:
INSERTINTOcompras
Nãovamoscometeromesmoerrodaúltimavez.Sedesejamosfazeralgocomos4camposdatabela,
temos que falar os valores que desejamos adicionar. Por exemplo, vamos pegar as informações da
primeiralinhadaplanilha:
Figura2.2:Planilhaexemplo
Então nós temos uma compra com valor 20, observação Lanchonete, data 05/01/2016, e que foi
recebidacomsucesso(1):
mysql>INSERTINTOcomprasVALUES(20,'Lanchonete','05/01/2016',1);
ERROR1292(22007):Incorrectdatevalue:'Lanchonete'forcolumn'data'atrow1
Parece que nosso sistema pirou. Ele achou que a Lanchonete era a data. Lanchonete era o campo
observacoes.Mas...comoelepoderiasaberisso?Eunãomencioneiparaeleaordemdosdados,entãoele
assumiu uma ordem determinada, uma ordem que eu não prestei atenção, mas foi fornecida quando
executamosoDESCcompras:
|valor|decimal(18,2)|YES||NULL||
|data|date|YES||NULL||
|observacoes|varchar(255)|YES||NULL||
|recebida|tinyint(4)|YES||NULL||
Tentamosnovamente,agoranaordemcorretadoscampos:
mysql>INSERTINTOcomprasVALUES(20,'05/01/2016','Lanchonete',1);
ERROR1292(22007):Incorrectdatevalue:'05/01/2016'forcolumn'data'atrow1
Outro erro? Agora ele está dizendo que o valor para data está incorreto, porém, aqui no Brasil,
usamosesseformatoparadatas...Oquefaremosagora?Quetaltentarmosutilizaroformatoquejávem
2.6INSERINDOREGISTROSNOBANCODEDADOS
9
porpadrãonoMySQLqueéano-mês-dia?
Vamostentarmaisumavez,porém,dessavez,comadata'2016-01-05':
mysql>INSERTINTOcomprasVALUES(20,'2016-01-05','Lanchonete',1);
QueryOK,1rowaffected(0.01sec)
Agora sim os dados foram inseridos com sucesso. Temos um novo registro em nossa tabela. Mas
porque será que o MySQL adotou o formato ano-mês-dia ao invés de dia/mês/ano? O formato
ano-mês-diaéutilizadopelopadrãoSQL,ouseja,porquestõesdepadronização,eleéoformatomais
adequadoparaquefuncioneparatodos.
2.7SELECÃOSIMPLES
Como fazemos para conferir se ele foi inserido? Pedimos para o sistema de banco de dados
selecionartodososcampos(asterisco)danossatabeladecompras.Queremosfazerumaconsulta(uma
query)deseleção(SELECT)na(FROM)tabelacompras:
mysql>SELECT*FROMcompras;
+-------+------------+-------------+----------+
|valor|data|observacoes|recebida|
+-------+------------+-------------+----------+
|20.00|2016-01-05|Lanchonete|1|
+-------+------------+-------------+----------+
1rowinset(0.00sec)
Perfeito! Registro inserido e selecionado com sucesso. Hora de revisar um errinho que deixamos
paratrás.
2.8AFORMATAÇÃODENÚMEROSDECIMAIS
Desejamosinserirosdoispróximosregistrosdenossatabela:
+---------+------------+--------------+----------+
+valor+data+observacoes+recebida+
+---------+------------+--------------+----------+
+R$20+05/01/2016+Lanchonete+1+
+R$15+06/01/2016+Lanchonete+1+
+R$915,5+06/01/2016+Guarda-roupa+0+
+...+...+...+...+
+---------+------------+--------------+----------+
PortantooprimeiroINSERT:
mysql>INSERTINTOcomprasVALUES(15,'2016-01-06','Lanchonete',1);
QueryOK,1rowaffected(0.01sec)
Easegundalinha:
mysql>INSERTINTOcomprasVALUES(915,5,'2016-01-06','Guarda-roupa',0);
ERROR1136(21S01):Columncountdoesn'tmatchvaluecountatrow1
10
2.7SELECÃOSIMPLES
Oerroindicaqueonúmerodecolunasnãocondizcomonúmerodecolunasquetemosnatabela.
VamosdarumaolhadadentrodeVALUES?
(915,5,'2016-01-06','Guarda-roupa',0)
Avírgulaapareceu4vezes,portantoteríamos5camposdiferentes!Reparequeovalor 915,5está
formatadonoestiloportuguêsdoBrasil,diferentedoestiloinglêsamericano,queusapontoparadefinir
ovalordecimal,comoem915.5.Alteramoseexecutamos:
mysql>INSERTINTOcomprasVALUES(915.5,'2016-01-06','Guarda-roupa',0);
QueryOK,1rowaffected(0.00sec)
Podemosconferiros3registrosqueinserimosatravésdoSELECTquefizemosantes:
mysql>SELECT*FROMcompras;
+--------+------------+--------------+----------+
|valor|data|observacoes|recebida|
+--------+------------+--------------+----------+
|20.00|2016-01-05|Lanchonete|1|
|15.00|2016-01-06|Lanchonete|1|
|915.50|2016-01-06|Guarda-roupa|0|
+--------+------------+--------------+----------+
3rowsinset(0.01sec)
Maseseeuquisesseinserirosdadosemumaordemdiferente?Porexemplo,primeiroinformara
data,depoisas observacoes,valor,eporfimsefoi recebidaounão?Seráquepodemosfazer
isso? Quando estamos fazendo um INSERT, podemos informar o que estamos querendo inserir por
meiodeparênteses:
mysql>INSERTINTOcompras(data,observacoes,valor,recebida)
Notequeagoraestamosinformandoexplicitamenteaordemdosvaloresqueinformaremosapósa
instruçãoVALUES:
mysql>INSERTINTOcompras(data,observacoes,valor,recebida)
VALUES('2016-01-10','Smartphone',949.99,0);
QueryOK,1rowaffected(0,00sec)
Seconsultarmosanossatabela:
mysql>SELECT*FROMcompras;
+--------+------------+--------------+----------+
|valor|data|observacoes|recebida|
+--------+------------+--------------+----------+
|20.00|2016-01-05|Lanchonete|1|
|15.00|2016-01-06|Lanchonete|1|
|915.50|2016-01-06|Guarda-roupa|0|
|949.99|2016-01-10|Smartphone|0|
+--------+------------+--------------+----------+
4rowsinset(0,00sec)
Anossacomprafoiinseridacorretamente,porémcomumaordemdiferentedaestruturadatabela.
2.9ACHAVEPRIMÁRIA
2.9ACHAVEPRIMÁRIA
11
Agoraseeuolharessaminhatabeladepoisde6meseseencontraressaslinhas,comovoufalarsobre
umadasminhascompras?Tenhoquefalar"lembradacompranalanchonetefeitanodia2016-01-05no
valorde20.00quejáfoirecebida?".Ficadifícilreferenciarcadalinhadatabelaportodososvaloresdela.
Seriamuitomaisfácilseeupudessefalar:"sabeacompra15?"ou"sabeacompra37654?".
Fica difícil falar sobre um registro se eu não tenho nada identificando ele de maneira única. Por
exemplo, como você sabe que eu sou o Guilherme, e não o Paulo? Pois meu CPF é um identificador
único, (por exemplo) 222.222.222-22. Só eu tenho esse CPF e ele me identifica. O Paulo tem o dele,
333.333.333-33.
O mesmo vale para computadores de uma marca, por exemplo, o meu tem número de série
16X000015, e o computador que está ao lado do meu tem o número de série 16X000016. A única
maneiradeidentificarunicamenteumacoisaétendoumachaveimportantíssima,umachavequeétão
importante,queéprimáriaaosbrasileiros,aoscomputadores...easminhascompras.
Mas como definir esses números únicos? Devo usar o ano de fabricação como no caso do
computadorquecomeçacom16(ano2016)?Ousequenciascomnúmerosdiferentescomonocasodo
CPF?
Existem diversas maneiras de gerar esses números únicos, mas a mais simples de todas é: começa
com 1. A primeira compra é a compra 1. A segunda compra é a 2. A terceira é a 3. Uma sequencia
natural,queéincrementadaautomaticamenteacadanovacompra,acadanovoregistro.
Éissoquequeremos,umcampoquesejanossachaveprimária(PRIMARYKEY),queéumnúmero
inteiro(INT),equesejaautomaticamenteincrementado(AUTO_INCREMENT).Sófaltadefinironome
dele, que como identifica nossa compra, usamos a abreviação id. Esse é um padrão amplamente
utilizado.PortantoqueremosumcampoidINTAUTO_INCREMENTPRIMARYKEY.
Para fazer isso vamos alterar nossa tabela ( ALTER_TABLE ) e adicionar uma coluna nova
(ADD_COLUMN):
mysql>ALTERTABLEcomprasADDCOLUMNidINTAUTO_INCREMENTPRIMARYKEY;
QueryOK,0rowsaffected(0.07sec)
Records:0Duplicates:0Warnings:0
Everificamosoresultadoemnossatabela:
mysql>SELECT*FROMcompras;
+--------+------------+--------------+----------+----+
|valor|data|observacoes|recebida|id|
+--------+------------+--------------+----------+----+
|20.00|2016-01-05|Lanchonete|1|1|
|15.00|2016-01-06|Lanchonete|1|2|
|915.50|2016-01-06|Guarda-roupa|0|3|
|949.99|2016-01-10|Smartphone|0|4|
+--------+------------+--------------+----------+----+
4rowsinset(0,00sec)
Reparequeagoratodasascompraspodemseridentificadasunicamente!
12
2.9ACHAVEPRIMÁRIA
2.10RECRIANDOATABELADOZERO
Claroqueopadrãonãoécriaro iddepoisdeumano.Emgeralcriamosumatabelajácomoseu
campo id. Vamos então adaptar nossa query para criar a tabela com todos os campos, inclusive o
campoid:
CREATETABLEcompras(
idINTAUTO_INCREMENTPRIMARYKEY,
valorDECIMAL(18,2),
dataDATE,
observacoesVARCHAR(255),
recebidaTINYINT);
ERROR1050(42S01):Table'compras'alreadyexists
Opa, como a tabela já existe não faz sentido recriá-la. Primeiro vamos jogar ela fora (DROP ),
inclusivecomtodososdadosquetemos:
DROPTABLEcompras;
Agoracriamosatabelacomtudooqueconhecemos:
CREATETABLEcompras(
idINTAUTO_INCREMENTPRIMARYKEY,
valorDECIMAL(18,2),
dataDATE,
observacoesVARCHAR(255),
recebidaTINYINT);
QueryOK,0rowsaffected(0.04sec)
Vamosverificarseficoualgumregistro:
mysql>SELECT*FROMcompras;
Emptyset(0,00sec)
Ótimo!Conseguimoslimparanossatabela,agorapodemosreenserirnossosdadosnovamente:
mysql>INSERTINTOcomprasVALUES(20,'2016-01-05','Lanchonete',1);
ERROR1136(21S01):Columncountdoesn'tmatchvaluecountatrow1
Ops,agoraanossatabelanãotemapenas4colunascomoantes,incluímosoidtambém,vejamos
adescriçãodatabelacompras:
mysql>desccompras;
+-------------+---------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------------+---------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|valor|decimal(18,2)|YES||NULL||
|data|date|YES||NULL||
|observacoes|varchar(255)|YES||NULL||
|recebida|tinyint(4)|YES||NULL||
+-------------+---------------+------+-----+---------+----------------+
5rowsinset(0,00sec)
Entãoprecisaremosdeixarexplícitooqueestamosinserindo:
2.10RECRIANDOATABELADOZERO
13
mysql>INSERTINTOcompras(valor,data,observacoes,recebida)
VALUES(20,'2016-01-05','Lanchonete',1);
QueryOK,1rowaffected(0,01sec)
Agorasimanossacomprafoicadastrada!Porém,temalgoumpoucoestranho,eoid?Seráquefoi
inseridotambém?Vejamos:
mysql>SELECT*FROMcompras;
+----+-------+------------+-------------+----------+
|id|valor|data|observacoes|recebida|
+----+-------+------------+-------------+----------+
|1|20.00|2016-01-05|Lanchonete|1|
+----+-------+------------+-------------+----------+
1rowinset(0,00sec)
Ele foi inserido automaticamente! Mas como isso aconteceu? Lembra que definimos o id como
AUTO_INCREMENT? Aquela propriedade que incrementa automaticamente a cada inserção. Então, é
exatamenteelaqueincrementouoidpranós!Agorabastaapenasinserirasdemaiscompraseverificar
oresultado:
mysql>INSERTINTOcompras(valor,data,observacoes,recebida)
VALUES(15,'2016-01-06','Lanchonete',1);
QueryOK,1rowaffected(0,00sec)
mysql>INSERTINTOcompras(valor,data,observacoes,recebida)
VALUES(915.50,'2016-01-06','Guarda-roupa',0);
QueryOK,1rowaffected(0,01sec)
mysql>INSERTINTOcompras(valor,data,observacoes,recebida)
VALUES(949.99,'2016-01-10','Smartphone',0);
QueryOK,1rowaffected(0,00sec)
Vamosverificarcomoficaramosregistrosdasnossascompras:
mysql>SELECT*FROMcompras;
+----+--------+------------+--------------+----------+
|id|valor|data|observacoes|recebida|
+----+--------+------------+--------------+----------+
|1|20.00|2016-01-05|Lanchonete|1|
|2|15.00|2016-01-06|Lanchonete|1|
|3|915.50|2016-01-06|Guarda-roupa|0|
|4|949.99|2016-01-10|Smartphone|0|
+----+--------+------------+--------------+----------+
4rowsinset(0,00sec)
Excelente!Agoraonossosistemacadastranossascomprasdamaneiraesperada!
2.11CONSULTASCOMFILTROS
Aprendemosacriarasnossastabelaseinserirregistrosnelas,entãovamosadicionartodasasnossas
compras. Eu já tenho um arquivo com as minhas compras irei executá-lo, não se preocupe, nos
exercíciosforneceremosolinkdoarquivo.Saiadoterminalcomocomandoexit:
mysql>exit
14
2.11CONSULTASCOMFILTROS
Executeocomando:
mysql-uroot-pControleDeGastos<compras.sql
Agora todas as compras foram registradas no nosso banco de dados! Vamos consultar todas as
comprasnovamente:
mysql>SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+
|id|valor|data|observacoes|recebida|
+----+----------+------------+------------------------------+----------+
|1|20.00|2016-01-05|Lanchonete|1|
|2|15.00|2016-01-06|Lanchonete|1|
|3|915.50|2016-01-06|Guarda-roupa|0|
|4|949.99|2016-01-10|Smartphone|0|
|5|200.00|2012-02-19|Materialescolar|1|
|6|3500.00|2012-05-21|Televisao|0|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|
|8|163.45|2012-12-15|Pizzaprafamilia|1|
|9|4780.00|2013-01-23|Saladeestar|1|
|10|392.15|2013-03-03|Quartos|1|
|11|1203.00|2013-03-18|Quartos|1|
|12|402.90|2013-03-21|Copa|1|
|13|54.98|2013-04-12|Lanchonete|0|
|14|12.34|2013-05-23|Lanchonete|0|
|15|78.65|2013-12-04|Lanchonete|0|
|16|12.39|2013-01-06|Sorvetenoparque|0|
|17|98.12|2013-07-09|HopiHari|1|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|
|19|3212.40|2013-11-13|Comprasdomes|1|
|20|223.09|2013-12-17|Comprasdenatal|1|
|21|768.90|2013-01-16|Festa|1|
|22|827.50|2014-01-09|Festa|1|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|
|24|678.43|2014-05-21|PassagempraBahia|1|
|25|10937.12|2014-04-30|CarnavalemCancun|1|
|26|1501.00|2014-06-22|Presentedasogra|0|
|27|1709.00|2014-08-25|Parceladacasa|0|
|28|567.09|2014-09-25|Parceladocarro|0|
|29|631.53|2014-10-12|IPTU|1|
|30|909.11|2014-02-11|IPVA|1|
|31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|
|33|115.90|2014-06-12|Diadosnamorados|0|
|34|98.00|2014-10-12|Diadascrianças|0|
|35|253.70|2014-12-20|Natal-presentes|0|
|36|370.15|2014-12-25|Comprasdenatal|0|
|37|32.09|2015-07-02|Lanchonete|1|
|38|954.12|2015-11-03|ShowdaIveteSangalo|1|
|39|98.70|2015-02-07|Lanchonete|1|
|40|213.50|2015-09-25|Roupas|0|
|41|1245.20|2015-10-17|Roupas|0|
|42|23.78|2015-12-18|LanchonetedoZé|1|
|43|576.12|2015-09-13|Sapatos|1|
|44|12.34|2015-07-19|Canetas|0|
|45|87.43|2015-05-10|Gravata|0|
|46|887.66|2015-02-02|Presenteparaofilhao|1|
+----+----------+------------+------------------------------+----------+
46rowsinset(0,00sec)
Anossaconsultadevolveutodasasnossascompras,maseuquerosaberadatadetodasascompras
2.11CONSULTASCOMFILTROS
15
"baratas" ou no caso, com valor abaixo de 500. Em SQL podemos adicionar filtros com o argumento
WHERE:
mysql>SELECT*FROMcomprasWHEREvalor<500;
+----+--------+------------+------------------------------+----------+
|id|valor|data|observacoes|recebida|
+----+--------+------------+------------------------------+----------+
|1|20.00|2016-01-05|Lanchonete|1|
|2|15.00|2016-01-06|Lanchonete|1|
|5|200.00|2012-02-19|Materialescolar|1|
|8|163.45|2012-12-15|Pizzaprafamilia|1|
|10|392.15|2013-03-03|Quartos|1|
|12|402.90|2013-03-21|Copa|1|
|13|54.98|2013-04-12|Lanchonete|0|
|14|12.34|2013-05-23|Lanchonete|0|
|15|78.65|2013-12-04|Lanchonete|0|
|16|12.39|2013-01-06|Sorvetenoparque|0|
|17|98.12|2013-07-09|HopiHari|1|
|20|223.09|2013-12-17|Comprasdenatal|1|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|
|33|115.90|2014-06-12|Diadosnamorados|0|
|34|98.00|2014-10-12|Diadascrianças|0|
|35|253.70|2014-12-20|Natal-presentes|0|
|36|370.15|2014-12-25|Comprasdenatal|0|
|37|32.09|2015-07-02|Lanchonete|1|
|39|98.70|2015-02-07|Lanchonete|1|
|40|213.50|2015-09-25|Roupas|0|
|42|23.78|2015-12-18|LanchonetedoZé|1|
|44|12.34|2015-07-19|Canetas|0|
|45|87.43|2015-05-10|Gravata|0|
+----+--------+------------+------------------------------+----------+
24rowsinset(0,00sec)
Verificamosascomprasmaisbaratas,maseparaverificarascomprasmaiscaras?Porexemplo,com
valor acima de 1500? Usamos o WHERE novamente indicando que agora queremos valores acima de
1500:
mysql>SELECT*FROMcomprasWHEREvalor>1500;
+----+----------+------------+------------------------+----------+
|id|valor|data|observacoes|recebida|
+----+----------+------------+------------------------+----------+
|6|3500.00|2012-05-21|Televisao|0|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|
|9|4780.00|2013-01-23|Saladeestar|1|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|
|19|3212.40|2013-11-13|Comprasdomes|1|
|25|10937.12|2014-04-30|CarnavalemCancun|1|
|26|1501.00|2014-06-22|Presentedasogra|0|
|27|1709.00|2014-08-25|Parceladacasa|0|
+----+----------+------------+------------------------+----------+
8rowsinset(0,00sec)
Sabemostodasascomprasmaiscaras,porémeusóquerosabertodascomprasmaiscarasequenão
foram entregues ao mesmo tempo. Para isso precisamos adicionar mais um filtro, ou seja, filtrar por
comprasacimade1500etambémquenãoforamentregues:
mysql>SELECT*FROMcomprasWHEREvalor>1500ANDrecebida=0;
+----+---------+------------+-------------------+----------+
16
2.11CONSULTASCOMFILTROS
|id|valor|data|observacoes|recebida|
+----+---------+------------+-------------------+----------+
|6|3500.00|2012-05-21|Televisao|0|
|26|1501.00|2014-06-22|Presentedasogra|0|
|27|1709.00|2014-08-25|Parceladacasa|0|
+----+---------+------------+-------------------+----------+
3rowsinset(0,00sec)
Agoraquepodemosutilizarmaisdeumfiltrovamosverificartodasascomprasmaisbaratas(abaixo
de500)emaiscaras(acimade1500):
mysql>SELECT*FROMcomprasWHEREvalor<500ANDvalor>1500;
Emptyset(0,00sec)
Parecequenão funcionouadicionar maisdeum filtropara amesmacoluna... Vamosanalisar um
pouco a nossa query, será que realmente está fazendo sentido esse nosso filtro? Observe que estamos
tentandopegarcomprasquetenhamovalorabaixode500eaomesmotempotenhaumvaloracimade
1500.Seovalorfor300éabaixode500,porémnãoéacimade1500,seovalorfor1800éacimade1500,
porémnãoéabaixode500,ouseja,éimpossívelqueessefiltrosejaválido.Podemosfazerumpequeno
ajustenessefiltro,podemosindicarquequeremosvaloresquesejammenoresque500oumaioresque
1500:
mysql>SELECT*FROMcomprasWHEREvalor<500ORvalor>1500;
+----+----------+------------+------------------------------+----------+
|id|valor|data|observacoes|recebida|
+----+----------+------------+------------------------------+----------+
|1|20.00|2016-01-05|Lanchonete|1|
|2|15.00|2016-01-06|Lanchonete|1|
|5|200.00|2012-02-19|Materialescolar|1|
|6|3500.00|2012-05-21|Televisao|0|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|
|8|163.45|2012-12-15|Pizzaprafamilia|1|
|9|4780.00|2013-01-23|Saladeestar|1|
|10|392.15|2013-03-03|Quartos|1|
|12|402.90|2013-03-21|Copa|1|
|13|54.98|2013-04-12|Lanchonete|0|
|14|12.34|2013-05-23|Lanchonete|0|
|15|78.65|2013-12-04|Lanchonete|0|
|16|12.39|2013-01-06|Sorvetenoparque|0|
|17|98.12|2013-07-09|HopiHari|1|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|
|19|3212.40|2013-11-13|Comprasdomes|1|
|20|223.09|2013-12-17|Comprasdenatal|1|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|
|25|10937.12|2014-04-30|CarnavalemCancun|1|
|26|1501.00|2014-06-22|Presentedasogra|0|
|27|1709.00|2014-08-25|Parceladacasa|0|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|
|33|115.90|2014-06-12|Diadosnamorados|0|
|34|98.00|2014-10-12|Diadascrianças|0|
|35|253.70|2014-12-20|Natal-presentes|0|
|36|370.15|2014-12-25|Comprasdenatal|0|
|37|32.09|2015-07-02|Lanchonete|1|
|39|98.70|2015-02-07|Lanchonete|1|
|40|213.50|2015-09-25|Roupas|0|
|42|23.78|2015-12-18|LanchonetedoZé|1|
|44|12.34|2015-07-19|Canetas|0|
|45|87.43|2015-05-10|Gravata|0|
2.11CONSULTASCOMFILTROS
17
+----+----------+------------+------------------------------+----------+
32rowsinset(0,00sec)
Perceba que agora temos todas as compras que possuem valores abaixo de 500 e acima de 1500.
Suponhamosquefizemosumacompracomumvalorespecífico,porexemplo3500,equeremossaber
apenasascomprasquetiveramessevalor.Ofiltroquequeremosutilizaréoigualaovalordesejado:
mysql>SELECT*FROMcomprasWHEREvalor=3500;
+----+---------+------------+-------------+----------+
|id|valor|data|observacoes|recebida|
+----+---------+------------+-------------+----------+
|6|3500.00|2012-05-21|Televisao|0|
+----+---------+------------+-------------+----------+
1rowinset(0,00sec)
Conseguimos realizar várias queries com muitos filtros, porém, eu quero saber dentre as compras
realizadas,quaisforamemLanchonete.Entãovamosverificartodasasobservaçõesquesejamiguaisa
Lanchonete:
mysql>SELECT*FROMcomprasWHEREobservacoes='Lanchonete';
+----+-------+------------+-------------+----------+
|id|valor|data|observacoes|recebida|
+----+-------+------------+-------------+----------+
|1|20.00|2016-01-05|Lanchonete|1|
|2|15.00|2016-01-06|Lanchonete|1|
|13|54.98|2013-04-12|Lanchonete|0|
|14|12.34|2013-05-23|Lanchonete|0|
|15|78.65|2013-12-04|Lanchonete|0|
|37|32.09|2015-07-02|Lanchonete|1|
|39|98.70|2015-02-07|Lanchonete|1|
+----+-------+------------+-------------+----------+
7rowsinset(0,00sec)
Agora preciso saber todas as minhas compras que foram Parcelas. Então novamente eu usarei o
mesmofiltro,porémparaParcelas:
mysql>SELECT*FROMcomprasWHEREobservacoes='Parcelas';
Emptyset(0,00sec)
Queestranhoeulembrodeteralgumregistrodeparcela...Sim,existemregistrosdeparcelas,porém
não existe apenas uma observação "Parcela" e sim "Parcela do carro" ou "Parcela da casa", ou seja,
precisamos filtrar as observações verificando se existe um pedaço do texto que queremos na coluna
desejada.ParaverificarumpedaçodotextoutilizamosoargumentoLIKE:
mysql>SELECT*FROMcomprasWHEREobservacoesLIKE'Parcela%';
+----+---------+------------+------------------+----------+
|id|valor|data|observacoes|recebida|
+----+---------+------------+------------------+----------+
|27|1709.00|2014-08-25|Parceladacasa|0|
|28|567.09|2014-09-25|Parceladocarro|0|
+----+---------+------------+------------------+----------+
2rowsinset(0,00sec)
Perceba que utilizamos o "%". Quando adicionamos o "%" durante um filtro utilizando o LIKE
significa que queremos todos os registros que iniciem com Parcela e que tenha qualquer tipo de
18
2.11CONSULTASCOMFILTROS
informação a direita, ou seja, se a observação for "Parcela da casa" ou "Parcela de qualquer coisa" ele
retornaráparanós,massuponhamosquequiséssemossabertodasascomprascomobservaçõesemque
o"de"estivessenomeiodotexto?Bastariaadicionaro"%"tantonoinícioquantonofinal:
mysql>SELECT*FROMcomprasWHEREobservacoesLIKE'%de%';
+----+---------+------------+------------------------------+----------+
|id|valor|data|observacoes|recebida|
+----+---------+------------+------------------------------+----------+
|7|1576.40|2012-04-30|Materialdeconstrucao|1|
|9|4780.00|2013-01-23|Saladeestar|1|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|
|20|223.09|2013-12-17|Comprasdenatal|1|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|
|36|370.15|2014-12-25|Comprasdenatal|0|
+----+---------+------------+------------------------------+----------+
6rowsinset(0,00sec)
Vejaqueagoraforamdevolvidastodasascomprasquepossuemum"DE"nacoluna observacoes
independentedeondeesteja.
2.12MODELANDOTABELAS
Mascomofoiquechegamosnatabeladecomprasmesmo?Começamoscomumaidéiadosdados
que gostaríamos de armazenar: nossas compras. Cada uma delas tem um valor, uma data, uma
observação e se ela já foi recebida ou não. Seguindo essa idéia moldamos uma estrutura que compõe
umacompra,omoldedeumacompra:
Compra
==>temumvalor,comcasasdecimais
==>temumadata
==>temumaobservação,texto
==>podetersidorecebidaounão
Costumamosabreviaromodelocomo:
Compra
-valor
-data
-observacoes
-recebida
Eexistemdiversasmaneirasderepresentartalmodeloatravésdediagramas.Nossofocomaioraqui
seráempensarmosomodeloecomotrabalharmoscomoSQL,porissonãonospreocuparemostanto
comcriarcentenasdediagramas.
Após definirmos os campos importantes para nosso negócio, nossa empresa (o business),
percebemos que era necessário identificar cada compra de maneira única, e para isso definimos uma
chaveprimária,oid.
Tenho um novo cliente em minha empresa de desenvolvimento de software e este cliente é uma
escolaprimária.Elatemalunosbemnovoseprecisamanterumbancocomasinformaçõesdelespara
2.12MODELANDOTABELAS
19
fazerdiversaspesquisas.
Pensandosozinho,nomeutrabalho,tentomodelarumaluno:
Aluno
-id
-nome
-serie
-sala
-email
-telefone
-endereco
Pronto,apresentoomodelodomeubancoparaoclienteeelereclamamuito.Adiretoracomenta
quequandotemquesecomunicararespeitodoalunonãoéotelefoneouemaildelequeelaprecisa.Ela
precisaconversarcomospaiseporissoénecessáriooscamposdecontatodospais.Alteramosentão
nossomodelo:
Aluno
-id
-nome
-serie
-sala
-email
-telefone
-endereco
-nomeDoPai
-nomeDaMae
-telefoneDoPai
-telefoneDaMae
Um outro detalhe que sumiu é a questão da vacinação. A diretora gosta de conversar com os pais
para indicar quando é época de vacinação. Ela sabe da importância do efeito de vacinação em grupo
(todosdevemservacinados,seumnãoé,existechancedeinfectarmuitos).Porissoelasempreenvia
cartas (campo endereco) para os pais (campo nomeDoPai e nomeDaMae). Mas... como saber qual
vacinaeleprecisatomar?Dependedaidade,sugerimosentãoarmazenaradatadenascimentodoaluno:
Aluno
-id
-nascimento
-nome
-serie
-sala
-email
-telefone
-endereco
-nomeDoPai
-nomeDaMae
-telefoneDoPai
-telefoneDaMae
Além disso, ela comenta que este ano o aluno está na terceira série, mas ano que vem estará na
quartasérie!Tantoasériequantoasalasãovaloresquemudamanualmente,eprecisamosatualizá-los
sempre, quase que todos os alunos tem sua série e sala atualizadas de uma vez só (algumas escolas
permitem a troca de salas no meio do ano letivo). Para deixar isso claro, alteramos nossos campos,
20
2.12MODELANDOTABELAS
refletindoquetantoasériequantoasalasãoatuais:
Aluno
-id
-nascimento
-nome
-serieAtual
-salaAtual
-email
-telefone
-endereco
-nomeDoPai
-nomeDaMae
-telefoneDoPai
-telefoneDaMae
Essenossoprocessodecriar,demoldar,demodelarumatabelaéoquechamamosdemodelagemde
dados. Na verdade estamos modelando a estrutura que será capaz de receber os dados. E não existem
regras 100% fixas nesse processo, por isso mesmo é importantíssimo levantarmos as necessidades, os
requisitosdosnossosclientesjuntoaeles.Conversandocomeles,vendootrabalhodelesnodiaadia,
entendemosoqueelesprecisamecomomodelaressasinformaçõesnobanco.Emesmotendomodelado
umavez,podeserquedaquiumanotemosqueevoluirnossomodelo.
2.13RESUMINDO
Nósaprendemosacriarumbancodedados,criartabelascomoasplanilhasdoExceleagoraestamos
aprendendoamanipularinformaçõesfazendofiltrocom WHERE, AND, ORe LIKE.Vamosparaos
exercícios?
2.14EXERCÍCIOS
1. Instale o servidor do MySQL em sua máquina. Qual o sistema operacional em que você fez sua
instalação? Sentiu que algo podia ser simplificado no processo de instalação? Lembre-se que você
poderealizarodownloaddeambosemhttp://MySQL.com/downloads/MySQL.
Vocêpodeoptarporbaixaraversãomaisatual.
1. LoguenoMySQL,ecomececriandoobancodedados:
mysql-uroot-p
CREATEDATABASEControleDeGastos;
USEControleDeGastos;
Agoravamoscriaratabela.Elaprecisaterosseguintescampos: idinteiro, valornúmerocom
vírgula,data,observacoeseumbooleanoparamarcarseacomprafoirecebida.Atabeladeve-se
chamar"compras".
1. Cliqueaquiefaçaodownloaddoarquivo.sql,eimportenoMySQL:
2.13RESUMINDO
21
mysql-uroot-pControleDeGastos<compras.sql
Emseguida,executeoSELECTparagarantirquetodasasinformaçoesforamadicionadas:
SELECT*FROMcompras;
DICA: Salve o arquivo compras.sql em uma pasta de fácil acesso na linha de comando. Além
disso,oarquivodeveestarnomesmolugarondevocêexecutaráocomando.
1. Selecione valor e observacoes de todas as compras cuja data seja maior-ou-igual que
15/12/2012.
2. Qual o comando SQL para juntar duas condições diferentes? Por exemplo, SELECT * FROM
TABELAWHEREcampo>1000campo<5000.Façaotesteevejaoresultado.
3. Vimosquetodotextoépassadoatravésdeaspassimples(').Possopassaraspasduplas(")nolugar?
4. Selecione todas as compras cuja data seja maior-ou-igual que 15/12/2012 e menor do que
15/12/2014.
5. SelecionetodasascomprascujovalorestejaentreR$15,00eR$35,00eaobservaçãocomececoma
palavra'Lanchonete'.
6. Selecionetodasascomprasquejáforamrecebidas.
7. Selecionetodasascomprasqueaindanãoforamrecebidas.
8. VimosqueparaguardarovalorVERDADEIROparaacolunarecebida,devemospassarovalor1.
ParaFALSO,devemospassarovalor0.Equantoaspalavrasjáconhecidasparaverdadeiroefalso:
TRUEeFALSE.Elasfuncionam?Ouseja:
INSERTINTOcompras(valor,data,observacoes,recebida)VALUES(100.0,'2015-09-08','COMIDA',TRUE)
;
Funciona?Façaoteste.
1. Selecionetodasascomprascomvalormaiorque5.000,00ouquejáforamrecebidas.
2. Selecionetodasascomprasqueovalorestejaentre1.000,00e3.000,00ousejamaiorque5.000,00.
22
2.14EXERCÍCIOS
CAPÍTULO3
ATUALIZANDOEEXCLUINDODADOS
Cadastramos todas as nossas compras no banco de dados, porém, no meu rascunho onde eu coloco
todasasminhascomprasalgunsvaloresnãoestãobatendo.Vamosselecionarovaloreaobservaçãode
todasascomprascomvaloresentre1000e2000:
mysql>SELECTvalor,observacoesFROMcompras
WHEREvalor>=1000ANDvalor<=2000;
+---------+------------------------+
|valor|observacoes|
+---------+------------------------+
|1576.40|Materialdeconstrucao|
|1203.00|Quartos|
|1501.00|Presentedasogra|
|1709.00|Parceladacasa|
|1245.20|Roupas|
+---------+------------------------+
5rowsinset(0,00sec)
Fizemosumfiltroparaumintervalodevalor,ouseja,entre1000e2000.EmSQL,quandoqueremos
filtrarumintervalo,podemosutilizarooperadorBETWEEN:
mysql>SELECTvalor,observacoesFROMcompras
WHEREvalorBETWEEN1000AND2000;
+---------+------------------------+
|valor|observacoes|
+---------+------------------------+
|1576.40|Materialdeconstrucao|
|1203.00|Quartos|
|1501.00|Presentedasogra|
|1709.00|Parceladacasa|
|1245.20|Roupas|
+---------+------------------------+
5rowsinset(0,00sec)
Oresultadoéomesmoqueanossaqueryanterior,porémagoraestámaisclaraeintuitura.
Nomeurascunhoinformaqueacomprafoinoanode2013,porémnãofoifeitoessefiltro.Então
vamosadicionarmaisumfiltropegandoointervalode01/01/2013e31/12/2013:
mysql>SELECTvalor,observacoesFROMcompras
WHEREvalorBETWEEN1000AND2000
ANDdataBETWEEN'2013-01-01'AND'2013-12-31';
+---------+-------------+
|valor|observacoes|
+---------+-------------+
|1203.00|Quartos|
+---------+-------------+
1rowinset(0,00sec)
3ATUALIZANDOEEXCLUINDODADOS
23
3.1UTILIZANDOOUPDATE
Encontreiacompraquefoifeitacomovalorerrado,nomeurascunhoacompradeQuartosovalor
éde1500.Entãoagoraprecisamosalteraressevalornanossatabela.Paraalterar/atualizarvaloresem
SQLutilizamosainstruçãoUPDATE:
UPDATEcompras
AgoraprecisamosadicionaroquequeremosalterarpormeiodainstruçãoSET:
UPDATEcomprasSETvalor=1500;
Precisamos sempre tomar cuidado com a instrução UPDATE , pois o exemplo acima executa
normalmente,porémoqueelevaiatualizar?Nãoinformamosemmomentoalgumqualcompraprecisa
seratualizar,ouseja,eleiriaatualizarTODASascomprasenãoéissoquequeremos.Antesdeexecutar
oUPDATE,precisamosverificaroiddacompraquequeremosalterar:
mysql>SELECTid,valor,observacoesFROMcompras
WHEREvalorBETWEEN1000AND2000
ANDdataBETWEEN'2013-01-01'AND'2013-12-31';
+----+---------+-------------+
|id|valor|observacoes|
+----+---------+-------------+
|11|1203.00|Quartos|
+----+---------+-------------+
1rowinset(0,00sec)
Agoraquesabemosqualéoiddacompra,bastainformá-lopormeiodainstruçãoWHERE:
mysql>UPDATEcomprasSETvalor=1500WHEREid=11;
QueryOK,1rowaffected(0,01sec)
Rowsmatched:1Changed:1Warnings:0
Vamosverificarseovalorfoiatualizado:
mysql>SELECTvalor,observacoesFROMcompras
WHEREvalorBETWEEN1000AND2000
ANDdataBETWEEN'2013-01-01'AND'2013-12-31';
+---------+-------------+
|valor|observacoes|
+---------+-------------+
|1500.00|Quartos|
+---------+-------------+
1rowinset(0,00sec)
Analisando melhor essa compra eu sei que se refere aos móveis do quarto, mas uma observação
como"Quartos"quedizeroque?Nãoéclaraosuficiente,podeserquedaquia6meses,1anooumais
tempoeunemsaibamaisoqueserefereessaobservaçãoevouacabarpensando:"Seráqueeucomprei
móveisoucompreiumquartotodo?".Umtantoconfuso...Entãovamosalterartambémasobservaçõese
deixaralgomaisclaro,comoporexemplo:"Reformadequartos".
UPDATEcompraSETobservacoes='Reformadequartos'WHEREid=11;
Verificandoanovaobservação:
24
3.1UTILIZANDOOUPDATE
mysql>SELECTvalor,observacoesFROMcompras
->WHEREvalorBETWEEN1000AND2000
->ANDdataBETWEEN'2013-01-01'AND'2013-12-31';
+---------+--------------------+
|valor|observacoes|
+---------+--------------------+
|1500.00|Reformadequartos|
+---------+--------------------+
1rowinset(0,00sec)
3.2ATUALIZANDOVÁRIASCOLUNASAOMESMOTEMPO
AssimcomousamosoUPDATEparaatualizarumaúnicacoluna,poderíamosatualizardoisoumais
valores,separandoosmesmoscomvírgula.Porexemplo,sedesejasseatualizarovaloreasobservações:
mysql>UPDATEcomprasSETvalor=1500,observacoes='Reformadequartoscara'
->WHEREid=11;
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
Eoresultado:
mysql>SELECTvalor,observacoesFROMcomprasWHEREid=11;
+---------+-------------------------+
|valor|observacoes|
+---------+-------------------------+
|1500.00|Reformadequartoscara|
+---------+-------------------------+
1rowinset(0,00sec)
Muitocuidadoaqui!DiferentementedoWHERE,nocasodo SETnãoutilizeooperador ANDpara
atribuirvalores,comonoexemploaseguir,queNÃOéoquequeremos:
errado:UPDATEcomprasSETvalor=1500ANDobservacoes='Reformadequartosnovos'WHEREid=8;
certo:UPDATEcomprasSETvalor=1500,observacoes='Reformadequartosnovos'WHEREid=8;
3.3 UTILIZANDO UMA COLUNA COMO REFERÊNCIA PARA OUTRA
COLUNA
Esqueci de adicionar 10% de imposto que paguei na compra de id 11... portanto quero fazer algo
como:
mysql>SELECTvalorFROMcomprasWHEREid=11;
+---------+
|valor|
+---------+
|1500.00|
+---------+
1rowinset(0,00sec)
Descobriovaloratual,agoramultiplicopor 1.1paraaumentar 10%: 1500 * 1.1 são 1650
reais.Entãoatualizoagoramanualmente:
UPDATEcomprasSETvalor=1650ANDobservacoes='Reformadequartosnovos'WHEREid=11;
3.2ATUALIZANDOVÁRIASCOLUNASAOMESMOTEMPO
25
Maseseeuquisessefazeromesmoparadiversaslinhas?
mysql>SELECTvalorFROMcomprasWHEREid>11ANDid<=14;
+--------+
|valor|
+--------+
|402.90|
|54.98|
|12.34|
+--------+
3rowsinset(0,00sec)
UPDATE....eagora???
E agora? Vou ter que calcular na mão cada um dos casos, com 10% a mais, e fazer uma linha de
UPDATEparacadaum?AsoluçãoéfazerumúnicoUPDATEemquedigoquequeroalterarovalorpara
ovalordelemesmo,vezesosmeus1.1quejáqueriaantes:
UPDATEcomprasSETvalor=valor*1.1
WHEREid>=11ANDid<=14;
Nessaqueryficaclaroqueeupossousarovalordeumcampo(qualquer)paraatualizarumcampo
damesmalinha.Nonossocasousamosovalororiginalparacalcularonovovalor.Masestoulivrepara
usaroutroscamposdamesmalinha,desdequefaçasentidoparaomeuproblema,claro.Porexemplo,se
eutenhoumatabelade produtoscomoscampos precoLiquidoeupossoatualizaro precoBruto
quandooimpostomudarpara15%:
UPDATEprodutosSETprecoBruto=precoLiquido*1.15;
3.4UTILIZANDOODELETE
Observei o meu rascunho e percebi que essa compra eu não devia ter sido cadastrada na minha
tabela de compras, ou seja, eu preciso excluir esse registro do meu banco de dados. Em SQL, quando
queremosexcluiralgumregistro,utilizamosainstruçãoDELETE:
DELETEFROMcompras;
ODELETEtemocomportamentosimilarao UPDATE,ouseja,precisamossempretomarcuidado
quando queremos excluir algum registro da tabela. Da mesma forma que fizemos com o UPDATE,
precisamos adicionar a instrução WHERE para informa o que queremos excluir, justamente para
evitamosaexclusãodetodososdados:
mysql>DELETEFROMcomprasWHEREid=11;
QueryOK,1rowaffected(0,01sec)
Severificarmosnovamenteseexisteoregistrodessacompra:
mysql>SELECTvalor,observacoesFROMcompras
WHEREvalorBETWEEN1000AND2000
ANDdataBETWEEN'2013-01-01'AND'2013-12-31';
Emptyset(0,00sec)
26
3.4UTILIZANDOODELETE
Acomprafoiexcluídaconformeoesperado!
3.5CUIDADOSCOMODELETEEUPDATE
VimoscomooDELETEeUPDATEpodemserperigososembancodedados,senãodefinirmosuma
condição para cada uma dessas instruções podemos excluir/alterar TODAS as informações da nossa
tabela.Aboapráticaparaexecutaressasinstruçõesésempreescreverainstrução WHEREantes,ouseja,
definirprimeiroqualseráacondiçãoparaexecutaressasinstruções:
WHEREid=11;
EntãoadicionamosoDELETE/UPDATE:
DELETEFROMcomprasWHEREid=11;
UPDATEcomprasSETvalor=1500WHEREid=11;
Dessaformagarantimosqueonossobancodedadosnãotenharisco.
3.6RESUMINDO
NessecapítuloaprendemoscomofazerqueriespormeiodeintervalosutilizandooBETWEENecomo
alterar/excluirosdadosdanossatabelautilizandooDELETEeoUPDATE.Vamosparaosexercícios?
EXERCÍCIOS
1. Altereascompras,colocandoaobservação'preparandoonatal'paratodasasqueforamefetuadasno
dia20/12/2014.
2. AltereoVALORdascomprasfeitasantesde01/06/2013.SomeR$10,00aovaloratual.
3. Atualizetodasascomprasfeitasentre01/07/2013e01/07/2014paraqueelastenhamaobservação
'entregueantesde2014'eacolunarecebidacomovalorTRUE.
4. EmumcomandoWHEREépossívelespecificarumintervalodevalores.Paratanto,éprecisodizer
qualovalormínimoeovalormáximoquedefineointervalo.Qualéooperadorqueéusadopara
isso?
5. Qualoperadorvocêusapararemoverlinhasdecomprasdesuatabela?
6. Excluaascomprasrealizadasentre05e20marçode2013.
7. Existe um operador lógico chamado NOT. Esse operador pode ser usado para negar qualquer
condição.Porexemplo,paraselecionarqualquerregistrocomdatadiferentede03/11/2014,podeser
construídooseguinteWHERE:
3.5CUIDADOSCOMODELETEEUPDATE
27
WHERENOTDATA='2011-11-03'
UseooperadorNOTemonteumSELECTqueretornatodasascomprascomvalordiferentedeR$
108,00.
28
3.6RESUMINDO
CAPÍTULO4
ALTERANDOERESTRINGINDOO
FORMATODENOSSASTABELAS
Muitasvezesqueinserimosdadosemumbancodedados,precisamossaberquaissãoostiposdedados
de cada coluna da tabela que queremos popular. Vamos dar uma olhada novamente na estrutura da
nossatabelapormeiodainstruçãoDESC:
mysql>DESCcompras;
+-------------+---------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------------+---------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|valor|decimal(18,2)|YES||NULL||
|data|date|YES||NULL||
|observacoes|varchar(255)|YES||NULL||
|recebida|tinyint(4)|YES||NULL||
+-------------+---------------+------+-----+---------+----------------+
5rowsinset(0,00sec)
Como já vimos, o MySQL demonstra algumas características das colunas da nossa tabela. Vamos
verificaracolunaNull,oqueseráqueelasignifica?Seráqueestádizendoquenossascolunasaceitam
valoresvazios?Entãovamostentarinserirumacompracom observacoesvazia,ouseja,nula,como
valornull:
INSERTINTOcompras(valor,data,recebida,observacoes)
VALUES(150,'2016-01-04',1,NULL);
QueryOK,1rowaffected(0,01sec)
Vamosverificaroresultado:
mysql>SELECT*FROMcomprasWHEREdata='2016-01-04';
+----+--------+------------+-------------+----------+
|id|valor|data|observacoes|recebida|
+----+--------+------------+-------------+----------+
|47|150.00|2016-01-04|NULL|1|
+----+--------+------------+-------------+----------+
1rowinset(0,00sec)
Onossobancodedadospermitiuoregistrodeumacomprasemobservação.Masemqualmomento
informamosaoMySQLquequeriámosvaloresnulos?Emnenhummomento!Porém,quandocriamos
uma tabela no MySQL e não informamos se queremos ou não valores nulos para uma determinada
coluna,porpadrão,oMySQLaceitaosvaloresnulos.
Notequeumtextovazioédiferentedenãoternada!Édiferentedeumtextovaziocomo''.Nuloé
4ALTERANDOERESTRINGINDOOFORMATODENOSSASTABELAS
29
nada,éainexistênciadeumvalor.Umtextosemcaractereséumtextocomzerocaracteres.Umvalor
nulo nem texto é, nem nada é. Nulo é o não ser. Uma questão filosófica milenar, discutida entre os
melhoresfilósofoseprogramadores,alémdeserfontedemuitosbugs.Cuidado.
4.1RESTRINGINDOOSNULOS
Para resolver esse problema podemos criar restrições, Constraints, que tem a capacidade de
determinarasregrasqueascolunasdenossastabelasterão.AntesdeconfiguraroConstraints,vamos
verificartodososregistrosquetiveremobservaçõesnulasevamosapagá-los.Queremosselecionartodas
asobservaçõesquesãonulas,sãonulas,SÃONULAS,ISNULL:
mysql>SELECT*FROMcomprasWHEREobservacoesISNULL;
+----+--------+------------+-------------+----------+
|id|valor|data|observacoes|recebida|
+----+--------+------------+-------------+----------+
|47|150.00|2016-01-04|NULL|1|
+----+--------+------------+-------------+----------+
1rowinset(0,00sec)
Vamosexcluirtodasascomprasquetenhamasobservaçõesnulas:
DELETEFROMcomprasWHEREobservacoesISNULL;
QueryOK,1rowaffected(0,01sec)
4.2ADICIONANDOCONSTRAINTS
PodemosdefinirConstraintsnomomentodacriaçãodatabela,comonocasodedefinirquenosso
valoredatanãopodemsernulos(NOTNULL):
CREATETABLEcompras(
idINTAUTO_INCREMENTPRIMARYKEY,
valorDECIMAL(18,2)NOTNULL,
dataDATENOTNULL,
...
Porém,atabelajáexiste!Enãoéumaboapráticaexcluiratabelaecria-lanovamente,poispodemos
perder registros! Para fazer alterações na estrutura da tabela, podemos utilizar a instrução ALTER
TABLEquevimosantes:
ALTERTABLEcompras;
Precisamosespecificaroquequeremosfazernatabela,nessecasomodificarumacolunaeadicionar
umaConstraints:
mysql>ALTERTABLEcomprasMODIFYCOLUMNobservacoesVARCHAR(255)NOTNULL;
QueryOK,45rowsaffected(0,04sec)
Records:45Duplicates:0Warnings:0
Observequeforamalteradastodasas45linhasexistentesnonossobancodedados.Severificarmosa
estruturadanossatabelanovamente:
30
4.1RESTRINGINDOOSNULOS
mysql>DESCcompras;
+-------------+---------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------------+---------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|valor|decimal(18,2)|YES||NULL||
|data|date|YES||NULL||
|observacoes|varchar(255)|NO||NULL||
|recebida|tinyint(4)|YES||NULL||
+-------------+---------------+------+-----+---------+----------------+
5rowsinset(0,01sec)
Na coluna Null e na linha das observacoes está informando que não é mais permitido valores
nulos.Vamostestar:
mysql>INSERTINTOcompras(valor,data,recebida,observacoes)
VALUES(150,'2016-01-04',1,NULL);
ERROR1048(23000):Column'observacoes'cannotbenull
4.3VALORESDEFAULT
Vamossuporqueamaioriadascomprasqueregistramosnãosãoentreguesequequeremosqueo
próprioMySQLentendaque,quandoeunãoinformaracolunarecebida,elasejapopuladacomovalor
0. No MySQL, além de Constraints, podemos adicionar valores padrões, no inglês Default, em uma
colunautilizandoainstruçãoDEFAULT:
mysql>ALTERTABLEcomprasMODIFYCOLUMNrecebidatinyint(1)DEFAULT0;
QueryOK,0rowsaffected(0,00sec)
Records:0Duplicates:0Warnings:0
Inserindoumacompranovaseminformaacolunarecebida:
INSERTINTOcompras(valor,data,observacoes)
VALUES(150,'2016-01-05','Compradeteste');
QueryOK,1rowaffected(0,01sec)
Verificandoovalorinseridonacolunarecebida:
mysql>SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+
|id|valor|data|observacoes|recebida|
+----+----------+------------+------------------------------+----------+
|1|20.00|2016-01-05|Lanchonete|1|
|2|15.00|2016-01-06|Lanchonete|1|
|3|915.50|2016-01-06|Guarda-roupa|0|
|...|...|...|...|...|
|48|150.00|2016-01-05|Compradeteste|0|
+----+----------+------------+------------------------------+----------+
46rowsinset(0,00sec)
Sequisemosinformarqueovalorpadrãodeveriaserentregue,bastariautilizaromesmocomando,
porémmodificandoo0para1.
4.4EVOLUÇÃODOBANCO
4.3VALORESDEFAULT
31
Nossa escola deseja agora entrar em contato por email com todos os pais de alunos de um
determinado bairro devido a maior taxa de ocorrência de dengue. Como fazer isso se o campo de
endereçonãoobrigavaobairro?Podemosapartirdeagoraadicionarumnovocampo:
Aluno
-id
-nascimento
-nome
-serieAtual
-salaAtual
-email
-telefone
-bairro
-endereco
-nomeDoPai
-nomeDaMae
-telefoneDoPai
-telefoneDaMae
Alémdisso,depois deum anodescobrimosum bugno sistema.Algunsalunos sairamdo mesmo,
comorepresentarquenãoexistemaissérieatualenemsalaatualparaessesalunos?Podemosescolher
trêsabordagens:
1. marcaroscamposserieAtualesalaAtualcomoNULL
2. marcaroscamposcomoNULLecolocarumcampoativocomo0ou1
3. colocarumcampoativocomo0ou1
Reparequeoprimeiroeoúltimoparecemsersimplesdeimplementar,claramentemaissimplesque
implementar os dois ao mesmo tempo (item 2). Mas cada um deles implica em um problema de
modelagemdiferente.
Seassumimos(1)ecolocamoscamposserieAtualesalaAtualcomoNULL,todavezquequeremos
saberquemestáinativotemosumaquerybemestranha,quenãoparecefazerisso:
select*fromAlunoswhereserieAtualisnullandsalaAtualisnull
Sériomesmo?Issosignificaqueoalunoestáinativo?Doisnulos?Alémdisso,seeupermitonulos,eu
possoterumcasomuitoestranhocomooalunoaseguir:
GuilhermeSilveira,seriaAtual8,salaAtualnull
Como assim? É um aluno novo que alguém/o sistema esqueceu de colocar a sala? Ou é um aluno
antigoquealguém/osistemaesqueceuderemoverasérie?
Poroutrolado,naterceiraabordagem,adocampoativo,solto,geraumpossívelalunocomo:
GuilhermeSilveira,serieAtual8,salaAtualB,ativo0
Comoassim,oGuilhermeestáinativomasestána8Baomesmotempo?Oueleestána8Bounão
está,nãotemasduascoisasaomesmotempo.Modeloestranho.
32
4.4EVOLUÇÃODOBANCO
Por fim, a abordagem de modelagem 2, que utiliza o novo campo e o valor nulo, garante que as
queriessejamsimples:
select*fromAlunoswhereativo=false;
Masaindapermitecasosestranhoscomo:
GuilhermeSilveira,serieAtual8,salaAtualB,ativo0
GuilhermeSilveira,serieAtualNULL,salaAtualB,ativo0
GuilhermeSilveira,serieAtualNULL,salaAtualNULL,ativo1
Poderíamosusarrecursosbemmaisavançadosparatentarnosprotegerdealgunsdessescasos,mas
essesrecursossão,emgeral,específicosdeumbancodeterminado.Porexemplo,funcionamnoOracle
masnãonoMySQL.OunoMySQLmasnãonoPostgreSQL.NessecursofocamosempadrõesdoSQL
queconseguimosaplicar,emgeral,paratodoseles.
E agora? Três modelagens diferentes, todas resolvem o problema e todas geram outros problemas
técnicos.Modelarbancosétomardecisõesqueapresentamvantagensedesvantagens.Nãoháregraem
qual das três soluções devemos escolher, escolha a que você julgar melhor e mais justa para sua
empresa/sistema/ocasião. O mais importante é tentar prever os possíveis pontos de erro como os
mencionadosaqui.Conhecerasfalhasemnossodesenhodemodelagem(design)deantemãopermite
que não tenhamos uma surpresa desagradável quando descobrirmos elas de uma maneira infeliz no
futuro.
4.5RESUMINDO
Vimosquealémdecriarastabelasemnossobancodedadosprecisamosverificarsealgumacoluna
pode receber valores nulos ou não, podemos determinar essas regras por meio de Constraints no
momento da criação da tabela ou utilizando a instrução ALTER TABLE se caso a tabela exista e
queremos modificar sua estrutura. Também vimos que em alguns casos os valores de cada INSERT
podeserepetirmuitoeporissopodemosdefinirvalorespadrõescomainstruçãoDEFAULT.
EXERCÍCIOS
1. Configureovalorpadrãoparaacolunarecebida.
2. Configureacolunaobservacoesparanãoaceitarvaloresnulos.
3. Nonossomodeloatual,qualcampovocêdeixariacomvalores DEFAULTequaisnão?Justifiquesua
decisão. Note que como estamos falando de modelagem, não existe uma regra absoluta, existe
vantagensedesvantagensnadecisãoquetomar,tentecitá-las.
4. NOTNULLe DEFAULT podem ser usados também no CREATE TABLE? Crie uma tabela nova e
adicioneConstraintsevaloresDAFAULT.
4.5RESUMINDO
33
5. Reescreva o CREATE TABLE do começo do curso, marcando observacoes como nulo e valor
padrãodorecebidacomo1.
34
4.5RESUMINDO
CAPÍTULO5
AGRUPANDODADOSEFAZENDO
CONSULTASMAISINTELIGENTES
Jáadicionamosmuitosdadosemnossobancodedadoseseriamaisinteressantefazermosqueriesmais
robustas,comoporexemplo,saberototalquejágastei.OMySQLforneceafunção SUM()quesoma
todosdosvaloresdeumacoluna:
mysql>SELECTSUM(valor)FROMcompras;
+------------+
|SUM(valor)|
+------------+
|43967.91|
+------------+
1rowinset(0,00sec)
Vamosverificarototaldetodasascomprasrecebidas:
mysql>SELECTSUM(valor)FROMcomprasWHERErecebida=1;
+------------+
|SUM(valor)|
+------------+
|31686.75|
+------------+
1rowinset(0,00sec)
Agoratodasascomprasquenãoforamrecebidas:
mysql>SELECTSUM(valor)FROMcomprasWHERErecebida=0;
+------------+
|SUM(valor)|
+------------+
|12281.16|
+------------+
1rowinset(0,00sec)
Podemostambém,contarquantascomprasforamrecebidaspormeiodafunçãoCOUNT():
SELECTCOUNT(*)FROMcomprasWHERErecebida=1;
+----------+
|COUNT(*)|
+----------+
|26|
+----------+
Agoravamosfazercomquesejaretornadoasomadetodasascomprasrecebidasenãorecebidas,
porémretornaremosacolunarecebida,ouseja,emumalinhaestaráascomprasrecebidaseasuasomae
emoutraasnãorecebidasesuasoma:
5AGRUPANDODADOSEFAZENDOCONSULTASMAISINTELIGENTES
35
mysql>SELECTrecebida,SUM(valor)FROMcompras;
+----------+------------+
|recebida|SUM(valor)|
+----------+------------+
|1|43967.91|
+----------+------------+
1rowinset(0,00sec)
Observe que o resultado não saiu conforme o esperado... Mas por que será que isso aconteceu?
Quandoutilizamosafunção SUM()doMySQLelasomatodososvaloresdacolunaeretornaapenas
umaúnicalinha,poiséumafunçãodeagregração!Pararesolvermosesseproblema,podemosutilizara
instrução GROUP BY que indica como a soma precisa ser agrupada, ou seja, some todas as compras
recebidaseagrupeemumalinha,sometodasascomprasnãorecebidaseagrupeemoutralinha:
mysql>SELECTrecebida,SUM(valor)FROMcomprasGROUPBYrecebida;
+----------+------------+
|recebida|SUM(valor)|
+----------+------------+
|0|12281.16|
|1|31686.75|
+----------+------------+
2rowsinset(0,00sec)
Oresultadofoiconformeoesperado,porémnotequeonomedacolunaparaasomaestáumpouco
estranho,poisestamosaplicandoumafunçãoaoinvésderetornarumacoluna,seriamelhorseonome
retornadofosseapenas"soma".PodemosnomearascolunaspormeiodainstruçãoAS:
mysql>SELECTrecebida,SUM(valor)ASsomaFROMcompras
GROUPBYrecebida;
+----------+----------+
|recebida|soma|
+----------+----------+
|0|12281.16|
|1|31686.75|
+----------+----------+
2rowsinset(0,00sec)
Tambémpodemosaplicarfiltrosemqueriesqueutilizamfunçõesdeagregação:
mysql>SELECTrecebida,SUM(valor)ASsomaFROMcompras
WHEREvalor<1000
GROUPBYrecebida;
+----------+---------+
|recebida|soma|
+----------+---------+
|0|4325.96|
|1|8682.83|
+----------+---------+
2rowsinset(0,00sec)
Suponhamos uma query mais robusta, onde podemos verificar em qual mês e ano a compra foi
entregueounãoeovalordasoma.PodemosretornarainformaçãodeanoutilizandoafunçãoYEAR()
eainformaçãodemêsutilizandoafunçãoMONTH():
mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,
SUM(valor)ASsomaFROMcompras
GROUPBYrecebida;
36
5AGRUPANDODADOSEFAZENDOCONSULTASMAISINTELIGENTES
+------+------+----------+----------+
|mes|ano|recebida|soma|
+------+------+----------+----------+
|1|2016|0|12281.16|
|1|2016|1|31686.75|
+------+------+----------+----------+
2rowsinset(0,00sec)
Lembre-sequeestamoslidandocomumafunçãodeagregação!Porissoprecisamosinformartodas
ascolunasquequeremosagrupar,ouseja,acolunademêsedeano:
mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,
SUM(valor)ASsomaFROMcompras
GROUPBYrecebida,mes,ano;
+------+------+----------+----------+
|mes|ano|recebida|soma|
+------+------+----------+----------+
|1|2013|0|12.39|
|1|2016|0|2015.49|
|4|2013|0|54.98|
|4|2014|0|434.00|
|5|2012|0|3500.00|
|5|2013|0|12.34|
|5|2015|0|87.43|
|6|2014|0|1616.90|
|7|2015|0|12.34|
|8|2014|0|1709.00|
|9|2014|0|567.09|
|9|2015|0|213.50|
|10|2014|0|98.00|
|10|2015|0|1245.20|
|12|2013|0|78.65|
|12|2014|0|623.85|
|1|2013|1|8046.90|
|1|2014|1|827.50|
|1|2016|1|35.00|
|2|2012|1|200.00|
|2|2014|1|921.11|
|2|2015|1|986.36|
|3|2013|1|795.05|
|4|2012|1|1576.40|
|4|2014|1|11705.30|
|5|2014|1|678.43|
|7|2013|1|98.12|
|7|2015|1|32.09|
|9|2015|1|576.12|
|10|2014|1|631.53|
|11|2013|1|3212.40|
|11|2015|1|954.12|
|12|2012|1|163.45|
|12|2013|1|223.09|
|12|2015|1|23.78|
+------+------+----------+----------+
35rowsinset(0,00sec)
5.1ORDENANDOOSRESULTADOS
Conseguimos criar uma query bem robusta, mas perceba que as informações estão bem
desordenadas,aprimeiravistaédifícildeverasomadomêsdejaneiroemtodososanos,comotambém
5.1ORDENANDOOSRESULTADOS
37
a soma de todos os meses em um único ano. Para podermos ordernar as informações das colunas,
podemos utilizar a instrução ORDER BY passando as colunas que queremos que sejam ordenadas.
Vamosordenarpormêsprimeiro:
mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,
SUM(valor)ASsomaFROMcompras
GROUPBYrecebida,mes,ano
ORDERBYmes;
+------+------+----------+----------+
|mes|ano|recebida|soma|
+------+------+----------+----------+
|1|2013|1|8046.90|
|1|2014|1|827.50|
|1|2016|1|35.00|
|1|2016|0|2015.49|
|1|2013|0|12.39|
|2|2014|1|921.11|
|2|2012|1|200.00|
|2|2015|1|986.36|
|3|2013|1|795.05|
|4|2014|0|434.00|
|4|2013|0|54.98|
|4|2014|1|11705.30|
|4|2012|1|1576.40|
|5|2013|0|12.34|
|5|2014|1|678.43|
|5|2015|0|87.43|
|5|2012|0|3500.00|
|6|2014|0|1616.90|
|7|2015|0|12.34|
|7|2015|1|32.09|
|7|2013|1|98.12|
|8|2014|0|1709.00|
|9|2014|0|567.09|
|9|2015|0|213.50|
|9|2015|1|576.12|
|10|2014|1|631.53|
|10|2015|0|1245.20|
|10|2014|0|98.00|
|11|2013|1|3212.40|
|11|2015|1|954.12|
|12|2012|1|163.45|
|12|2013|1|223.09|
|12|2015|1|23.78|
|12|2014|0|623.85|
|12|2013|0|78.65|
+------+------+----------+----------+
35rowsinset(0,01sec)
Jáestámelhor!Masagoravamosordernarpormêseporano:
mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,
SUM(valor)ASsomaFROMcompras
GROUPBYrecebida,mes,ano
ORDERBYmes,ano;
+------+------+----------+----------+
|mes|ano|recebida|soma|
+------+------+----------+----------+
|1|2013|1|8046.90|
|1|2013|0|12.39|
|1|2014|1|827.50|
38
5.1ORDENANDOOSRESULTADOS
|1|2016|1|35.00|
|1|2016|0|2015.49|
|2|2012|1|200.00|
|2|2014|1|921.11|
|2|2015|1|986.36|
|3|2013|1|795.05|
|4|2012|1|1576.40|
|4|2013|0|54.98|
|4|2014|0|434.00|
|4|2014|1|11705.30|
|5|2012|0|3500.00|
|5|2013|0|12.34|
|5|2014|1|678.43|
|5|2015|0|87.43|
|6|2014|0|1616.90|
|7|2013|1|98.12|
|7|2015|0|12.34|
|7|2015|1|32.09|
|8|2014|0|1709.00|
|9|2014|0|567.09|
|9|2015|0|213.50|
|9|2015|1|576.12|
|10|2014|1|631.53|
|10|2014|0|98.00|
|10|2015|0|1245.20|
|11|2013|1|3212.40|
|11|2015|1|954.12|
|12|2012|1|163.45|
|12|2013|1|223.09|
|12|2013|0|78.65|
|12|2014|0|623.85|
|12|2015|1|23.78|
+------+------+----------+----------+
35rowsinset(0,00sec)
Ficoumaisfácildevisualizar,sóqueaindanãoconseguimosverificardeumaformaclaraasomade
cadamêsduranteumano.Percebaqueainstrução ORDERBYpriorizaascolunaspelaordememque
sãoinformadadas,ouseja,quandofizemos:
ORDERBYmes,ano;
Informamosquequeremosquedêprioridadeàordenaçãodacolunamêseasdemaiscolunassejam
ordenadasdeacordocomomês!Issosignificaqueparaordenarmosoanoedepoisomêsbastaapenas
colocaroanonoiníciodoORDERBY:
mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,
SUM(valor)ASsomaFROMcompras
GROUPBYrecebida,mes,ano
ORDERBYano,mes;
+------+------+----------+----------+
|mes|ano|recebida|soma|
+------+------+----------+----------+
|2|2012|1|200.00|
|4|2012|1|1576.40|
|5|2012|0|3500.00|
|12|2012|1|163.45|
|1|2013|1|8046.90|
|1|2013|0|12.39|
|3|2013|1|795.05|
|4|2013|0|54.98|
5.1ORDENANDOOSRESULTADOS
39
|5|2013|0|12.34|
|7|2013|1|98.12|
|11|2013|1|3212.40|
|12|2013|1|223.09|
|12|2013|0|78.65|
|1|2014|1|827.50|
|2|2014|1|921.11|
|4|2014|0|434.00|
|4|2014|1|11705.30|
|5|2014|1|678.43|
|6|2014|0|1616.90|
|8|2014|0|1709.00|
|9|2014|0|567.09|
|10|2014|1|631.53|
|10|2014|0|98.00|
|12|2014|0|623.85|
|2|2015|1|986.36|
|5|2015|0|87.43|
|7|2015|0|12.34|
|7|2015|1|32.09|
|9|2015|0|213.50|
|9|2015|1|576.12|
|10|2015|0|1245.20|
|11|2015|1|954.12|
|12|2015|1|23.78|
|1|2016|1|35.00|
|1|2016|0|2015.49|
+------+------+----------+----------+
35rowsinset(0,00sec)
Agora conseguimos verificar de uma forma clara a soma dos meses em cada ano. Além da soma
podemos também utilizar outras funções de agragação como por exemplo, a AVG() que retorna a
médiadeumacoluna:
mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,
AVG(valor)ASsomaFROMcompras
GROUPBYrecebida,mes,ano
ORDERBYano,mes;
+------+------+----------+-------------+
|mes|ano|recebida|soma|
+------+------+----------+-------------+
|2|2012|1|200.000000|
|4|2012|1|1576.400000|
|5|2012|0|3500.000000|
|12|2012|1|163.450000|
|1|2013|0|12.390000|
|1|2013|1|2682.300000|
|3|2013|1|397.525000|
|4|2013|0|54.980000|
|5|2013|0|12.340000|
|7|2013|1|98.120000|
|11|2013|1|3212.400000|
|12|2013|1|223.090000|
|12|2013|0|78.650000|
|1|2014|1|827.500000|
|2|2014|1|460.555000|
|4|2014|0|434.000000|
|4|2014|1|5852.650000|
|5|2014|1|678.430000|
|6|2014|0|808.450000|
|8|2014|0|1709.000000|
40
5.1ORDENANDOOSRESULTADOS
|9|2014|0|567.090000|
|10|2014|1|631.530000|
|10|2014|0|98.000000|
|12|2014|0|311.925000|
|2|2015|1|493.180000|
|5|2015|0|87.430000|
|7|2015|1|32.090000|
|7|2015|0|12.340000|
|9|2015|1|576.120000|
|9|2015|0|213.500000|
|10|2015|0|1245.200000|
|11|2015|1|954.120000|
|12|2015|1|23.780000|
|1|2016|1|17.500000|
|1|2016|0|671.830000|
+------+------+----------+-------------+
35rowsinset(0,00sec)
5.2RESUMINDO
Vimos como podemos fazer queries mais robustas e inteligentes utilizando funções de agregação,
comoporexemplo,a SUM()parasomarea AVG() para tirar a média. Vimos também que quando
queremos retornar outras colunas ao utilizar funções de agregação, precisamos utilizar a instrução
GROUPBY para determinar quais serão as colunas que queremos que seja feita o agrupamento e que
nem sempre o resultado vem organizado e por isso, em determinados casos, precisamos utilizar a
instruçãoORDERBYparaordenaranossaquerypormeiodeumacoluna.
EXERCÍCIOS
1. Calculeamédiadetodasascomprascomdatasinferioresa12/05/2013.
2. Calculeaquantidadedecomprascomdatasinferioresa12/05/2013equejáforamrecebidas.
3. Calculeasomadetodasascompras,agrupadasseacomprarecebidaounão.
5.2RESUMINDO
41
CAPÍTULO6
JUNTANDODADOSDEVÁRIASTABELAS
Anossatabeladecomprasestábempopulada,commuitasinformaçõesdascomprasrealizadas,porém
está faltando uma informação muito importante, que são os compradores. Por vezes foi eu quem
comprou algo, mas também o meu irmão pode ter comprado ou até mesmo o meu primo... Como
podemos fazer para identificar o comprador de uma compra? De acordo com o que vimos até agora
podemosadicionarumanovacolunachamadacompradorcomotipovarchar(200):
mysql>ALTERTABLEcomprasADDCOLUMNcompradorVARCHAR(200);
QueryOK,0rowsaffected(0,04sec)
Records:0Duplicates:0Warnings:0
Vamosverificarcomoficouaestruturadanossatabela:
mysql>DESCcompras;
+-------------+---------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------------+---------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|valor|decimal(18,2)|YES||NULL||
|data|date|YES||NULL||
|observacoes|varchar(255)|NO||NULL||
|recebida|tinyint(1)|YES||0||
|comprador|varchar(200)|YES||NULL||
+-------------+---------------+------+-----+---------+----------------+
6rowsinset(0,00sec)
Ótimo! Agora podemos adicionar os compradores de cada compra! Mas, antes de adicionarmos
precisamos verificar novamente verificar as informações das compras e identificar quem foi que
comprou:
mysql>SELECTid,valor,observacoes,dataFROMcompras;
+----+----------+------------------------------+------------+
|id|valor|observacoes|data|
+----+----------+------------------------------+------------+
|1|20.00|Lanchonete|2016-01-05|
|2|15.00|Lanchonete|2016-01-06|
|3|915.50|Guarda-roupa|2016-01-06|
|4|949.99|Smartphone|2016-01-10|
|5|200.00|Materialescolar|2012-02-19|
|6|3500.00|Televisao|2012-05-21|
|7|1576.40|Materialdeconstrucao|2012-04-30|
|8|163.45|Pizzaprafamilia|2012-12-15|
|9|4780.00|Saladeestar|2013-01-23|
|10|392.15|Quartos|2013-03-03|
|12|402.90|Copa|2013-03-21|
|13|54.98|Lanchonete|2013-04-12|
|14|12.34|Lanchonete|2013-05-23|
42
6JUNTANDODADOSDEVÁRIASTABELAS
|15|78.65|Lanchonete|2013-12-04|
|16|12.39|Sorvetenoparque|2013-01-06|
|17|98.12|HopiHari|2013-07-09|
|18|2498.00|Comprasdejaneiro|2013-01-12|
|19|3212.40|Comprasdomes|2013-11-13|
|20|223.09|Comprasdenatal|2013-12-17|
|21|768.90|Festa|2013-01-16|
|22|827.50|Festa|2014-01-09|
|23|12.00|Salgadonoaeroporto|2014-02-19|
|24|678.43|PassagempraBahia|2014-05-21|
|25|10937.12|CarnavalemCancun|2014-04-30|
|26|1501.00|Presentedasogra|2014-06-22|
|27|1709.00|Parceladacasa|2014-08-25|
|28|567.09|Parceladocarro|2014-09-25|
|29|631.53|IPTU|2014-10-12|
|30|909.11|IPVA|2014-02-11|
|31|768.18|GasolinaviagemPortoAlegre|2014-04-10|
|32|434.00|RodeiointeriordeSaoPaulo|2014-04-01|
|33|115.90|Diadosnamorados|2014-06-12|
|34|98.00|Diadascrianças|2014-10-12|
|35|253.70|Natal-presentes|2014-12-20|
|36|370.15|Comprasdenatal|2014-12-25|
|37|32.09|Lanchonete|2015-07-02|
|38|954.12|ShowdaIveteSangalo|2015-11-03|
|39|98.70|Lanchonete|2015-02-07|
|40|213.50|Roupas|2015-09-25|
|41|1245.20|Roupas|2015-10-17|
|42|23.78|LanchonetedoZé|2015-12-18|
|43|576.12|Sapatos|2015-09-13|
|44|12.34|Canetas|2015-07-19|
|45|87.43|Gravata|2015-05-10|
|46|887.66|Presenteparaofilhao|2015-02-02|
|48|150.00|Compradeteste|2016-01-05|
+----+----------+------------------------------+------------+
46rowsinset(0,00sec)
Agora que já sei as informações das compras posso adicionar os seus compradores. Começaremos
pelas5primeirascompras:
|1|20.00|Lanchonete|2016-01-05|
|2|15.00|Lanchonete|2016-01-06|
|3|915.50|Guarda-roupa|2016-01-06|
|4|949.99|Smartphone|2016-01-10|
|5|200.00|Materialescolar|2012-02-19|
Deacordocomasinformaçõesdomeurascunho,aprimeiracomprafuieumesmoquecomprei:
mysql>UPDATEcomprasSETcomprador='AlexFelipe'WHEREid=1;
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
AsegundaomeuprimoexaráAlexVieiraquecomprou:
mysql>UPDATEcomprasSETcomprador='AlexVieira'WHEREid=2;
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
Oguarda-roupafoiomeutioJoãodaSilvaquecomprou:
mysql>UPDATEcomprasSETcomprador='JoãodaSilva'WHEREid=3;
QueryOK,1rowaffected(0,00sec)
6JUNTANDODADOSDEVÁRIASTABELAS
43
Rowsmatched:1Changed:1Warnings:0
Osmartphonefuieu:
mysql>UPDATEcomprasSETcomprador='AlexFelipe'WHEREid=4;
QueryOK,1rowaffected(0,01sec)
Rowsmatched:1Changed:1Warnings:0
EomaterialescolarfoiomeutioJoãodaSilva:
mysql>UPDATEcomprasSETcomprador='JoãodaSilva'WHEREid=5;
QueryOK,1rowaffected(0,01sec)
Rowsmatched:1Changed:1Warnings:0
Vejamosoresultado:
mysql>SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+----------------+
|id|valor|data|observacoes|recebida|comprador|
+----+----------+------------+------------------------------+----------+----------------+
|1|20.00|2016-01-05|Lanchonete|1|AlexFelipe|
|2|15.00|2016-01-06|Lanchonete|1|AlexVieira|
|3|915.50|2016-01-06|Guarda-roupa|0|JoãodaSilva|
|4|949.99|2016-01-10|Smartphone|0|AlexFelipe|
|5|200.00|2012-02-19|Materialescolar|1|JoãodaSilva|
|...|...|...|...|...|...|
+----+----------+------------+------------------------------+----------+----------------+
46rowsinset(0,01sec)
Pensandobem,nãofoiomeutioJoãodaSilvaquecomprouMaterialescolaresimomeutioJoão
Vieira,eudevoteranotadoerrado...Entãovamosalterar:
mysql>UPDATEcomprasSETcomprador='Joãovieira'WHEREcomprador='JoãodaSilva';
QueryOK,2rowsaffected(0,01sec)
Rowsmatched:2Changed:2Warnings:0
Vejamoscomoficouoresultado:
mysql>select*fromcompraslimit5;
+----+--------+------------+------------------+----------+--------------+
|id|valor|data|observacoes|recebida|comprador|
+----+--------+------------+------------------+----------+--------------+
|1|20.00|2016-01-05|Lanchonete|1|AlexFelipe|
|2|15.00|2016-01-06|Lanchonete|1|AlexVieira|
|3|915.50|2016-01-06|Guarda-roupa|0|Joãovieira|
|4|949.99|2016-01-10|Smartphone|0|AlexFelipe|
|5|200.00|2012-02-19|Materialescolar|1|Joãovieira|
+----+--------+------------+------------------+----------+--------------+
5rowsinset(0,00sec)
Opa! Espera aí! O meu tio João da Silva sumiu!? E o meu tio João Vieira foi inserido com o
sobrenomeminúsculo...Vamosalterarnovamente...PrimeiroonomedoJoãoVieira:
mysql>UPDATEcomprasSETcomprador='JoãoVieira'WHEREcomprador='Joãovieira';
QueryOK,2rowsaffected(0,01sec)
Rowsmatched:2Changed:2Warnings:0
AgoravamosreenseriromeutioJoãodaSilvaparaacompradoguarda-roupa:
44
6JUNTANDODADOSDEVÁRIASTABELAS
mysql>UPDATEcomprasSETcomprador='JoãodaSilva'WHEREid=3;
QueryOK,1rowaffected(0,01sec)
Rowsmatched:1Changed:1Warnings:0
Vamosverificarnovamenteanossatabela:
mysql>SELECT*FROMcomprasLIMIT5;
+----+--------+------------+------------------+----------+----------------+
|id|valor|data|observacoes|recebida|comprador|
+----+--------+------------+------------------+----------+----------------+
|1|20.00|2016-01-05|Lanchonete|1|AlexFelipe|
|2|15.00|2016-01-06|Lanchonete|1|AlexVieira|
|3|915.50|2016-01-06|Guarda-roupa|0|JoãodaSilva|
|4|949.99|2016-01-10|Smartphone|0|AlexFelipe|
|5|200.00|2012-02-19|Materialescolar|1|JoãoVieira|
|...|...|...|...|...|...|
+----+--------+------------+------------------+----------+----------------+
5rowsinset(0,00sec)
Nossa,quetrabalheira!Porcausadeumerrinhoeutivequealterartudonovamente!Seeunãoficar
atento, com certeza acontecerá uma caca gigante no meu banco. Além disso, eu preciso também
adicionarotelefonedocompradorparaumdia,sefornecessário,entraremcontato!Então,novamente,
iremosadicionarumcolunanovacomonometelefoneetipoVARCHAR(15):
mysql>ALTERTABLEcomprasADDCOLUMNtelefoneVARCHAR(30);
QueryOK,0rowsaffected(0,06sec)
Records:0Duplicates:0Warnings:0
Vamosadicionarotelefonedecadaum.Primeiroomeutelefone:
mysql>UPDATEcomprasSETtelefone='5571-2751'WHEREcomprador='AlexFelipe';
QueryOK,2rowsaffected(0,01sec)
Rowsmatched:2Changed:2Warnings:0
Agoraodomeuxará,AlexVieira:
mysql>UPDATEcomprasSETtelefone='5083-3884'WHEREcomprador='AlexVieira';
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
Porfim,ostelefonesdosmeustios:
mysql>UPDATEcomprasSETtelefone='2220-4156'WHEREcomprador='JoãodaSilva';
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
mysql>UPDATEcomprasSETtelefone='2297-0033'WHEREcomprador='JoãoVieira';
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
Vejamoscomoficouanossatabela:
SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+----------------+-----------+
|id|valor|data|observacoes|recebida|comprador|telefone|
+----+----------+------------+------------------------------+----------+----------------+-----------+
|1|20.00|2016-01-05|Lanchonete|1|AlexFelipe|5571-2751|
|2|15.00|2016-01-06|Lanchonete|1|AlexVieira|5083-3884|
|3|915.50|2016-01-06|Guarda-roupa|0|JoãodaSilva|2220-4156|
6JUNTANDODADOSDEVÁRIASTABELAS
45
|4|949.99|2016-01-10|Smartphone|0|AlexFelipe|5571-2751|
|5|200.00|2012-02-19|Materialescolar|1|JoãoVieira|2297-0033|
|...|...|...|...|...|...|...|
+----+----------+------------+------------------------------+----------+----------------+-----------+
46rowsinset(0,00sec)
Certo, agora nossos compradores possuem telefone também. Ainda faltam mais compras sem
comprador, então continuaremos adicionando os compradores. A próxima compra foi o meu primo
AlexVieiraquecomprou.Vamosinseri-lonovamente:
mysql>UPDATEcomprasSETcomprador='AlexVieira'WHEREid=6;
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
Consultandonossatabelaparaverificarcomoestáficando:
mysql>SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+----------------+-----------+
|id|valor|data|observacoes|recebida|comprador|telefone|
+----+----------+------------+------------------------------+----------+----------------+-----------+
|1|20.00|2016-01-05|Lanchonete|1|AlexFelipe|5571-2751|
|2|15.00|2016-01-06|Lanchonete|1|AlexVieira|5083-3884|
|3|915.50|2016-01-06|Guarda-roupa|0|JoãodaSilva|2220-4156|
|4|949.99|2016-01-10|Smartphone|0|AlexFelipe|5571-2751|
|5|200.00|2012-02-19|Materialescolar|1|JoãoVieira|2297-0033|
|6|3500.00|2012-05-21|Televisao|0|AlexVieira|NULL|
|...|...|...|...|...|...|...|
+----+----------+------------+------------------------------+----------+----------------+-----------+
46rowsinset(0,00sec)
Ahnão!Esquecidecolocarotelefone.Deixaeucolocar:
mysql>UPDATEcomprasSETtelefone='5571-2751'WHEREid=6;
QueryOK,1rowaffected(0,00sec)
Rowsmatched:1Changed:1Warnings:0
Vamosverseagoravaidarcerto:
mysql>SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+----------------+-----------+
|id|valor|data|observacoes|recebida|comprador|telefone|
+----+----------+------------+------------------------------+----------+----------------+-----------+
|1|20.00|2016-01-05|Lanchonete|1|AlexFelipe|5571-2751|
|2|15.00|2016-01-06|Lanchonete|1|AlexVieira|5083-3884|
|3|915.50|2016-01-06|Guarda-roupa|0|JoãodaSilva|2220-4156|
|4|949.99|2016-01-10|Smartphone|0|AlexFelipe|5571-2751|
|5|200.00|2012-02-19|Materialescolar|1|JoãoVieira|2297-0033|
|6|3500.00|2012-05-21|Televisao|0|AlexVieira|5571-2751|
|...|...|...|...|...|...|...|
+----+----------+------------+------------------------------+----------+----------------+-----------+
46rowsinset(0,00sec)
Poxa vida... Percebi que ao invés de colocar o telefone do meu primo acabei colocando o meu...
Quanto sofrimento por causa de 2 colunas novas! Além de ter que me preocupar se os nomes dos
compradores estão sendo exatamente iguais, agora eu preciso me preocupar se os telefones também
estãocorretosemais,todasasvezesqueeuprecisoinserirumcompradoreuprecisolembrardeinseriro
seu telefone... E se agora eu precisasse adicionar o endereço, e-mail ou quaisquer informações do
46
6JUNTANDODADOSDEVÁRIASTABELAS
comprador?Euteriaquelembrartodasessasinformaçõescadavezqueeuinserirapenasumacompra!
Quehorror!
Veja o quão problemático está sendo manter as informações de um comprador em uma tabela de
compras.Alémdetrabalhosa,ainserçãoébemproblemática,poisháumgranderiscodefalhasnomeu
banco de dados como por exemplo: informações redundantes (que se repetem) ou então informações
incoerentes(omesmocompradorcomalgumainformaçãodiferente).Comtodacertezanãoéumaboa
soluçãoparaanossanecessidade!Seráquenãoexisteumaformadiferentederesolverisso?
6.1NORMALIZANDONOSSOMODELO
Reparaquetemosdoiselementos,duasentidades,emumaunicatabela:acompraeocomprador.
Quandonosdeparamoscomessestiposdeproblemascriamosnovastabelas.Entãovamoscriaruma
tabelachamadacompradores.
Nonossocasoqueremosquecadacompradorsejarepresentadopeloseunome,endereçoetelefone.
Como já pensamos em modelagem antes, aqui fica o nosso modelo de tabela para representar os
compradores:
mysql>CREATETABLEcompradores(
idINTPRIMARYKEYAUTO_INCREMENT,
nomeVARCHAR(200),
enderecoVARCHAR(200),
telefoneVARCHAR(30)
);
QueryOK,0rowsaffected(0,01sec)
VamosverificaranossatabelapormeiodoDESC:
mysql>DESCcompradores;
+----------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+----------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|nome|varchar(200)|YES||NULL||
|endereco|varchar(200)|YES||NULL||
|telefone|varchar(30)|YES||NULL||
+----------+--------------+------+-----+---------+----------------+
4rowsinset(0,00sec)
Agora que criamos a nossa tabela, vamos inserir alguns compradores, no meu caso, as compras
foramfeitasapenaspormimepelomeutioJoãodaSilva,entãoadicionaremosapenas2compradores:
mysql>INSERTINTOcompradores(nome,endereco,telefone)VALUES
('AlexFelipe','RuaVergueiro,3185','5571-2751');
QueryOK,1rowaffected(0,01sec)
mysql>INSERTINTOcompradores(nome,endereco,telefone)VALUES
('JoãodaSilva','Av.Paulista,6544','2220-4156');
QueryOK,1rowaffected(0,01sec)
Vamosverificarosnossoscompradores:
6.1NORMALIZANDONOSSOMODELO
47
mysql>SELECT*FROMcompradores;
+----+----------------+---------------------+-----------+
|id|nome|endereco|telefone|
+----+----------------+---------------------+-----------+
|1|AlexFelipe|RuaVergueiro,3185|5571-2751|
|2|JoãodaSilva|Av.Paulista,6544|2220-4156|
+----+----------------+---------------------+-----------+
2rowsinset(0,00sec)
Criamos agora duas tabelas diferentes na nossa base de dados, a tabela compras e a tabela
compradores.Entãonãoprecisamosmaisdasinforçõesdoscompradoresnatabelacompras,ouseja,
vamos excluir as colunas comprador e telefone , mas como podemos excluir uma coluna?
Precisamosalteraraestruturadatabela,entãocomeçaremospeloALTERTABLE:
ALTERTABLEcompras
Separaadicionarumacolunautilizamosainstrução ADDCOLUMN, logo, para excluir uma tabela,
utilizaremosainstruçãoDROPCOLUMN:
mysql>ALTERTABLEcomprasDROPCOLUMNcomprador;
QueryOK,0rowsaffected(0,06sec)
Records:0Duplicates:0Warnings:0
Vamosverificaraestruturadanossatabela:
mysql>DESCcompras;
+-------------+---------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------------+---------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|valor|decimal(18,2)|YES||NULL||
|data|date|YES||NULL||
|observacoes|varchar(255)|NO||NULL||
|recebida|tinyint(1)|YES||0||
|telefone|varchar(30)|YES||NULL||
+-------------+---------------+------+-----+---------+----------------+
6rowsinset(0,00sec)
Acolunacompradorfoiexcluídacomsucesso!Porfim,excluiremosacolunatelefone:
mysql>ALTERTABLEcomprasDROPCOLUMNtelefone;
QueryOK,0rowsaffected(0,03sec)
Records:0Duplicates:0Warnings:0
Agora que excluímos todas as colunas relacionadas aos compradores da tabela compras ,
precisamos de alguma forma associar uma compra com um comprador. Então vamos criar uma nova
colunanatabelacompraseadicionaroiddocompradornessacoluna:
mysql>ALTERTABLEcomprasADDCOLUMNid_compradoresint;
QueryOK,0rowsaffected(0,03sec)
Records:0Duplicates:0Warnings:0
Porqueoiddoscompradoresprecisaficarnatabelacomprasenãooiddatabelacomprasna
tabela compradores ? Precisamos pensar seguinte forma: uma compra só pode ser de 1 único
compradoreocompradorpodeter1oumaiscompras,comopodemosgarantirque1compraperteça
48
6.1NORMALIZANDONOSSOMODELO
apenas a 1 comprador? Fazendo com que a compra saiba quem é o seu único comprador! Ou seja,
recebendoachavedoseudono!Porissoadicionamosoiddoscompradoresnatabeladecompras,se
fizéssemosocontrário, ou seja, adicionássemos o id da compra na tabela compradores, iríamos
permitirque1única comprafosserealizadapor1oumaiscompradores,oquenãofazsentidoalgum
sabendoqueasminhascomprassóforamrealizadaspormimapenaseasdomeutioporeleapenas.
Temos a nossa nova coluna para associar o comprador com a sua compra, então vamos fazer
UPDATE na tabela compras inserindo o comprador de id 1 para a primeira metade das compras e
inserindoasegundametadeparaocompradordeid2.Primeirovamosverificarquantascomprasnós
temosusandoafunçãoCOUNT();
mysql>SELECTcount(*)FROMcompras;
+----------+
|count(*)|
+----------+
|46|
+----------+
1rowinset(0,00sec)
Vamosatualizaraprimeirametade:
mysql>UPDATEcomprasSETid_compradores=1WHEREid<22;
QueryOK,20rowsaffected(0,01sec)
Rowsmatched:20Changed:20Warnings:0
Agoraasegundametade:
mysql>UPDATEcomprasSETid_compradores=2WHEREid>21;
QueryOK,26rowsaffected(0,01sec)
Rowsmatched:26Changed:26Warnings:0
Verificandoascomprasnonossobancodedados:
mysql>SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+----------------+
|id|valor|data|observacoes|recebida|id_compradores|
+----+----------+------------+------------------------------+----------+----------------+
|1|20.00|2016-01-05|Lanchonete|1|1|
|2|15.00|2016-01-06|Lanchonete|1|1|
|3|915.50|2016-01-06|Guarda-roupa|0|1|
|4|949.99|2016-01-10|Smartphone|0|1|
|5|200.00|2012-02-19|Materialescolar|1|1|
|6|3500.00|2012-05-21|Televisao|0|1|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|1|
|8|163.45|2012-12-15|Pizzaprafamilia|1|1|
|9|4780.00|2013-01-23|Saladeestar|1|1|
|10|392.15|2013-03-03|Quartos|1|1|
|12|402.90|2013-03-21|Copa|1|1|
|13|54.98|2013-04-12|Lanchonete|0|1|
|14|12.34|2013-05-23|Lanchonete|0|1|
|15|78.65|2013-12-04|Lanchonete|0|1|
|16|12.39|2013-01-06|Sorvetenoparque|0|1|
|17|98.12|2013-07-09|HopiHari|1|1|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|1|
|19|3212.40|2013-11-13|Comprasdomes|1|1|
|20|223.09|2013-12-17|Comprasdenatal|1|1|
|21|768.90|2013-01-16|Festa|1|1|
6.1NORMALIZANDONOSSOMODELO
49
|22|827.50|2014-01-09|Festa|1|2|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|2|
|24|678.43|2014-05-21|PassagempraBahia|1|2|
|25|10937.12|2014-04-30|CarnavalemCancun|1|2|
|26|1501.00|2014-06-22|Presentedasogra|0|2|
|27|1709.00|2014-08-25|Parceladacasa|0|2|
|28|567.09|2014-09-25|Parceladocarro|0|2|
|29|631.53|2014-10-12|IPTU|1|2|
|30|909.11|2014-02-11|IPVA|1|2|
|31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|
|33|115.90|2014-06-12|Diadosnamorados|0|2|
|34|98.00|2014-10-12|Diadascrianças|0|2|
|35|253.70|2014-12-20|Natal-presentes|0|2|
|36|370.15|2014-12-25|Comprasdenatal|0|2|
|37|32.09|2015-07-02|Lanchonete|1|2|
|38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|
|39|98.70|2015-02-07|Lanchonete|1|2|
|40|213.50|2015-09-25|Roupas|0|2|
|41|1245.20|2015-10-17|Roupas|0|2|
|42|23.78|2015-12-18|LanchonetedoZé|1|2|
|43|576.12|2015-09-13|Sapatos|1|2|
|44|12.34|2015-07-19|Canetas|0|2|
|45|87.43|2015-05-10|Gravata|0|2|
|46|887.66|2015-02-02|Presenteparaofilhao|1|2|
|48|150.00|2016-01-05|Compradeteste|0|2|
+----+----------+------------+------------------------------+----------+----------------+
46rowsinset(0,00sec)
6.2ONETOMANY/MANYTOONE
Repare que o que fizemos foi deixar claro que um comprador pode ter muitas compras (um para
muitos),ouaindaquemuitascompraspodemvirdomesmocomprador(manytoone),sódependedo
pontodevista.ChamamosentãodeumarelaçãoOnetoMany(ouManytoOne).
6.3FOREIGNKEY
Conseguimosassociarumacompracomumcomprador,entãoagoravamosbuscarasinformações
dascompraseseusrespectivoscompradores:
mysql>SELECT*FROMcompras;
+----+----------+------------+------------------------------+----------+----------------+
|id|valor|data|observacoes|recebida|id_compradores|
+----+----------+------------+------------------------------+----------+----------------+
|1|20.00|2016-01-05|Lanchonete|1|1|
|2|15.00|2016-01-06|Lanchonete|1|1|
|3|915.50|2016-01-06|Guarda-roupa|0|1|
|4|949.99|2016-01-10|Smartphone|0|1|
|5|200.00|2012-02-19|Materialescolar|1|1|
|6|3500.00|2012-05-21|Televisao|0|1|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|1|
|8|163.45|2012-12-15|Pizzaprafamilia|1|1|
|9|4780.00|2013-01-23|Saladeestar|1|1|
|10|392.15|2013-03-03|Quartos|1|1|
|12|402.90|2013-03-21|Copa|1|1|
|13|54.98|2013-04-12|Lanchonete|0|1|
|14|12.34|2013-05-23|Lanchonete|0|1|
50
6.2ONETOMANY/MANYTOONE
|15|78.65|2013-12-04|Lanchonete|0|1|
|16|12.39|2013-01-06|Sorvetenoparque|0|1|
|17|98.12|2013-07-09|HopiHari|1|1|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|1|
|19|3212.40|2013-11-13|Comprasdomes|1|1|
|20|223.09|2013-12-17|Comprasdenatal|1|1|
|21|768.90|2013-01-16|Festa|1|1|
|22|827.50|2014-01-09|Festa|1|2|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|2|
|24|678.43|2014-05-21|PassagempraBahia|1|2|
|25|10937.12|2014-04-30|CarnavalemCancun|1|2|
|26|1501.00|2014-06-22|Presentedasogra|0|2|
|27|1709.00|2014-08-25|Parceladacasa|0|2|
|28|567.09|2014-09-25|Parceladocarro|0|2|
|29|631.53|2014-10-12|IPTU|1|2|
|30|909.11|2014-02-11|IPVA|1|2|
|31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|
|33|115.90|2014-06-12|Diadosnamorados|0|2|
|34|98.00|2014-10-12|Diadascrianças|0|2|
|35|253.70|2014-12-20|Natal-presentes|0|2|
|36|370.15|2014-12-25|Comprasdenatal|0|2|
|37|32.09|2015-07-02|Lanchonete|1|2|
|38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|
|39|98.70|2015-02-07|Lanchonete|1|2|
|40|213.50|2015-09-25|Roupas|0|2|
|41|1245.20|2015-10-17|Roupas|0|2|
|42|23.78|2015-12-18|LanchonetedoZé|1|2|
|43|576.12|2015-09-13|Sapatos|1|2|
|44|12.34|2015-07-19|Canetas|0|2|
|45|87.43|2015-05-10|Gravata|0|2|
|46|887.66|2015-02-02|Presenteparaofilhao|1|2|
|48|150.00|2016-01-05|Compradeteste|0|2|
+----+----------+------------+------------------------------+----------+----------------+
46rowsinset(0,00sec)
mysql>SELECT*FROMcompradores;
+----+----------------+---------------------+-----------+
|id|nome|endereco|telefone|
+----+----------------+---------------------+-----------+
|1|AlexFelipe|RuaVergueiro,3185|5571-2751|
|2|JoãodaSilva|Av.Paulista,6544|2220-4156|
+----+----------------+---------------------+-----------+
2rowsinset(0,00sec)
Não... Não era isso que eu queria, eu quero que, em apenas uma query, nesse caso um SELECT,
retornetantoasinformaçõesdatabela comprasedatabela compradores. Será que podemos fazer
isso? Sim, podemos! Lembra da instrução FROM que indica qual é a tabela que estamos buscando as
informações?Alémdebuscarporumaúnicatabela,podemosindicarquequeremosbuscarpormaisde
1tabelaseparandoastabelaspor",":
SELECT*FROMcompras,compradores,tabela3,tabela4,...;
EntãovamosadicionaremumúnicoSELECTastabelas:comprasecompradores:
mysql>SELECT*FROMcompras,compradores;
+----+----------+------------+------------------------------+----------+----------------+----+---------------+---------------------+-----------+
|id|valor|data|observacoes|recebida|id_compradores|id|nome
|endereco|telefone|
6.3FOREIGNKEY
51
+----+----------+------------+------------------------------+----------+----------------+----+---------------+---------------------+-----------+
|1|20.00|2016-01-05|Lanchonete|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|1|20.00|2016-01-05|Lanchonete|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|2|15.00|2016-01-06|Lanchonete|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|2|15.00|2016-01-06|Lanchonete|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|3|915.50|2016-01-06|Guarda-roupa|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|3|915.50|2016-01-06|Guarda-roupa|0|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|4|949.99|2016-01-10|Smartphone|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|4|949.99|2016-01-10|Smartphone|0|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|5|200.00|2012-02-19|Materialescolar|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|5|200.00|2012-02-19|Materialescolar|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|6|3500.00|2012-05-21|Televisao|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|6|3500.00|2012-05-21|Televisao|0|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|8|163.45|2012-12-15|Pizzaprafamilia|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|8|163.45|2012-12-15|Pizzaprafamilia|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|9|4780.00|2013-01-23|Saladeestar|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|9|4780.00|2013-01-23|Saladeestar|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|10|392.15|2013-03-03|Quartos|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|10|392.15|2013-03-03|Quartos|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|12|402.90|2013-03-21|Copa|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|12|402.90|2013-03-21|Copa|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|13|54.98|2013-04-12|Lanchonete|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|13|54.98|2013-04-12|Lanchonete|0|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|14|12.34|2013-05-23|Lanchonete|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|14|12.34|2013-05-23|Lanchonete|0|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|15|78.65|2013-12-04|Lanchonete|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|15|78.65|2013-12-04|Lanchonete|0|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|16|12.39|2013-01-06|Sorvetenoparque|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|16|12.39|2013-01-06|Sorvetenoparque|0|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|17|98.12|2013-07-09|HopiHari|1|1|1|AlexF
52
6.3FOREIGNKEY
elipe|RuaVergueiro,3185|5571-2751|
|17|98.12|2013-07-09|HopiHari|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|19|3212.40|2013-11-13|Comprasdomes|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|19|3212.40|2013-11-13|Comprasdomes|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|20|223.09|2013-12-17|Comprasdenatal|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|20|223.09|2013-12-17|Comprasdenatal|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|21|768.90|2013-01-16|Festa|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|21|768.90|2013-01-16|Festa|1|1|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|22|827.50|2014-01-09|Festa|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|22|827.50|2014-01-09|Festa|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|24|678.43|2014-05-21|PassagempraBahia|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|24|678.43|2014-05-21|PassagempraBahia|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|25|10937.12|2014-04-30|CarnavalemCancun|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|25|10937.12|2014-04-30|CarnavalemCancun|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|26|1501.00|2014-06-22|Presentedasogra|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|26|1501.00|2014-06-22|Presentedasogra|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|27|1709.00|2014-08-25|Parceladacasa|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|27|1709.00|2014-08-25|Parceladacasa|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|28|567.09|2014-09-25|Parceladocarro|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|28|567.09|2014-09-25|Parceladocarro|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|29|631.53|2014-10-12|IPTU|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|29|631.53|2014-10-12|IPTU|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|30|909.11|2014-02-11|IPVA|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|30|909.11|2014-02-11|IPVA|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
6.3FOREIGNKEY
53
|33|115.90|2014-06-12|Diadosnamorados|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|33|115.90|2014-06-12|Diadosnamorados|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|34|98.00|2014-10-12|Diadascrianças|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|34|98.00|2014-10-12|Diadascrianças|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|35|253.70|2014-12-20|Natal-presentes|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|35|253.70|2014-12-20|Natal-presentes|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|36|370.15|2014-12-25|Comprasdenatal|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|36|370.15|2014-12-25|Comprasdenatal|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|37|32.09|2015-07-02|Lanchonete|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|37|32.09|2015-07-02|Lanchonete|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|39|98.70|2015-02-07|Lanchonete|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|39|98.70|2015-02-07|Lanchonete|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|40|213.50|2015-09-25|Roupas|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|40|213.50|2015-09-25|Roupas|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|41|1245.20|2015-10-17|Roupas|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|41|1245.20|2015-10-17|Roupas|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|42|23.78|2015-12-18|LanchonetedoZé|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|42|23.78|2015-12-18|LanchonetedoZé|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|43|576.12|2015-09-13|Sapatos|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|43|576.12|2015-09-13|Sapatos|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|44|12.34|2015-07-19|Canetas|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|44|12.34|2015-07-19|Canetas|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|45|87.43|2015-05-10|Gravata|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|45|87.43|2015-05-10|Gravata|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|46|887.66|2015-02-02|Presenteparaofilhao|1|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|46|887.66|2015-02-02|Presenteparaofilhao|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|48|150.00|2016-01-05|Compradeteste|0|2|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|48|150.00|2016-01-05|Compradeteste|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
+----+----------+------------+------------------------------+----------+----------------+----+---------------+---------------------+-----------+
92rowsinset(0,00sec)
54
6.3FOREIGNKEY
Opa!Observequeessaqueryestáumpoucoestranha,poiseladevolveu92linhas,sendoquetemos
apenas46comprasregistradas!Duplicouosnossosregistros...Esseproblemaaconteceu,poisoMySQL
nãosabecomoassociaratabelacompraseatabelacompradores.PrecisamosinformaraoMySQLpor
meiodequalcolunaelefaráessaassociação.Percebaqueacolunaquecontémachave(primarykey)do
comprador é justamente a id_compradores, chamamos esse tipo de coluna de FOREIGN KEY ou
chaveestrangeira.Parajuntarmosachaveestrangeiracomachaveprimáriadeumatabela,utilizamosa
instruçãoJOIN,informandoatabelaeachavequequeremosassociar:
mysql>SELECT*FROMcomprasJOINcompradoresoncompras.id_compradores=compradores.id;
+----+----------+------------+------------------------------+----------+----------------+----+---------------+---------------------+-----------+
|id|valor|data|observacoes|recebida|id_compradores|id|nome
|endereco|telefone|
+----+----------+------------+------------------------------+----------+----------------+----+---------------+---------------------+-----------+
|1|20.00|2016-01-05|Lanchonete|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|2|15.00|2016-01-06|Lanchonete|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|3|915.50|2016-01-06|Guarda-roupa|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|4|949.99|2016-01-10|Smartphone|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|5|200.00|2012-02-19|Materialescolar|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|6|3500.00|2012-05-21|Televisao|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|7|1576.40|2012-04-30|Materialdeconstrucao|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|8|163.45|2012-12-15|Pizzaprafamilia|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|9|4780.00|2013-01-23|Saladeestar|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|10|392.15|2013-03-03|Quartos|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|12|402.90|2013-03-21|Copa|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|13|54.98|2013-04-12|Lanchonete|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|14|12.34|2013-05-23|Lanchonete|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|15|78.65|2013-12-04|Lanchonete|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|16|12.39|2013-01-06|Sorvetenoparque|0|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|17|98.12|2013-07-09|HopiHari|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|18|2498.00|2013-01-12|Comprasdejaneiro|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|19|3212.40|2013-11-13|Comprasdomes|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|20|223.09|2013-12-17|Comprasdenatal|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|21|768.90|2013-01-16|Festa|1|1|1|AlexF
elipe|RuaVergueiro,3185|5571-2751|
|22|827.50|2014-01-09|Festa|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|23|12.00|2014-02-19|Salgadonoaeroporto|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
6.3FOREIGNKEY
55
|24|678.43|2014-05-21|PassagempraBahia|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|25|10937.12|2014-04-30|CarnavalemCancun|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|26|1501.00|2014-06-22|Presentedasogra|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|27|1709.00|2014-08-25|Parceladacasa|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|28|567.09|2014-09-25|Parceladocarro|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|29|631.53|2014-10-12|IPTU|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|30|909.11|2014-02-11|IPVA|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|33|115.90|2014-06-12|Diadosnamorados|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|34|98.00|2014-10-12|Diadascrianças|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|35|253.70|2014-12-20|Natal-presentes|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|36|370.15|2014-12-25|Comprasdenatal|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|37|32.09|2015-07-02|Lanchonete|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|39|98.70|2015-02-07|Lanchonete|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|40|213.50|2015-09-25|Roupas|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|41|1245.20|2015-10-17|Roupas|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|42|23.78|2015-12-18|LanchonetedoZé|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|43|576.12|2015-09-13|Sapatos|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|44|12.34|2015-07-19|Canetas|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|45|87.43|2015-05-10|Gravata|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|46|887.66|2015-02-02|Presenteparaofilhao|1|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
|48|150.00|2016-01-05|Compradeteste|0|2|2|Joãod
aSilva|Av.Paulista,6544|2220-4156|
+----+----------+------------+------------------------------+----------+----------------+----+---------------+---------------------+-----------+
46rowsinset(0,01sec)
Ainstrução JOINesperaumatabelaqueprecisaserjuntada: FROMcomprasJOINcompradores.
Nessecasoestamosjuntandoatabelacomprascomatabelacompradores.Parapassarmosocritériode
junção, utilizamos a instrução ON : ON compras.id_compradores = compradores.id . Nesse
momentoestamosinformandoaFOREIGNKEYdatabelacompras(compras.id_compradores)equalé
achave(compradores.id)databelacompradoresquereferenciaessaFOREIGNKEY.
Agoraanossaqueryretornouosnossosregistroscorretamente.Porém,aindaexisteumproblemana
56
6.3FOREIGNKEY
nossatabelacompras.ObserveesseINSERT:
mysql>INSERTINTOcompras(valor,data,observacoes,id_compradores)
VALUES(1500,'2016-01-05','Playstation4',100);
QueryOK,1rowaffected(0,00sec)
Vamosverificaranossatabelacompras:
mysql>SELECT*FROMcomprasWHEREid_compradores=100;
+----+---------+------------+---------------+----------+----------------+
|id|valor|data|observacoes|recebida|id_compradores|
+----+---------+------------+---------------+----------+----------------+
|49|1500.00|2016-01-05|Playstation4|0|100|
+----+---------+------------+---------------+----------+----------------+
1rowinset(0,00sec)
Note que não existe o comprador com id 100, mas, mesmo assim, conseguimos adicioná-lo na
nossatabelade compras.Precisamosgarantiraintegridadedosnossosdados,informandoaoMySQL
queacolunaid_compradoresdatabelacompraséumaFOREIGNKEYdatabelacompradores,ouseja,
só poderemos adicionar um id apenas se estiver registrado na tabela compradores. Antes de
adicionarmosaFOREIGNKEY,precisamosexcluiroregistrocomoid_compradores=100,poisnãoé
possíveladicionarumaFOREIGNKEYcomdadosquenãoexistanatabelaqueiremosreferenciar.
mysql>DELETEFROMcomprasWHEREid_compradores=100;
QueryOK,1rowaffected(0,01sec)
Quando adicionamos uma FOREIGNKEY em uma tabela, estamos adicionando uma Constraints,
entãoprecisaremosalteraraestruturadatabelacomprasutilizandoainstruçãoALTERTABLE:
mysql>ALTERTABLEcomprasADDCONSTRAINTfk_compradoresFOREIGNKEY(id_compradores)
REFERENCEScompradores(id);
QueryOK,46rowsaffected(0,04sec)
Records:46Duplicates:0Warnings:0
Setentarmosadicionaracompraanteriornovamente:
mysql>INSERTINTOcompras(valor,data,observacoes,id_compradores)
VALUES(1500,'2016-01-05','Playstation4',100);
ERROR1452(23000):Cannotaddorupdateachildrow:aforeignkeyconstraintfails(`ControleDeGast
os`.`compras`,CONSTRAINT`fk_compradores`FOREIGNKEY(`id_compradores`)REFERENCES`compradores`(`
id`))
Agoraonossobancodedadosnãopermiteainserçãoderegistroscomcompradoresinexistentes!Se
tentarmosadicionaressamesmacompracomumcompradorexistente:
INSERTINTOcompras(valor,data,observacoes,id_compradores)
VALUES(1500,'2016-01-05','Playstation4',1);
QueryOK,1rowaffected(0,00sec)
severificarmosessacompra:
mysql>SELECT*FROMcomprasWHEREobservacoes='Playstation4';
+----+---------+------------+---------------+----------+----------------+
|id|valor|data|observacoes|recebida|id_compradores|
+----+---------+------------+---------------+----------+----------------+
|51|1500.00|2016-01-05|Playstation4|0|1|
6.3FOREIGNKEY
57
+----+---------+------------+---------------+----------+----------------+
1rowinset(0,00sec)
6.4DETERMINANDOVALORESFIXOSNATABELA
Conseguimos deixar a nossa base de dados bem robusta, restringindo as nossas colunas para não
permitirvaloresnulosenãoaceitarainserçãodecompradoresinexistentes,masagorasurgiuumanova
necessidadequeprecisaserimplementada,precisamosinformartambémqualéaformadepagamento
que foi realizada na compra, por exemplo, existem 2 formas de pagamento, boleto e crédito. Como
poderíamosimplementaressanovainformaçãonanossatabelaatualmente?Criamosumanovacoluna
comotipoVARCHARecorresmosoriscodeinserirumaformadepagamentoinválida?Nãopareceuma
boa solução... Que tal criarmos uma nova tabela chamada id_forma_de_pagto e fazermos uma
FOREIGNKEY?Aparentementeéumaboasolução,porémiremoscriarumanovatabelapararesolver
umproblemabempequeno?Lembre-sequeacadaFOREIGNKEYmaisJOINsasnossasqueriesterão...
NoMySQL,existeotipodedadoENUMquepermitequeinformemosquaisserãoosdadosqueelepode
aceitar.VamosadicionaroENUMnanossatabeladecompras:
mysql>ALTERTABLEcomprasADDCOLUMNforma_pagtoENUM('BOLETO','CREDITO');
QueryOK,0rowsaffected(0,04sec)
Records:0Duplicates:0Warnings:0
Vamostentaradicionarumacompra:
mysql>INSERTINTOcompras(valor,data,observacoes,id_compradores,forma_pagto)
VALUES(400,'2016-01-06','SSD128GB',1,'BOLETO');
QueryOK,1rowaffected(0,00sec)
Retornandoessanovacompra:
mysql>SELECT*FROMcomprasWHEREobservacoes='SSD128GB';
+----+--------+------------+-------------+----------+----------------+-------------+
|id|valor|data|observacoes|recebida|id_compradores|forma_pagto|
+----+--------+------------+-------------+----------+----------------+-------------+
|52|400.00|2016-01-06|SSD128GB|0|1|BOLETO|
+----+--------+------------+-------------+----------+----------------+-------------+
1rowinset(0,00sec)
Masesetentarmosadicionarumanovacompra,porémcomaformadepagamentoemdinheiro?
mysql>INSERTINTOcompras(valor,data,observacoes,id_compradores,forma_pagto)
VALUES(80,'2016-01-07','Boladefutebol',2,'DINHEIRO');
QueryOK,1rowaffected,1warning(0,00sec)
Vamosverificarcomoficounanossatabeladecompras:
mysql>SELECT*FROMcomprasWHEREobservacoes='Boladefutebol';
+----+-------+------------+-----------------+----------+----------------+-------------+
|id|valor|data|observacoes|recebida|id_compradores|forma_pagto|
+----+-------+------------+-----------------+----------+----------------+-------------+
|53|80.00|2016-01-07|Boladefutebol|0|2||
+----+-------+------------+-----------------+----------+----------------+-------------+
1rowinset(0,00sec)
58
6.4DETERMINANDOVALORESFIXOSNATABELA
6.5SERVERSQLMODES
O MySQL impediu que fosse adicionado um valor diferente de "BOLETO" ou "CREDITO", mas o
querealmenteprecisamoséqueelesimplesmentenãodeixeadicionarumacompraquenãotenhapelo
menos uma dessas formas de pagamento. Além das configurações das tabelas, podemos também
configurar o próprio servidor do MySQL. O servidor do MySQL opera em diferentes SQL modes e
dentre esses modos, existe o strict mode que tem a finalidade de tratar valores inválidos que
configuramosemnossastabelasparainstruçõesde INSERTe UPDATE, como por exemplo, o nosso
ENUM.ParahabilitarostrictmodeprecisamosalteraroSQLmodedanossasessão.Nessecasousaremos
omodo"STRICT_ALL_TABLES":
mysql>SETSESSIONsql_mode='STRICT_ALL_TABLES';
QueryOK,0rowsaffected(0,00sec)
Sequisermosverificarseomodofoimodificadopodemosretornaressevalorpormeiodainstrução
SELECT:
mysql>SELECT@@SESSION.sql_mode;
+--------------------+
|@@SESSION.sql_mode|
+--------------------+
|STRICT_ALL_TABLES|
+--------------------+
1rowinset(0,00sec)
AgoraqueconfiguramosoSQLmodedoMySQLparaimpedirainserçãodevaloresinválidos,vamos
apagaroúltimoregistroquefoiinseridocomvalorinválidoetentaradicioná-lonovamente:
mysql>DELETEFROMcomprasWHEREobservacoes='BOLADEFUTEBOL';
QueryOK,1rowaffected(0,00sec)
Testandonovamenteamesmainserção:
mysql>INSERTINTOcompras(valor,data,observacoes,id_compradores,forma_pagto)
->VALUES(80,'2016-01-07','BOLADEFUTEBOL',2,'DINHEIRO');
ERROR1265(01000):Datatruncatedforcolumn'forma_pagto'atrow1
Perceba que agora o MySQL impediu que a linha fosse inserida, pois o valor não é válido para a
colunaforma_pagto.
OSQLmodeéumaconfiguraçãodoservidorenósalteramosapenasasessãoqueestávamoslogado,o
que aconteceria se caso saímos da sessão que configuramos e entrássemos em uma nova? O MySQL
adotariaoSQLmodepadrãojáconfigurado!Ouseja,teriámosquealterarnovamenteparaostrictmode.
Masalémdasessão,podemosfazeraconfiguraçãoglobaldoSQLmode.
mysql>SETGLOBALsql_mode='STRICT_ALL_TABLES';
QueryOK,0rowsaffected(0,00sec)
SeverificarmosaconfiguraçãoglobalparaoSQLmode:
mysql>SELECT@@GLOBAL.sql_mode;
+-------------------+
6.5SERVERSQLMODES
59
|@@GLOBAL.sql_mode|
+-------------------+
|STRICT_ALL_TABLES|
+-------------------+
1rowinset(0,00sec)
Agora,todasasvezesqueentrarmosnoMySQL,seráadotadoostrictmode.
O ENUMéumaboasoluçãoquandoqueremosrestringirvaloresespecíficosejáesperadosemum
coluna,porémnãofazpartedopadrãoANSIqueéopadrãoparaaescritadeinstruçõesSQL,ouseja,é
umrecursoexclusivodoMySQLecadabancodedadospossuiasuaprópriaimplementaçãoparaessa
mesmafuncionadalidade.
6.6RESUMINDO
Nesse capítulo aprendemos a fazer uma relação entre duas tabelas utilizando FOREIGN KEYS.
Vimostambémqueparafazermosqueriescomduastabelasdiferentesutilizamosaschavesestrangeiras
pormeiodainstrução JOIN que informa ao MySQL quais serão os critérios para associar as tabelas
distintas.Esempreprecisamoslembrarque,quandoestamoslidandocomFOREIGNKEY, precisamos
criarumaConstraintparagarantirquetodasaschavesestrangeirasprecisaexistirnatabelaquefazemos
referência.Alémdisso,vimostambémquequandoqueremosadicionarumacolunanovaparaqueaceite
apenas determinados valores já esperado por nós, como é o caso da forma de pagamento, podemos
utilizaro ENUMdoMySQLcomoumsolução,poréméimportantelembrarqueéumasoluçãoquenão
fazpartedopadrãoANSI,ouseja,cadabancodedadospossuisuaprópriaimplementação.Vamospara
osexercícios?
Vamosparaosexercícios?
EXERCÍCIOS
1. Crieatabelacompradorescomid,nome,enderecoetelefone.
2. Insiraoscompradores,GuilhermeeJoãodaSilva.
3. Adicioneacoluna id_compradoresnatabela compras e defina a chave estrangeira (FOREIGN
KEY)referenciandooiddatabelacompradores.
4. Atualizeatabelacompraseinsiraoiddoscompradoresnacolunaid_compradores.
5. ExibaoNOMEdocompradoreoVALORdetodasascomprasfeitasantesde09/08/2014.
6. ExibatodasascomprasdocompradorquepossuiIDiguala2.
7. Exibatodasascompras(massemosdadosdocomprador),cujocompradortenhanomequecomeça
com'GUILHERME'.
60
6.6RESUMINDO
8. Exibaonomedocompradoreasomadetodasassuascompras.
9. A tabela compras foi alterada para conter uma FOREIGN KEY referenciando a coluna id da
tabela compradores . O objetivo é deixar claro para o banco de dados que
compras.id_compradoresestádealgumaformarelacionadocomatabela compradoresatravés
dacolunacompradores.id.MesmosemcriaraFOREIGNKEYépossívelrelacionartabelasatravés
docomandoJOIN.
10. QualavantagememutilizaraFOREIGNKEY?
11. Crieumacolunachamada"forma_pagto"dotipoENUMedefinaosvalores:'BOLETO'e'CREDITO'.
12. Ativeostrictmodenasessãoqueestáutilizandoparaimpossibilitarvaloresinválidos.Utilizeomodo
"STRICT_ALL_TABLES".EverifiqueseoSQLmodefoialteradofazendoumSELECTnasessão.
13. Tente inserir uma compra com forma de pagamento diferente de 'BOLETO' ou 'CREDITO', por
exemplo,'DINHEIRO'everifiqueseoMySQLrecusaainserção.
14. AdicioneasformasdepagamentoparatodasascompraspormeiodainstruçãoUPDATE.
15. FaçaaconfiguraçãoglobaldoMySQLparaqueelesempreentrenostrictmode.
6.6RESUMINDO
61
CAPÍTULO7
ALUNOSSEMMATRÍCULAEOEXISTS
Paraasegundapartedenossocursoutilizaremosumconjuntodedadosdeumsistemadeensinoonline
comoexemplo.Nãosepreocupe,poisdisponilizaremosoarquivoparaquevocêbaixeeexecuteoscript
comtodasastabelas.
Continuaremos usando o terminal do MySQL durante o curso, porém, se você prefere uma outra
interface,comoporexemplooMySQLWorkbench,fiqueavontadeeusaroqueformelhorparavocê.
AbraoterminaldoMySQLcomocomandomysql-uroot-pecrieabasededadosescola.
createdatabaseescola
QueryOK,1rowaffected(0,01sec)
Agora que criamos a nossa base de dados, podemos importar o arquivo .sql já existente. Saia do
terminaleexecuteoarquivonabasededadosescola.
mysql-uroot-pescola<escola.sql
Com o arquivo importado, podemos abrir novamente o MySQL, porém selecione a base de dados
escoola.
mysql-uroot-pescola
ParaverificartodasastabelasdanossabasededadospodemosutilizaroinstruçãoSHOWTABLESdo
MySQL:
SHOWTABLES;
+------------------+
|Tables_in_escola|
+------------------+
|aluno|
|curso|
|exercicio|
|matricula|
|nota|
|resposta|
|secao|
+------------------+
7rowsinset(0,00sec)
Sabemos quais são as tabelas, porém precisamos saber mais sobre a estrutura dessas tabelas, então
vamosutilizarainstruçãoDESC.Vamosverificarprimeiroatabelaaluno:
62
7ALUNOSSEMMATRÍCULAEOEXISTS
DESCaluno;
+-------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|nome|varchar(255)|NO||||
|email|varchar(255)|NO||||
+-------+--------------+------+-----+---------+----------------+
Percebaqueéumatabelabemsimples,ondeserãoarmazenadasapenasasinformaçõesdosalunos.
Vamosverificaratabelacurso:
DESCcurso;
+-------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|nome|varchar(255)|NO||||
+-------+--------------+------+-----+---------+----------------+
Da mesma forma que a tabela aluno, a tabela curso armazenada apenas as informações dos
cursos.Agoravamosverificaratabelamatricula:
DESCmatricula;
+----------+-------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+----------+-------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|aluno_id|int(11)|NO||NULL||
|curso_id|int(11)|NO||NULL||
|data|datetime|NO||NULL||
|tipo|varchar(20)|NO||||
+----------+-------------+------+-----+---------+----------------+
Conseguimos achar a primeira associação das tabelas, ou seja, a coluna aluno_id referencia a
tabelaalunoeacoluna curso_idreferenciaatabela curso.Entãovamosverificarquaissãotodos
oscursosdeumaluno.Paraumamelhorcompreensãodecomoéoresultadoesperadodessaquery,veja
aplanilhaaseguir:
Figura7.1:Planilhaexemplo
Comopodemosver,nóstemosalistadealunoseseusrespectivoscursos.Entãovamoscomeçara
retornar todos os alunos que possuem uma matrícula, ou seja, vamos fazer um JOIN entre a tabela
alunoematricula:
7ALUNOSSEMMATRÍCULAEOEXISTS
63
SELECTa.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id;
+-----------------+
|nome|
+-----------------+
|JoãodaSilva|
|FredericoJosé|
|AlbertoSantos|
|RenataAlonso|
|PauloJosé|
|ManoelSantos|
|RenataFerreira|
|PaulaSoares|
|RenataAlonso|
|ManoelSantos|
|JoãodaSilva|
|FredericoJosé|
|AlbertoSantos|
|FredericoJosé|
+-----------------+
Percebaqueutilizamosa.nome,m.aluno_idea.id,afinal,oquesignifica?Quandoescrevemos
alunoa,significaqueestamos"apelidando"atabela aluno,ouseja,todasasvezesqueutilizarmos
a.alguma_coisa,estaremospegandoalgumacolunadatabela aluno!EmSQL,esses"apelidos"são
conhecidoscomoAlias.
Os alunos foram retornados. Então agora vamos juntar a tabela curso e aluno com a tabela
matriculaaomesmotempo,porém,dessavezvamosretornaronomedoalunoeonomedocurso:
SELECTa.nome,c.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id
JOINcursocONm.curso_id=c.id;
+-----------------+------------------------------------+
|nome|nome|
+-----------------+------------------------------------+
|JoãodaSilva|SQLebancodedados|
|FredericoJosé|SQLebancodedados|
|AlbertoSantos|Scrumemétodoságeis|
|RenataAlonso|C#eorientaçãoaobjetos|
|PauloJosé|SQLebancodedados|
|ManoelSantos|Scrumemétodoságeis|
|RenataFerreira|DesenvolvimentowebcomVRaptor|
|PaulaSoares|DesenvolvimentomobilecomAndroid|
|RenataAlonso|DesenvolvimentomobilecomAndroid|
|ManoelSantos|SQLebancodedados|
|JoãodaSilva|C#eorientaçãoaobjetos|
|FredericoJosé|C#eorientaçãoaobjetos|
|AlbertoSantos|C#eorientaçãoaobjetos|
|FredericoJosé|DesenvolvimentowebcomVRaptor|
+-----------------+-------------------|-----------------+
Vamosverificarquantosalunosnóstemosnanossabasededados:
SELECTCOUNT(*)FROMaluno;
+----------+
|COUNT(*)|
64
7ALUNOSSEMMATRÍCULAEOEXISTS
+----------+
|16|
+----------+
7.1SUBQUERIES
Observe que foram retornadas 14 linhas quando buscamos todos os alunos e seus cursos, ou seja,
existem alunos que não tem matrícula! Como podemos verificar quais são os alunos que não estão
matriculados?NoMySQL,podemosutilizarafunção EXISTS()paraverificarseexistealgumregistro
deacordocomumadeterminadaquery:
SELECTa.nomeFROMalunoa
WHEREEXISTS(SELECTm.idFROMmatriculamWHEREm.aluno_id=a.id);
+-----------------+
|nome|
+-----------------+
|JoãodaSilva|
|FredericoJosé|
|AlbertoSantos|
|RenataAlonso|
|PauloJosé|
|ManoelSantos|
|RenataFerreira|
|PaulaSoares|
+-----------------+
Repare que escrevemos uma query dentro de uma função, quando fazemos esse tipo de query
chamamosdesubquery.Masoqueaconteceuexatamentenessaquery?QuandoutilizamosoEXISTS()
indicamosquequeremosoretornodetodososalunosnomesdosalunos(a.nome)queestãonatabela
aluno,porém,queremosapenasseexistirumamatrículaparaessealuno EXISTS(SELECTm.idFROM
matriculamWHEREm.aluno_id=a.id).
Perceba que novamente estamos retornando os alunos matriculados sendo que precisamos dos
alunosquenãoestãomatriculados.Nessecaso,podemosutilizarainstrução NOTparafazeranegação,
ouseja,pararetornarosalunosquenãopossuemmatrícula:
SELECTa.nomeFROMalunoa
WHERENOTEXISTS(SELECTm.idFROMmatriculamWHEREm.aluno_id=a.id);
+------------------+
|nome|
+------------------+
|PaulodaSilva|
|CarlosCunha|
|JosedaSilva|
|DaniloCunha|
|ZilmiraJosé|
|CristaldoSantos|
|OsmirFerreira|
|ClaudioSoares|
+------------------+
Conseguimosachartodosalunosquefazempartedosistemaequenãopossuemumamatrícula,algo
7.1SUBQUERIES
65
quenãoéesperado...Provavelmenteessesalunoshaviamcanceladoamatrículamasaindasim,existiao
cadastro deles, sabendo dessas informações temos a capacidade de criar relatórios com informações
relevantespara,porexemplo,osetorcomercialdessainstituiçãoentraremcontatocomosalunosnão
matriculadosetentarefetuarumavenda.
Ainstituiçãosubiualgunsexercícioseprecisasaberquaisdessesexercíciosnãoforamrespondidos.
Vamosnovamenteobservaroresultadoqueseesperautilizandoumaplanilhacomoexemplo:
Figura7.2:Planilhaexemplo
Como havíamos visto anteriormente, existem as tabelas, exercicio e resposta, vamos uma
olhadanatabelaexercicio:
DESCexercicio;
+------------------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+------------------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|secao_id|int(11)|NO||NULL||
|pergunta|varchar(255)|NO||NULL||
|resposta_oficial|varchar(255)|NO||NULL||
+------------------+--------------+------+-----+---------+----------------+
Agoraatabelarespostas:
DESCresposta;
+---------------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+---------------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|exercicio_id|int(11)|YES||NULL||
|aluno_id|int(11)|YES||NULL||
|resposta_dada|varchar(255)|YES||NULL||
+---------------+--------------+------+-----+---------+----------------+
Novamente encontramos uma outra associação, porém agora é entre exercicio e resposta. Então
vamospegartodososexercíciosquenãoforamrespondidosutilizandonovamenteoNOTEXISTS:
SELECT*FROMexercicioe
WHERENOTEXISTS(SELECTr.idFROMrespostarWHEREr.exercicio_id=e.id);
+----+----------+------------------------------+-----------------------------------------------------+
|id|secao_id|pergunta|resposta_oficial
|
+----+----------+------------------------------+-----------------------------------------------------+
|8|4|comofunciona?|insertinto(coluna1,coluna2)values(v1,v2)
|
66
7.1SUBQUERIES
|9|5|Comofuncionaaweb?|requisicaoeresposta
|
|10|5|Quelinguagenspossoajudar?|varias,java,php,c#,etc
|
|11|6|OqueehMVC?|modelviewcontroller
|
|12|6|Frameworksqueusam?|vraptor,springmvc,struts,etc
|
|14|8|Oqueéuminterceptor?|ehcomosefosseumfiltroqueehexecutadoantes
|
|15|8|quandousar?|tratamentodeexcecoes,conexaocomobancodedados
|
+----+----------+------------------------------+-----------------------------------------------------+
Se quisermos retornar da mesma forma que fizemos no exemplo da planilha, basta informar os
camposdesejados:
SELECTe.id,e.perguntaFROMexercicioe
WHERENOTEXISTS(SELECTr.idFROMrespostarWHEREr.exercicio_id=e.id);
+----+------------------------------+
|id|pergunta|
+----+------------------------------+
|8|comofunciona?|
|9|Comofuncionaaweb?|
|10|Quelinguagenspossoajudar?|
|11|OqueehMVC?|
|12|Frameworksqueusam?|
|14|Oqueéuminterceptor?|
|15|quandousar?|
+----+------------------------------+
Opessoaldocomercialdainstituição,informouqueexistemalgunscursosquenãotemnenhuma
matrícula.Vamosverificarcomoéesperadodesseresultadopelanossaplanilha:
Figura7.3:Planilhaexemplo
Da mesma forma que retornamos todos os exercícios que não tinha respostas, podemos retornar
todososcursosquenãopossuemmatrícula:
SELECTc.nomeFROMcursoc
WHERENOTEXISTS(SELECTm.idFROMmatriculamWHEREm.curso_id=c.id);
+--------------------------------+
|nome|
+--------------------------------+
|Javaeorientaçãoaobjetos|
|DesenvolvimentomobilecomiOS|
|RubyonRails|
|PHPeMySql|
+--------------------------------+
7.1SUBQUERIES
67
Vejaquequeriesmuitoparecidaspodemresolverproblemasdiferentes!
A instituição informou que tiveram vários exercícios que não foram respondidos pelos alunos nos
cursosqueforamrealizadosrecentemente.Vamosverificarquemforamessesalunos,paraverificarmos
o motivo de não ter respondido, se foi um problema no sistema ou na base de dados... Novamente
vamosverificaroqueseesperadesseresultadonumaplanilha:
Figura7.4:Planilhaexemplo
Vamostentarfazeressaquery.Começaremosretornandooalunojuntandoatabela alunocoma
tabelamatricula.
SELECTa.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id
Agoravamosjuntartambématabelacursoeretornaroscursostambém:
SELECTa.nome,c.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id
JOINcursocONm.curso_id=c.id
Vamostestareverificarcomoestáanossaqueryatualmente:
+-----------------+------------------------------------+
|nome|nome|
+-----------------+------------------------------------+
|JoãodaSilva|SQLebancodedados|
|FredericoJosé|SQLebancodedados|
|AlbertoSantos|Scrumemétodoságeis|
|RenataAlonso|C#eorientaçãoaobjetos|
|PauloJosé|SQLebancodedados|
|ManoelSantos|Scrumemétodoságeis|
|RenataFerreira|DesenvolvimentowebcomVRaptor|
|PaulaSoares|DesenvolvimentomobilecomAndroid|
|RenataAlonso|DesenvolvimentomobilecomAndroid|
|ManoelSantos|SQLebancodedados|
|JoãodaSilva|C#eorientaçãoaobjetos|
|FredericoJosé|C#eorientaçãoaobjetos|
|AlbertoSantos|C#eorientaçãoaobjetos|
|FredericoJosé|DesenvolvimentowebcomVRaptor|
+-----------------+------------------------------------+
Aparentementeestátudocerto,porémaindaprecisamosinformarquequeremosapenasosalunos
que não responderam os exercícios desses de algum desses cursos. Então adicionaremos agora o NOT
EXISTS():
SELECTa.nome,c.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id
JOINcursocONm.curso_id=c.id
68
7.1SUBQUERIES
WHERENOTEXISTS(SELECTr.aluno_id
FROMrespostarWHEREr.aluno_id=a.id);
+-----------------+------------------------------------+
|nome|nome|
+-----------------+------------------------------------+
|PauloJosé|SQLebancodedados|
|ManoelSantos|Scrumemétodoságeis|
|RenataFerreira|DesenvolvimentowebcomVRaptor|
|PaulaSoares|DesenvolvimentomobilecomAndroid|
|ManoelSantos|SQLebancodedados|
+-----------------+------------------------------------+
Há uma regra no sistema em que não pode permitir que alunos que não estejam matriculados
resposdamosexercícios,ouseja,nãopodeexistirumarespostanatabela respostacomumiddeum
aluno(aluno_id)quenãoestejamatriculado.Vamosprimeiroverificartodososalunosmatriculados
queresponderamosexercícios:
SELECTr.id,a.nomeFROMalunoa
JOINrespostarONr.aluno_id=a.id
WHEREEXISTS(SELECTm.aluno_idFROMmatriculam
WHEREm.aluno_id=a.id);
+----+-----------------+
|id|nome|
+----+-----------------+
|1|JoãodaSilva|
|2|JoãodaSilva|
|3|JoãodaSilva|
|4|JoãodaSilva|
|5|JoãodaSilva|
|6|JoãodaSilva|
|7|JoãodaSilva|
|8|FredericoJosé|
|9|FredericoJosé|
|10|FredericoJosé|
|11|FredericoJosé|
|12|AlbertoSantos|
|13|AlbertoSantos|
|14|AlbertoSantos|
|15|AlbertoSantos|
|16|AlbertoSantos|
|17|AlbertoSantos|
|18|AlbertoSantos|
|19|AlbertoSantos|
|20|AlbertoSantos|
|21|RenataAlonso|
|22|RenataAlonso|
|23|RenataAlonso|
|24|RenataAlonso|
|25|RenataAlonso|
|26|RenataAlonso|
|27|RenataAlonso|
+----+-----------------+
Observequerepetiualgunsalunos,poisumalunorespondeumaisdeumaquestão.Vamosverificar
agoraosqueresponderam,poŕemnãoestãomatriculados:
SELECTr.id,a.nomeFROMalunoa
JOINrespostarONr.aluno_id=a.id
7.1SUBQUERIES
69
WHERENOTEXISTS(SELECTm.aluno_idFROMmatriculam
WHEREm.aluno_id=a.id);
Emptyset(0,00sec)
Oresultadosaiuconformeoesperado,issosignifica,quenãoexistemrespostasdealunosquenão
possuemmatrícula!
7.2RESUMINDO
Nesse capítulo aprendemos a criar queries capazes de retornar valores caso exista, ou não, uma
associação entre duas tabelas, como por exemplo, se um aluno está matriculado ou se um aluno
respondeualgumexercíciopormeiodafunção EXISTS().Aprendemostambémoquesãosubqueries
como no exemplo em que passamos uma query como parâmetro para a função EXISTS() resolver
diversosproblemas,alémdisso,analisamosqueváriasqueries"similadres"temacapacidadederesolver
problemasdistintosutilizandooEXISTS().Vamosparaosexercícios?
EXERCÍCIOS
1. Baixeoschemadobancodedadosaqui
Crieobancodedadosescola:
createdatabaseescola
Importe-onoseuMysqlcomoseguintecomando,diretonoterminal:
mysql-uroot-pescola<escola.sql
FaçaumSELECTqualquerparagarantirqueosdadosestãolá.
DICA:Salveoarquivo escola.sqlemumlugardefácilacessopeloterminal.Vocêdeverodaro
comando para importar o schema no mesmo lugar onde estar o aquivo escola.sql. Por exemplo,
salveoarquivoSQLnapasta\alura-mysql.Depoisabraumterminaleentrenessapasta:
cd\aula-mysql
mysql-uroot-pescola<escola.sql
NoWindowsvocêpodeusarocomando diréparalistarosarquivosdapastaatualnalinhade
comando. Ou seja, ao executar dir na pasta aula-mysql você deve encontrar o arquivo
escola.sql.
1. Busquetodososalunosquenãotenhamnenhumamatrículanoscursos.
2. Busquetodososalunosquenãotiveramnenhumamatrículanosúltimos45dias,usandoainstrução
EXISTS.
70
7.2RESUMINDO
3. ÉpossívelfazeramesmaconsultasemusarEXISTS?Quaissão?
7.2RESUMINDO
71
CAPÍTULO8
AGRUPANDODADOSCOMGROUPBY
Ainstuiçãosolicitouamédiadetodososcursosparafazerumacomparaçãodenotasparaverificarse
todososcursospossuemamesmamédia,quaiscursostemmenoresnotasequaispossuemasmaiores
notas.Vamosverificaraestruturadealgumastabelasdanossabasededados,começaremospelatabela
curso:
DESCcurso;
+-------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|nome|varchar(255)|NO||||
+-------+--------------+------+-----+---------+----------------+
Agoravamosverificaratabelasecao:
DESCsecao;
+------------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+------------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|curso_id|int(11)|NO||NULL||
|titulo|varchar(255)|NO||||
|explicacao|varchar(255)|NO||NULL||
|numero|int(11)|NO||NULL||
+------------+--------------+------+-----+---------+----------------+
Já podemos perceber que existe uma relação entre curso de secao. Vamos também dar uma
olhadanatabelaexercicio:
DESCexercicio;
+------------------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+------------------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|secao_id|int(11)|NO||NULL||
|pergunta|varchar(255)|NO||NULL||
|resposta_oficial|varchar(255)|NO||NULL||
+------------------+--------------+------+-----+---------+----------------+
Observe que na tabela exercicio temos uma associação com a tabela secao. Agora vamos
verificaratabelaresposta:
DESCresposta
72
8AGRUPANDODADOSCOMGROUPBY
+---------------+--------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+---------------+--------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|exercicio_id|int(11)|YES||NULL||
|aluno_id|int(11)|YES||NULL||
|resposta_dada|varchar(255)|YES||NULL||
+---------------+--------------+------+-----+---------+----------------+
Podemosverificarqueatabelarespostaestáassociadacomatabela exercício.Porfim,vamos
verificaratabelanota:
DESCnota;
+-------------+---------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+-------------+---------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|resposta_id|int(11)|YES||NULL||
|nota|decimal(18,2)|YES||NULL||
+-------------+---------------+------+-----+---------+----------------+
Notequetambématabelanotapossuiumaassociação,nessecasocomatabelaresposta.
Comovimos,existemmuitastabelasquepodemosselecionaremnossaquery,entãovamosmontara
nossaqueryporpartes.Começaremospelatabelanota:
SELECTn.notaFROMnotan;
+-------+
|nota|
+-------+
|8.00|
|0.00|
|7.00|
|6.00|
|9.00|
|10.00|
|4.00|
|4.00|
|7.00|
|8.00|
|6.00|
|7.00|
|4.00|
|9.00|
|3.00|
|5.00|
|5.00|
|5.00|
|6.00|
|8.00|
|8.00|
|9.00|
|10.00|
|2.00|
|0.00|
|1.00|
|4.00|
8AGRUPANDODADOSCOMGROUPBY
73
+-------+
Conseguimos pegar todas as notas, agora precisamos resolver a nossa primeira associação, nesse
caso,juntaratabelarespostacomatabelanota:
SELECTn.notaFROMnotan
JOINrespostarONn.resposta_id=r.id;
+-------+
|nota|
+-------+
|8.00|
|0.00|
|7.00|
|6.00|
|9.00|
|10.00|
|4.00|
|4.00|
|7.00|
|8.00|
|6.00|
|7.00|
|4.00|
|9.00|
|3.00|
|5.00|
|5.00|
|5.00|
|6.00|
|8.00|
|8.00|
|9.00|
|10.00|
|2.00|
|0.00|
|1.00|
|4.00|
+-------+
Devolvemostodasasnotasassociadascomatabelaresposta.AgoravamosparaopróximoJOIN
entreatabelarespostaeatabelaexercicio:
SELECTn.notaFROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id;
+-------+
|nota|
+-------+
|8.00|
|0.00|
|7.00|
|6.00|
|9.00|
|10.00|
|4.00|
|4.00|
|7.00|
|8.00|
|6.00|
74
8AGRUPANDODADOSCOMGROUPBY
|7.00|
|4.00|
|9.00|
|3.00|
|5.00|
|5.00|
|5.00|
|6.00|
|8.00|
|8.00|
|9.00|
|10.00|
|2.00|
|0.00|
|1.00|
|4.00|
+-------+
Agorafaremosaassociaçãoentreatabelaexercicioeatabelasecao:
SELECTn.notaFROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id;
+-------+
|nota|
+-------+
|8.00|
|0.00|
|7.00|
|6.00|
|9.00|
|10.00|
|4.00|
|4.00|
|7.00|
|8.00|
|6.00|
|7.00|
|4.00|
|9.00|
|3.00|
|5.00|
|5.00|
|5.00|
|6.00|
|8.00|
|8.00|
|9.00|
|10.00|
|2.00|
|0.00|
|1.00|
|4.00|
+-------+
Porfim,faremosaúltimaassociaçãoentreatabelasecaoeatabelacurso.
SELECTn.notaFROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
8AGRUPANDODADOSCOMGROUPBY
75
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id;
+-------+
|nota|
+-------+
|8.00|
|0.00|
|7.00|
|6.00|
|9.00|
|10.00|
|4.00|
|4.00|
|7.00|
|8.00|
|6.00|
|7.00|
|4.00|
|9.00|
|3.00|
|5.00|
|5.00|
|5.00|
|6.00|
|8.00|
|8.00|
|9.00|
|10.00|
|2.00|
|0.00|
|1.00|
|4.00|
+-------+
Fizemos todas associações que precisávamos, porém repare que ainda está retornando a nota de
todososalunosporcursoumaauma,porémnósprecisamosdamédiaporcurso!NoMySQL,podemos
utilizarafunçãoAVG()paratiraramédia:
SELECTAVG(n.nota)FROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id;
+-------------+
|AVG(n.nota)|
+-------------+
|5.740741|
+-------------+
Observequefoiretornadoapenasumvalor,seráqueessamédiaéigualparatodososcursos?Vamos
tentarretornaroscursoseverificarmos:
SELECTc.nome,AVG(n.nota)FROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id;
76
8AGRUPANDODADOSCOMGROUPBY
+----------------------+-------------+
|nome|AVG(n.nota)|
+----------------------+-------------+
|SQLebancodedados|5.740741|
+----------------------+-------------+
Apenas1curso?Nãoeraesseoresultadoqueesperávamos!QuandoutilizamosafunçãoAVG()ela
calculatodososvaloresexistentesdaqueryeretornaamédia,porémemapenasumalinha!Paraquea
funçãoAVG()calculeamédiadecadacurso,precisamosinformarquequeremosagruparamédiapara
umadeterminadacoluna,nessecaso,acolunac.nome,ouseja,paracadacursodiferentequeremosque
calculeamédia.Paraagruparmosumacolunautilizamosainstrução GROUPBY,informandoacoluna
quequeremosagrupar:
SELECTc.nome,AVG(n.nota)FROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id
GROUPBYc.nome;
+---------------------------------+-------------+
|nome|AVG(n.nota)|
+---------------------------------+-------------+
|C#eorientaçãoaobjetos|4.857143|
|DesenvolvimentowebcomVRaptor|8.000000|
|Scrumemétodoságeis|5.777778|
|SQLebancodedados|6.100000|
+---------------------------------+-------------+
Opessoaldocomercialdainstituiçãoinformouquealgunsalunosestãoreclamandopelaquantidade
de exercícios nos cursos. Então vamos verificar quantos exercícios existem para cada curso. Primeiro
vamosverificarquantosexercíciosexistemnobancousandoafunçãoCOUNT():
SELECTCOUNT(*)FROMexercicio;
+----------+
|COUNT(*)|
+----------+
|31|
+----------+
Retormosaquantidadedetodososexercícios,porémnósprecisamossaberototaldeexercíciospara
cadacurso,ouseja,precisamosjuntaratabelacurso.Porém,parajuntaratabelacurso,teremosque
juntaratabelasecao:
SELECTCOUNT(*)FROMexercicioe
JOINsecaosONe.secao_id=s.id
Agorapodemosjuntaratabelacursoeretornaronomedocursotambém:
SELECTc.nome,COUNT(*)FROMexercicioe
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id;
+----------------------+----------+
|nome|COUNT(*)|
8AGRUPANDODADOSCOMGROUPBY
77
+----------------------+----------+
|SQLebancodedados|31|
+----------------------+----------+
Percebaqueoresultadofoisimilaraoqueaconteceuquandotentamostiraramédiasemagrupar!
Então precisamos também informar que queremos agrupar a contagem pelo nome do curso. Então
vamosadicionaroGROUPBY:
SELECTc.nome,COUNT(*)FROMexercicioe
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id
GROUPBYc.nome;
+---------------------------------+----------+
|nome|COUNT(*)|
+---------------------------------+----------+
|C#eorientaçãoaobjetos|7|
|DesenvolvimentowebcomVRaptor|7|
|Scrumemétodoságeis|9|
|SQLebancodedados|8|
+---------------------------------+----------+
Notequeonomedacolunaquecontatodososexercíciosestáumpoucoestranha,vamosadicionar
umaliasparamelhoraroresultado:
SELECTc.nome,COUNT(*)AScontagemFROMexercicioe
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id
GROUPBYc.nome;
+---------------------------------+----------+
|nome|contagem|
+---------------------------------+----------+
|C#eorientaçãoaobjetos|7|
|DesenvolvimentowebcomVRaptor|7|
|Scrumemétodoságeis|9|
|SQLebancodedados|8|
+---------------------------------+----------+
Agoraorelatóriofazmuitomaissentido.
TodofinaldesemestrenósprecisamosenviarumrelatórioparaoMECinformandoquantosalunos
estãomatriculadosemcadacursodainstituição.Faremosnovamenteanossaqueryporpartes,vamos
retornarprimeirotodososcursos:
SELECTc.nomeFROMcursoc
Vamosjuntaratabelamatricula:
SELECTc.nomeFROMcursoc
JOINmatriculamONm.curso_id=c.id
Agoravamosjuntarosalunostambém:
SELECTc.nomeFROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id;
78
8AGRUPANDODADOSCOMGROUPBY
Precisamosagoracontaraquantidadedealunos:
SELECTc.nome,COUNT(a.id)ASquantidadeFROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id;
+----------------------+------------+
|nome|quantidade|
+----------------------+------------+
|SQLebancodedados|14|
+----------------------+------------+
Lembre-sequeprecisamosagruparacontagempelonomedocurso:
SELECTc.nome,COUNT(a.id)ASquantidadeFROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id
GROUPBYc.nome;
+------------------------------------+------------+
|nome|quantidade|
+------------------------------------+------------+
|C#eorientaçãoaobjetos|4|
|DesenvolvimentomobilecomAndroid|2|
|DesenvolvimentowebcomVRaptor|2|
|Scrumemétodoságeis|2|
|SQLebancodedados|4|
+------------------------------------+------------+
Agoraconseguimosrealizaronossorelatórioconformeoesperado.
8.1RESUMINDO
Vimosnessecapítulocomopodemosgerarrelatóriosutilizandofunçõescomo AVG()e COUNT().
Vimostambémque,seprecisamosretornarascolunasparaverificarqualéovalordecadalinha,como
porexemplo,amédiadecadacurso,precisamosagruparessascolunaspormeiodo GROUPBY.Vamos
paraosexercícios?
EXERCÍCIOS
1. Exibaamédiadasnotasporcurso.
2. Devolva o curso e as médias de notas, levando em conta somente alunos que tenham "Silva" ou
"Santos"nosobrenome.
3. Conteaquantidadederespostasporexercício.Exibaaperguntaeonúmeroderespostas.
4. VocêpodeordenarpeloCOUNTtambém.BastacolocarORDERBYCOUNT(coluna).
Peguearespostadoexercícioanterior,eordenepornúmeroderespostas,emordemdecrescente.
1. Podemos agrupar por mais de um campo de uma só vez. Por exemplo, se quisermos a média de
8.1RESUMINDO
79
notasporalunoporcurso,podemosfazerGROUPBYaluno.id,curso.id.
80
8.1RESUMINDO
CAPÍTULO9
FILTRANDOAGREGAÇÕESEOHAVING
Todo o fim de semestre, a instituição de ensino precisa montar os boletins dos alunos. Então vamos
montaraqueryqueretornarátodasasinformaçõesparamontaroboletim.Começaremosretornando
todasasnotasdosalunos:
SELECTn.notaFROMnotan
Agoravamosassociaracomasrespostascomasnotas:
SELECTn.notaFROMnotan
JOINrespostarONr.id=n.resposta_id
Associaremosagoracomosexercícioscomasrespostas:
SELECTn.notaFROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
Agoraassociaremosaseçãocomosexercícios:
SELECTn.notaFROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
Agoraocursocomaseção:
SELECTn.notaFROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
Porfim,arespostacomoaluno:
SELECTn.notaFROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
JOINalunoaONa.id=r.aluno_id;
Verificandooresultado:
+-------+
|nota|
+-------+
|8.00|
9FILTRANDOAGREGAÇÕESEOHAVING
81
|0.00|
|7.00|
|6.00|
|9.00|
|10.00|
|4.00|
|4.00|
|7.00|
|8.00|
|6.00|
|7.00|
|4.00|
|9.00|
|3.00|
|5.00|
|5.00|
|5.00|
|6.00|
|8.00|
|8.00|
|9.00|
|10.00|
|2.00|
|0.00|
|1.00|
|4.00|
+-------+
Observe que estamos fazendo queries grandes, aconselhamos que, no momento que precisar fazer
uma query complexa, faça queries menores e testes seus resultados, pois se existir alguma instrução
errada,émaisfácildeidentificaroproblema.
Agora que associamos todas as nossas tabelas necessárias, vamos tirar a média com a função de
agregaçãoAVG()queécapazdetirarmédias,conformevistoduranteocurso:
SELECTAVG(n.nota)FROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
JOINalunoaONa.id=r.aluno_id;
+-------------+
|AVG(n.nota)|
+-------------+
|5.740741|
+-------------+
Retornou a média, porém não queremos apenas a média! Precisamos também dos alunos e dos
cursos.Entãovamosadicioná-los:
SELECTa.nome,c.nome,AVG(n.nota)FROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
JOINalunoaONa.id=r.aluno_id;
+----------------+----------------------+-------------+
82
9FILTRANDOAGREGAÇÕESEOHAVING
|nome|nome|AVG(n.nota)|
+----------------+----------------------+-------------+
|JoãodaSilva|SQLebancodedados|5.740741|
+----------------+----------------------+-------------+
Lembre-sequeestamoslidandocomumafunçãodeagregação,ouseja,senãoinformarmosaforma
queelaprecisaagruparascolunas,elaretornaráapenasumalinha!Porém,precisamossemprepensar
em qual tipo de agrupamento é necessário, nesse caso queremos que mostre a média de acada aluno,
entãoagruparemospelosalunos,porémtambémquequeremosqueacadacursoqueoalunosfezmostre
asua:
SELECTa.nome,c.nome,AVG(n.nota)FROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
JOINalunoaONa.id=r.aluno_id
GROUPBYa.nome,c.nome;
+-----------------+---------------------------------+-------------+
|nome|nome|AVG(n.nota)|
+-----------------+---------------------------------+-------------+
|AlbertoSantos|Scrumemétodoságeis|5.777778|
|FredericoJosé|DesenvolvimentowebcomVRaptor|8.000000|
|FredericoJosé|SQLebancodedados|5.666667|
|JoãodaSilva|SQLebancodedados|6.285714|
|RenataAlonso|C#eorientaçãoaobjetos|4.857143|
+-----------------+---------------------------------+-------------+
9.1CONDIÇÕESCOMHAVING
Retornamostodasasmédiasdosalunos,porémainstituiçãoprecisadeumrelatórioseparadopara
todososalunosquereprovaram,ouseja,quetiraramnotabaixa,nessecasomédiasmenoresque5.De
acordocomoquevimosatéagorabastariaadicionarmosumWHERE:
SELECTa.nome,c.nome,AVG(n.nota)FROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
JOINalunoaONa.id=r.aluno_id
WHEREAVG(n.nota)<5
GROUPBYa.nome,c.nome;
ERROR1111(HY000):Invaliduseofgroupfunction
Nesse caso, estamos tentando adicionar condições para uma função de agregação, porém, quando
queremosadicionarcondiçõesparafunçõesdeagregaçãoprecisamosutilizaro HAVING ao invés de
WHERE:
SELECTa.nome,c.nome,AVG(n.nota)FROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
9.1CONDIÇÕESCOMHAVING
83
JOINalunoaONa.id=r.aluno_id
HAVINGAVG(n.nota)<5
GROUPBYa.nome,c.nome;
ERROR1064(42000):YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourM
ySQLserverversionfortherightsyntaxtousenear'GROUPBYa.nome,c.nome'atline8
Além de utilizarmos o HAVING existe um pequeno detalhe, precisamos sempre agrupar antes as
colunaspeloGROUPBYparadepoisutilizarmosoHAVING:
SELECTa.nome,c.nome,AVG(n.nota)FROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
JOINalunoaONa.id=r.aluno_id
GROUPBYa.nome,c.nome
HAVINGAVG(n.nota)<5;
+---------------+-----------------------------+-------------+
|nome|nome|AVG(n.nota)|
+---------------+-----------------------------+-------------+
|RenataAlonso|C#eorientaçãoaobjetos|4.857143|
+---------------+-----------------------------+-------------+
Agoraconseguimosretornaroalunoqueteveamédiaabaixode5.Esequiséssemospegartodosos
alunosqueaprovaram?Ésimples,bastariaalterarosinalpara>=:
SELECTa.nome,c.nome,AVG(n.nota)FROMnotan
JOINrespostarONr.id=n.resposta_id
JOINexercicioeONe.id=r.exercicio_id
JOINsecaosONs.id=e.secao_id
JOINcursocONc.id=s.curso_id
JOINalunoaONa.id=r.aluno_id
GROUPBYa.nome,c.nome
HAVINGAVG(n.nota)>=5;
+-----------------+---------------------------------+-------------+
|nome|nome|AVG(n.nota)|
+-----------------+---------------------------------+-------------+
|AlbertoSantos|Scrumemétodoságeis|5.777778|
|FredericoJosé|DesenvolvimentowebcomVRaptor|8.000000|
|FredericoJosé|SQLebancodedados|5.666667|
|JoãodaSilva|SQLebancodedados|6.285714|
+-----------------+---------------------------------+-------------+
A instuição enviou mais uma solicitação de um relatório informando quais cursos tem poucos
alunosparatomarumadecisãosevaimanteroscursosouseirácancelá-los.Entãovamosnovamente
fazeranossaqueryporpassos,primeirovamoscomeçarselecionandooscursos:
SELECTc.nomeFROMcursoc
Agoravamosjuntarocursocomamatrículaeamatrículacomoalunoeverificaroresultado:
SELECTc.nomeFROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id;
+------------------------------------+
84
9.1CONDIÇÕESCOMHAVING
|nome|
+------------------------------------+
|SQLebancodedados|
|SQLebancodedados|
|Scrumemétodoságeis|
|C#eorientaçãoaobjetos|
|SQLebancodedados|
|Scrumemétodoságeis|
|DesenvolvimentowebcomVRaptor|
|DesenvolvimentomobilecomAndroid|
|DesenvolvimentomobilecomAndroid|
|SQLebancodedados|
|C#eorientaçãoaobjetos|
|C#eorientaçãoaobjetos|
|C#eorientaçãoaobjetos|
|DesenvolvimentowebcomVRaptor|
+------------------------------------+
Nossaqueryestáfuncionando,entãovamoscontaraquantidadedealunoscomafunçãoCOUNT():
SELECTc.nome,COUNT(a.id)FROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id;
Háumdetalhenessaquery,poisqueremoscontartodososalunosdecadacurso,ouseja,precisamos
agruparoscursos!
SELECTc.nome,COUNT(a.id)FROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id
GROUPBYc.nome;
+------------------------------------+-------------+
|nome|COUNT(a.id)|
+------------------------------------+-------------+
|C#eorientaçãoaobjetos|4|
|DesenvolvimentomobilecomAndroid|2|
|DesenvolvimentowebcomVRaptor|2|
|Scrumemétodoságeis|2|
|SQLebancodedados|4|
+------------------------------------+-------------+
Aquery funcionou, porém precisamos saber apenas os cursos que tem poucos alunos, nesse caso,
cursosquetenhammenosde10alunos:
SELECTc.nome,COUNT(a.id)FROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id
GROUPBYc.nome
HAVINGCOUNT(a.id)<10;
+------------------------------------+-------------+
|nome|COUNT(a.id)|
+------------------------------------+-------------+
|C#eorientaçãoaobjetos|4|
|DesenvolvimentomobilecomAndroid|2|
|DesenvolvimentowebcomVRaptor|2|
|Scrumemétodoságeis|2|
|SQLebancodedados|4|
+------------------------------------+-------------+
9.1CONDIÇÕESCOMHAVING
85
Agorapodemosenviarorelatórioparaainstituição.
9.2RESUMINDO
Sabemos que para adicionarmos filtros apenas para colunas utilizamos a instrução WHERE e
indicamostodasaspeculiaridadesnecessárias,porémquandoprecisamosadicionarfiltrosparafunções
de agregação, como por exemplo o AVG(), precisamos utilizar a instrução HAVING. Além disso, é
sempre bom lembrar que, quando estamos desenvolvendo queries grandes, é recomendado que faça
passa-a-passoqueries menores, ou seja, resolva os menores problemas juntando cada tabela por vez e
teste para verificar se está funcionando, pois isso ajuda a verificar aonde está o problema da query.
Vamosparaosexercícios?
EXERCÍCIOS
1. Qualéaprincipaldiferençaentreasinstruçõeshavingewheredosql?
2. Devolva todos os alunos, cursos e a média de suas notas. Lembre-se de agrupar por aluno e por
curso.Filtretambémpelanota:sómostrealunoscomnotamédiamenordoque5.
3. Exibatodososcursoseasuaquantidadedematrículas.Mas,exibasomentecursosquetenhammais
de1matrícula.
4. Exiba o nome do curso e a quantidade de seções que existe nele. Mostre só cursos com mais de 3
seções.
86
9.2RESUMINDO
CAPÍTULO10
MÚLTIPLOSVALORESNACONDIÇÃOEO
IN
Osetordefinanceirodessainstituição,solicitouumrelatórioinformandotodasasformasdepagamento
cadastradas no banco de dados para verificar se está de acordo com o que eles trabalham. Na base de
dados,severificarmosatabelamatricula:
DESCmatricula;
+----------+-------------+------+-----+---------+----------------+
|Field|Type|Null|Key|Default|Extra|
+----------+-------------+------+-----+---------+----------------+
|id|int(11)|NO|PRI|NULL|auto_increment|
|aluno_id|int(11)|NO||NULL||
|curso_id|int(11)|NO||NULL||
|data|datetime|NO||NULL||
|tipo|varchar(20)|NO||||
+----------+-------------+------+-----+---------+----------------+
Observequeexisteacolunatipoquerepresentaqualéaformadepagamento.Eprecisamospegar
umrelatóriodaseguintemaneira:
Figura10.1:Planilhaexemplo
Vamosselecionarapenasacolunatipodatabelamatricula:
SELECTm.tipoFROMmatriculam;
+-------------+
|tipo|
+-------------+
|PAGA_PF|
|PAGA_PJ|
|PAGA_PF|
|PAGA_CHEQUE|
|PAGA_BOLETO|
|PAGA_PJ|
|PAGA_PF|
|PAGA_PJ|
|PAGA_PJ|
|PAGA_CHEQUE|
|PAGA_BOLETO|
|PAGA_PJ|
10MÚLTIPLOSVALORESNACONDIÇÃOEOIN
87
|PAGA_PF|
|PAGA_PJ|
+-------------+
Vejaqueforamretornadostiposdepagamentoiguais,porémprecisamosenviarumrelatórioapenas
com os tipos de pagamento distintos. Para retornamos os valores distintos de uma coluna podemos
utilizarainstruçãoDISTINCT:
SELECTDISTINCTm.tipoFROMmatriculam;
+-------------+
|tipo|
+-------------+
|PAGA_PF|
|PAGA_PJ|
|PAGA_CHEQUE|
|PAGA_BOLETO|
+-------------+
Conseguimosretornarorelatóriodasformasdepagamento,porémosetorfinanceiroaindaprecisa
saberdemaisinformações.Agorafoisolicitadoqueenviasseumrelatóriocomoscursoseaquantidade
dealunosquepossuemotipodepagamentoPJ.Vamosverificaroexemploemumaplanilha:
Figura10.2:Planilhaexemplo
SabemosqueorelatórioésobreaquantidadedematrículasqueforampagascomoPJ,precisamos
contar,ouseja,usaremosafunçãoCOUNT().Vamoscomeçaracontaraquantidadedematrículas:
SELECTCOUNT(m.id)FROMmatriculam;
+-------------+
|COUNT(m.id)|
+-------------+
|14|
+-------------+
Agoravamosjuntarcomatabelacursoeexibironomedocurso:
SELECTc.nome,COUNT(m.id)FROMmatriculam
JOINcursocONm.curso_id=c.id;
+----------------------+-------------+
|nome|COUNT(m.id)|
+----------------------+-------------+
|SQLebancodedados|14|
+----------------------+-------------+
Observequefoiretornadoapenasumalinha!Issosignificaqueafunção COUNT()tambéméuma
funçãodeagregação,ouseja,sequeremosadicionarmaiscolunasnanossaquery,precisamosagrupá-las.
Entãovamosagruparonomedocurso:
88
10MÚLTIPLOSVALORESNACONDIÇÃOEOIN
SELECTc.nome,COUNT(m.id)FROMmatriculam
JOINcursocONm.curso_id=c.id
GROUPBYc.nome;
+------------------------------------+-------------+
|nome|COUNT(m.id)|
+------------------------------------+-------------+
|C#eorientaçãoaobjetos|4|
|DesenvolvimentomobilecomAndroid|2|
|DesenvolvimentowebcomVRaptor|2|
|Scrumemétodoságeis|2|
|SQLebancodedados|4|
+------------------------------------+-------------+
Conseguimos retornar todas os cursos e a quantidade de matrículas, porém precisamos filtrar por
tipodepagamentoPJ.EntãovamosadicionarumWHERE:
SELECTc.nome,COUNT(m.id)FROMmatriculam
JOINcursocONm.curso_id=c.id
WHEREm.tipo='PAGA_PJ'
GROUPBYc.nome;
+------------------------------------+-------------+
|nome|COUNT(m.id)|
+------------------------------------+-------------+
|C#eorientaçãoaobjetos|1|
|DesenvolvimentomobilecomAndroid|2|
|DesenvolvimentowebcomVRaptor|1|
|Scrumemétodoságeis|1|
|SQLebancodedados|1|
+------------------------------------+-------------+
FILTROSUTILIZANDOOIN
Osetorfinanceirodainstituiçãoprecisademaisdetalhessobreostiposdepagamentodecadacurso,
elesprecisamdeumrelatóriosimilaraoquefizemos,porémparatodosquesejampagamentoPJePF.
Paradiferenciarotipodepagamento,precisaremosadicionaracolunadom.tipo:
SELECTc.nome,COUNT(m.id),m.tipo
FROMmatriculam
JOINcursocONm.curso_id=c.id
WHEREm.tipo='PAGA_PJ'
ORm.tipo='PAGA_PF'
GROUPBYc.nome,m.tipo;
+------------------------------------+-------------+---------+
|nome|COUNT(m.id)|tipo|
+------------------------------------+-------------+---------+
|C#eorientaçãoaobjetos|1|PAGA_PF|
|C#eorientaçãoaobjetos|1|PAGA_PJ|
|DesenvolvimentomobilecomAndroid|2|PAGA_PJ|
|DesenvolvimentowebcomVRaptor|1|PAGA_PF|
|DesenvolvimentowebcomVRaptor|1|PAGA_PJ|
|Scrumemétodoságeis|1|PAGA_PF|
|Scrumemétodoságeis|1|PAGA_PJ|
|SQLebancodedados|1|PAGA_PF|
|SQLebancodedados|1|PAGA_PJ|
+------------------------------------+-------------+---------+
10MÚLTIPLOSVALORESNACONDIÇÃOEOIN
89
Suponhamos que agora precisamos retornar também os que foram pagos em boleto ou cheque. O
quepoderíamosadicionarnaquery?MaisORs?
SELECTc.nome,COUNT(m.id),m.tipo
FROMmatriculam
JOINcursocONm.curso_id=c.id
WHEREm.tipo='PAGA_PJ'
ORm.tipo='PAGA_PF'
ORm.tipo='PAGA_BOLETO'
ORm.tipo='PAGA_CHEQUE'
ORm.tipo='...'
ORm.tipo='...'
GROUPBYc.nome,m.tipo;
Resolveria, mas perceba que a nossa query a cada novo tipo de pagamento a nossa query tende a
crescer,dificultandoaleitura...EmSQL,existeainstrução INquepermiteespecificarmosmaisdeum
valorqueprecisamosfiltraraomesmotempoparaumadeterminadacoluna:
SELECTc.nome,COUNT(m.id),m.tipo
FROMmatriculam
JOINcursocONm.curso_id=c.id
WHEREm.tipoIN('PAGA_PJ','PAGA_PF','PAGA_CHEQUE','PAGA_BOLETO')
GROUPBYc.nome,m.tipo;
+------------------------------------+-------------+-------------+
|nome|COUNT(m.id)|tipo|
+------------------------------------+-------------+-------------+
|C#eorientaçãoaobjetos|1|PAGA_BOLETO|
|C#eorientaçãoaobjetos|1|PAGA_CHEQUE|
|C#eorientaçãoaobjetos|1|PAGA_PF|
|C#eorientaçãoaobjetos|1|PAGA_PJ|
|DesenvolvimentomobilecomAndroid|2|PAGA_PJ|
|DesenvolvimentowebcomVRaptor|1|PAGA_PF|
|DesenvolvimentowebcomVRaptor|1|PAGA_PJ|
|Scrumemétodoságeis|1|PAGA_PF|
|Scrumemétodoságeis|1|PAGA_PJ|
|SQLebancodedados|1|PAGA_BOLETO|
|SQLebancodedados|1|PAGA_CHEQUE|
|SQLebancodedados|1|PAGA_PF|
|SQLebancodedados|1|PAGA_PJ|
+------------------------------------+-------------+-------------+
Seumnovotipodepagamentoforadicionado,bastaadicionarmosdentrodo INeanossaquery
funcionarácorretamente.
Ainstituiçãonomeou3alunoscomoosmaisdestacadosnosúltimoscursosrealizamosegostariade
saberquaisforamtodososcursosqueelesfizeram.Os3alunosquesedestacaramforam:JoãodaSilva,
AlbertoSantoseaRenataAlonso.Vamosverificarquaissãoosidsdessesalunos:
SELECT*FROMaluno;
+----+------------------+----------------------+
|id|nome|email|
+----+------------------+----------------------+
|1|JoãodaSilva|[email protected]|
|2|FredericoJosé|[email protected]|
|3|AlbertoSantos|[email protected]|
|4|RenataAlonso|[email protected]|
90
10MÚLTIPLOSVALORESNACONDIÇÃOEOIN
|5|PaulodaSilva|[email protected]|
|6|CarlosCunha|[email protected]|
|7|PauloJosé|[email protected]|
|8|ManoelSantos|[email protected]|
|9|RenataFerreira|[email protected]|
|10|PaulaSoares|[email protected]|
|11|JosedaSilva|[email protected]|
|12|DaniloCunha|[email protected]|
|13|ZilmiraJosé|[email protected]|
|14|CristaldoSantos|[email protected]|
|15|OsmirFerreira|[email protected]|
|16|ClaudioSoares|[email protected]|
+----+------------------+----------------------+
OalunoJoãodaSilvaé1,AlbertoSantos3eRenataAlonso4.Agoraquesabemososidspodemos
verificarosseuscursos.Entãovamoscomeçaranossaqueryretornandotodososcursos:
SELECTc.nomeFROMcursoc;
Agoravamosjuntarocursocomamatrícula:
SELECTc.nomeFROMcursoc
JOINmatriculamONm.curso_id=c.id;
Porfim,vamosjuntaramatriculacomoalunoeretornaronomedoaluno:
SELECTa.nome,c.nomeFROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id;
Fizemostodasasjunções,agorasóprecisamosdofiltro.Precisamosretornaroscursosdos3alunos
aomesmotempo,podemosutilizarainstruçãoIN:
SELECTa.nome,c.nomeFROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id
WHEREa.idIN(1,3,4);
+----------------+------------------------------------+
|nome|nome|
+----------------+------------------------------------+
|JoãodaSilva|SQLebancodedados|
|AlbertoSantos|Scrumemétodoságeis|
|RenataAlonso|C#eorientaçãoaobjetos|
|RenataAlonso|DesenvolvimentomobilecomAndroid|
|JoãodaSilva|C#eorientaçãoaobjetos|
|AlbertoSantos|C#eorientaçãoaobjetos|
+----------------+------------------------------------+
Retornamos todos os cursos dos 3 alunos, porém ainda tá um pouco desorganizado, então vamos
ordenarpelonomedosalunosutilizandooORDERBY:
SELECTa.nome,c.nomeFROMcursoc
JOINmatriculamONm.curso_id=c.id
JOINalunoaONm.aluno_id=a.id
WHEREa.idIN(1,3,4)
ORDERBYa.nome;
+----------------+------------------------------------+
10MÚLTIPLOSVALORESNACONDIÇÃOEOIN
91
|nome|nome|
+----------------+------------------------------------+
|AlbertoSantos|Scrumemétodoságeis|
|AlbertoSantos|C#eorientaçãoaobjetos|
|JoãodaSilva|SQLebancodedados|
|JoãodaSilva|C#eorientaçãoaobjetos|
|RenataAlonso|C#eorientaçãoaobjetos|
|RenataAlonso|DesenvolvimentomobilecomAndroid|
+----------------+------------------------------------+
Nainstituição,serãolançadosalgunscursosnovosde.NETeopessoaldocomercialprecisadivulgar
essescursosparaosex-alunos,porémapenasparaosex-alunosquejáfizeramoscursosdeC#edeSQL.
Inicialmentevamosverificarosidsdessescursos:
SELECT*FROMcurso;
+----+------------------------------------+
|id|nome|
+----+------------------------------------+
|1|SQLebancodedados|
|2|DesenvolvimentowebcomVRaptor|
|3|Scrumemétodoságeis|
|4|C#eorientaçãoaobjetos|
|5|Javaeorientaçãoaobjetos|
|6|DesenvolvimentomobilecomiOS|
|7|DesenvolvimentomobilecomAndroid|
|8|RubyonRails|
|9|PHPeMySql|
+----+------------------------------------+
CursodeSQLé1eocursodeC#é4.Construindoanossaquery,começaremosretornandooaluno:
SELECTa.nomeFROMalunoa;
Entãojuntamoscomamatriculaeocursoevamosretornarquaisforamoscursosrealizados:
SELECTa.nome,c.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id
JOINcursocONm.curso_id=c.id;
AgorautilizaremosofiltropararetornartantoocursodeSQL(1),quantoocursodeC#(4):
SELECTa.nome,c.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id
JOINcursocONm.curso_id=c.id
WHEREc.idIN(1,4);
+-----------------+-----------------------------+
|nome|nome|
+-----------------+-----------------------------+
|JoãodaSilva|SQLebancodedados|
|FredericoJosé|SQLebancodedados|
|RenataAlonso|C#eorientaçãoaobjetos|
|PauloJosé|SQLebancodedados|
|ManoelSantos|SQLebancodedados|
|JoãodaSilva|C#eorientaçãoaobjetos|
|FredericoJosé|C#eorientaçãoaobjetos|
|AlbertoSantos|C#eorientaçãoaobjetos|
+-----------------+-----------------------------+
92
10MÚLTIPLOSVALORESNACONDIÇÃOEOIN
Novamenteoresultadoestádesordenado,vamosordenarpelonomedoaluno:
SELECTa.nome,c.nomeFROMalunoa
JOINmatriculamONm.aluno_id=a.id
JOINcursocONm.curso_id=c.id
WHEREc.idIN(1,4)
ORDERBYa.nome;
+-----------------+-----------------------------+
|nome|nome|
+-----------------+-----------------------------+
|AlbertoSantos|C#eorientaçãoaobjetos|
|FredericoJosé|SQLebancodedados|
|FredericoJosé|C#eorientaçãoaobjetos|
|JoãodaSilva|SQLebancodedados|
|JoãodaSilva|C#eorientaçãoaobjetos|
|ManoelSantos|SQLebancodedados|
|PauloJosé|SQLebancodedados|
|RenataAlonso|C#eorientaçãoaobjetos|
+-----------------+-----------------------------+
Agora sabemos que apenas os alunos Frederico José e João da Silva, são os ex-alunos aptos para
realizarosnovoscursosde.NET.
10.1RESUMINDO
Nesse capítulo vimos que quando precisamos saber todos os valores de uma determinada coluna
podemosutilizarainstruçãoDISTINCTpararetornartodososvaloresdistintos,ouseja,semnenhuma
repetição. Vimos também que quando precisamos realizar vários filtros para uma mesma coluna,
podemos utilizar a instrução IN passando por parâmetro todos os valores que esperamos que seja
retornado,aoinvésdeficarpreenchendoanossaquerycomváriosORs.Vamosparaosexercícios?
EXERCÍCIOS
1. Exibatodosostiposdematrículaqueexistemnatabela.UseDISTINCTparaquenãohajarepetição.
2. Exibatodososcursoseasuaquantidadedematrículas.MasfiltrepormatrículasdostiposPFouPJ.
3. Traga todas as perguntas e a quantidade de respostas de cada uma. Mas dessa vez, somente dos
cursoscomID1e3.
10.1RESUMINDO
93
CAPÍTULO11
SUB-QUERIES
Ainstituiçãoprecisadeumrelatóriomaisrobusto,comasseguintesinformações:Precisadonomedo
alunoecurso,amédiadoalunoemrelaçãoaocursoeadiferençaentreamédiadoalunoeamédiageral
docurso.Demonstrandoemumaplanilha,oresultadoqueseesperaéoseguinte:
Figura11.1:Planilhaexemplo
ObservequeoalunoAlexfezocursodeJavatirou6demédiaeadiferençaentreamédiadeleea
médiageralparaocursodeJavafoi-1,issosignificaqueamédiageraldocursodeJavaé7,ouseja,6-7.
VamosanalisaroalunoGuilherme,vejaqueeletirou6demédiaeadiferençafoi0,poisamédiageral
do curso de SQL é 6, ou seja, 6 - 6. Por fim, a aluna Renata tirou média 7 no curso de C#, porém a
diferençafoi1,issosignificaqueamédiageralé6,ouseja,7-6.
Comovocêmontariaessaquery?Aparentementeéumpoucocomplexa...Entãovamoscomeçarpor
partesdamesmaformaquefizemosanteriormente.Começaremospelatabelanota:
SELECTn.notaFROMnotan;
Agoravamosjuntarastabelasrespostaeexercicioevamosverificaroresultado:
SELECTn.notaFROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id;
+-------+
|nota|
+-------+
|8.00|
|0.00|
|7.00|
|6.00|
|9.00|
|10.00|
|4.00|
|4.00|
|7.00|
|8.00|
|6.00|
|7.00|
94
11SUB-QUERIES
|4.00|
|9.00|
|3.00|
|5.00|
|5.00|
|5.00|
|6.00|
|8.00|
|8.00|
|9.00|
|10.00|
|2.00|
|0.00|
|1.00|
|4.00|
+-------+
Anossaqueryestáfuncionando.Vamosadicionarastabelasde secaoe curso,porém,dessavez
vamosadicionaronomedocurso:
SELECTc.nome,n.notaFROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id;
+---------------------------------+-------+
|nome|nota|
+---------------------------------+-------+
|SQLebancodedados|8.00|
|SQLebancodedados|0.00|
|SQLebancodedados|7.00|
|SQLebancodedados|6.00|
|SQLebancodedados|9.00|
|SQLebancodedados|10.00|
|SQLebancodedados|4.00|
|SQLebancodedados|4.00|
|SQLebancodedados|7.00|
|DesenvolvimentowebcomVRaptor|8.00|
|SQLebancodedados|6.00|
|Scrumemétodoságeis|7.00|
|Scrumemétodoságeis|4.00|
|Scrumemétodoságeis|9.00|
|Scrumemétodoságeis|3.00|
|Scrumemétodoságeis|5.00|
|Scrumemétodoságeis|5.00|
|Scrumemétodoságeis|5.00|
|Scrumemétodoságeis|6.00|
|Scrumemétodoságeis|8.00|
|C#eorientaçãoaobjetos|8.00|
|C#eorientaçãoaobjetos|9.00|
|C#eorientaçãoaobjetos|10.00|
|C#eorientaçãoaobjetos|2.00|
|C#eorientaçãoaobjetos|0.00|
|C#eorientaçãoaobjetos|1.00|
|C#eorientaçãoaobjetos|4.00|
+---------------------------------+-------+
Porfim,juntaremosatabelaalunocomatabelarespostaeretornaremosonomedoaluno:
SELECTa.nome,c.nome,n.notaFROMnotan
11SUB-QUERIES
95
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id
JOINalunoaONr.aluno_id=a.id;
+-----------------+---------------------------------+-------+
|nome|nome|nota|
+-----------------+---------------------------------+-------+
|JoãodaSilva|SQLebancodedados|8.00|
|JoãodaSilva|SQLebancodedados|0.00|
|JoãodaSilva|SQLebancodedados|7.00|
|JoãodaSilva|SQLebancodedados|6.00|
|JoãodaSilva|SQLebancodedados|9.00|
|JoãodaSilva|SQLebancodedados|10.00|
|JoãodaSilva|SQLebancodedados|4.00|
|FredericoJosé|SQLebancodedados|4.00|
|FredericoJosé|SQLebancodedados|7.00|
|FredericoJosé|DesenvolvimentowebcomVRaptor|8.00|
|FredericoJosé|SQLebancodedados|6.00|
|AlbertoSantos|Scrumemétodoságeis|7.00|
|AlbertoSantos|Scrumemétodoságeis|4.00|
|AlbertoSantos|Scrumemétodoságeis|9.00|
|AlbertoSantos|Scrumemétodoságeis|3.00|
|AlbertoSantos|Scrumemétodoságeis|5.00|
|AlbertoSantos|Scrumemétodoságeis|5.00|
|AlbertoSantos|Scrumemétodoságeis|5.00|
|AlbertoSantos|Scrumemétodoságeis|6.00|
|AlbertoSantos|Scrumemétodoságeis|8.00|
|RenataAlonso|C#eorientaçãoaobjetos|8.00|
|RenataAlonso|C#eorientaçãoaobjetos|9.00|
|RenataAlonso|C#eorientaçãoaobjetos|10.00|
|RenataAlonso|C#eorientaçãoaobjetos|2.00|
|RenataAlonso|C#eorientaçãoaobjetos|0.00|
|RenataAlonso|C#eorientaçãoaobjetos|1.00|
|RenataAlonso|C#eorientaçãoaobjetos|4.00|
+-----------------+---------------------------------+-------+
Conseguimosretornartodasasnotasdoalunoeoscursos,porémnósprecisamosdasmédiasenão
detodasasnotas.EntãovamosutilizarafunçãoAVG()pararetornaramédiadoaluno.Lembre-seque
afunçãoAVG()éumafunçãodeagregação,ouseja,precisamosagruparoalunoeocursotambém:
SELECTa.nome,c.nome,AVG(n.nota)FROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id
JOINalunoaONr.aluno_id=a.id
GROUPBYa.nome,c.nome;
+-----------------+---------------------------------+-------------+
|nome|nome|AVG(n.nota)|
+-----------------+---------------------------------+-------------+
|AlbertoSantos|Scrumemétodoságeis|5.777778|
|FredericoJosé|DesenvolvimentowebcomVRaptor|8.000000|
|FredericoJosé|SQLebancodedados|5.666667|
|JoãodaSilva|SQLebancodedados|6.285714|
|RenataAlonso|C#eorientaçãoaobjetos|4.857143|
+-----------------+---------------------------------+-------------+
Agoranóstemosamédiadoalunoeseurespectivocurso,masaindafaltaacolunadadiferençaque
96
11SUB-QUERIES
calculaadiferençaentreamédiadoalunoemumdeterminadocursoesubtraipelamédiageral.Porém
ainda não temos a média geral, então como podemos pegar a média geral? Vamos verificar a tabela
nota:
SELECT*FROMnota;
+----+-------------+-------+
|id|resposta_id|nota|
+----+-------------+-------+
|1|1|8.00|
|2|2|0.00|
|3|3|7.00|
|4|4|6.00|
|5|5|9.00|
|6|6|10.00|
|7|7|4.00|
|8|8|4.00|
|9|9|7.00|
|10|10|8.00|
|11|11|6.00|
|12|12|7.00|
|13|13|4.00|
|14|14|9.00|
|15|15|3.00|
|16|16|5.00|
|17|17|5.00|
|18|18|5.00|
|19|19|6.00|
|20|20|8.00|
|21|21|8.00|
|22|22|9.00|
|23|23|10.00|
|24|24|2.00|
|25|25|0.00|
|26|26|1.00|
|27|27|4.00|
+----+-------------+-------+
Percebaquetemostodasasnotas,teoricamenteasnotasdetodososcursos,ouseja,parapegarmosa
médiageralusaremosoAVG():
SELECTAVG(n.nota)FROMnotan;
+-------------+
|AVG(n.nota)|
+-------------+
|5.740741|
+-------------+
Conseguimosamédiageral,agoravamosadicionaracolunadiferença.Antesdecomeçarafazera
colunadiferençavamosnomearacolunademédiadoalunoparamelhoraravisualização:
SELECTa.nome,c.nome,AVG(n.nota)asmedia_alunoFROMnota
...
Acolunadiferençaprecisadainformaçãoda media_aluno - media geral, porém, nós não temos
nenhumacolunaparaamédiageral,eoresultadoqueprecisamosestáemumaquerydiferente...Como
podemos resolver isso? Adicionando essa outra query dentro da query principal, ou seja, fazer uma
11SUB-QUERIES
97
subquery:
SELECTa.nome,c.nome,AVG(n.nota)asmedia_aluno,
AVG(n.nota)-(SELECTAVG(n.nota)FROMnotan)asdiferenca
FROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id
JOINalunoaONr.aluno_id=a.id
GROUPBYa.nome,c.nome;
+-----------------+---------------------------------+-------------+-----------+
|nome|nome|media_aluno|diferenca|
+-----------------+---------------------------------+-------------+-----------+
|AlbertoSantos|Scrumemétodoságeis|5.777778|0.037037|
|FredericoJosé|DesenvolvimentowebcomVRaptor|8.000000|2.259259|
|FredericoJosé|SQLebancodedados|5.666667|-0.074074|
|JoãodaSilva|SQLebancodedados|6.285714|0.544974|
|RenataAlonso|C#eorientaçãoaobjetos|4.857143|-0.883598|
+-----------------+---------------------------------+-------------+-----------+
Observe que agora retornamos a diferença, mas será que essas informações batem? Que tal
retornamosamédiageraltambém?
SELECTa.nome,c.nome,AVG(n.nota)asmedia_aluno,
(SELECTAVG(n.nota)FROMnotan)asmedia_geral,
AVG(n.nota)-(SELECTAVG(n.nota)FROMnotan)asdiferenca
FROMnotan
JOINrespostarONn.resposta_id=r.id
JOINexercicioeONr.exercicio_id=e.id
JOINsecaosONe.secao_id=s.id
JOINcursocONs.curso_id=c.id
JOINalunoaONr.aluno_id=a.id
GROUPBYa.nome,c.nome;
+-----------------+---------------------------------+-------------+-------------+-----------+
|nome|nome|media_aluno|media_geral|diferenca|
+-----------------+---------------------------------+-------------+-------------+-----------+
|AlbertoSantos|Scrumemétodoságeis|5.777778|5.740741|0.037037|
|FredericoJosé|DesenvolvimentowebcomVRaptor|8.000000|5.740741|2.259259|
|FredericoJosé|SQLebancodedados|5.666667|5.740741|-0.074074|
|JoãodaSilva|SQLebancodedados|6.285714|5.740741|0.544974|
|RenataAlonso|C#eorientaçãoaobjetos|4.857143|5.740741|-0.883598|
+-----------------+---------------------------------+-------------+-------------+-----------+
Conseguimos exibir o relatório como esperado, porém existe um pequeno detalhe. Note que o
resultadodasubquery(SELECTAVG(n.nota)FROMnotan)foideapenasumalinhaeéjustamentepor
essemotivoqueconseguimosefetuaroperaçõesaritméticascomo,nessecaso,asubtração.Seoresultado
fossemaisdeumalinha,nãoseriapossívelrealizaroperações.
Ainstituiçãoprecisadeumrelatóriodoaproveitamentodosalunosnoscursos,ouseja,precisamos
saberseelesestãorespondendotodososexercícios,entãoiremosbuscaronúmeroderespostasquecada
respondeualunoindividualmente.Vamosverificaroqueéesperadodoresultadoemumaplanilha:
98
11SUB-QUERIES
Figura11.2:Planilhaexemplo
Entãoprimeirocomeçaremosretornandoosalunos:
SELECTa.nomeFROMalunoa;
Agoraprecisamosdaquantidadedetodasasrespostas,entãousaremosoCOUNT():
SELECTCOUNT(r.id)FROMrespostar;
+-------------+
|COUNT(r.id)|
+-------------+
|27|
+-------------+
Sabemos a query que conta as respostas e sabemos a query que retornam os alunos, então vamos
adicionar a query que conta as respostas dentro da que retorna os alunos, ou seja, vamos fazer
novamenteumasubquery!
SELECTa.nome,(SELECTCOUNT(r.id)FROMrespostar)ASquantidade_respostasFROMalunoa;
+------------------+----------------------+
|nome|quantidade_respostas|
+------------------+----------------------+
|JoãodaSilva|27|
|FredericoJosé|27|
|AlbertoSantos|27|
|RenataAlonso|27|
|PaulodaSilva|27|
|CarlosCunha|27|
|PauloJosé|27|
|ManoelSantos|27|
|RenataFerreira|27|
|PaulaSoares|27|
|JosedaSilva|27|
|DaniloCunha|27|
|ZilmiraJosé|27|
|CristaldoSantos|27|
|OsmirFerreira|27|
|ClaudioSoares|27|
+------------------+----------------------+
Observequeos resultadosda quantidadederespostas foramiguais paratodosos alunos,poisnão
adicionamos nenhum filtro na subquery. Para resolver o problema, basta adicionar um WHERE
indicandooqueprecisaserfiltrado,nessecaso,oiddosalunosretornadosnaqueryprincipal:
SELECTa.nome,(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASquantidade_respostasF
ROMalunoa;
+------------------+----------------------+
11SUB-QUERIES
99
|nome|quantidade_respostas|
+------------------+----------------------+
|JoãodaSilva|7|
|FredericoJosé|4|
|AlbertoSantos|9|
|RenataAlonso|7|
|PaulodaSilva|0|
|CarlosCunha|0|
|PauloJosé|0|
|ManoelSantos|0|
|RenataFerreira|0|
|PaulaSoares|0|
|JosedaSilva|0|
|DaniloCunha|0|
|ZilmiraJosé|0|
|CristaldoSantos|0|
|OsmirFerreira|0|
|ClaudioSoares|0|
+------------------+----------------------+
Ainstituiçãoprecisadeumarelatóriomuitoparecidocomaqueryqueacabamosdefazer,elaprecisa
saberquantasmatrículasumalunotem,ouseja,aoínvesderesposta,informaremosasmatrículas.Então
vamosapenassubstituirasinformaçõesdasrespostaspelasinformaçõesdamatrícula:
SELECTa.nome,(SELECTCOUNT(m.id)FROMmatriculamWHEREm.aluno_id=a.id)ASquantidade_matricula
FROMalunoa;
+------------------+----------------------+
|nome|quantidade_matricula|
+------------------+----------------------+
|JoãodaSilva|2|
|FredericoJosé|3|
|AlbertoSantos|2|
|RenataAlonso|2|
|PaulodaSilva|0|
|CarlosCunha|0|
|PauloJosé|1|
|ManoelSantos|2|
|RenataFerreira|1|
|PaulaSoares|1|
|JosedaSilva|0|
|DaniloCunha|0|
|ZilmiraJosé|0|
|CristaldoSantos|0|
|OsmirFerreira|0|
|ClaudioSoares|0|
+------------------+----------------------+
Conseguimospegaraquantidadederespostaematriculadeumdeterminadoaluno,porémfizemos
isso separadamente, porém agora precisamos juntar essas informações para montar em um único
relatórioquemostre,onomedoaluno,aquantidadederespostaseaquantidadedematrículas.Então
vamospartirdoprincípio,ouseja,fazeraqueryqueretornatodososalunos:
SELECTa.nomeFROMalunoa;
Agoravamospegaraquantidadederespostas:
SELECTCOUNT(r.id)FROMrespostar;
100
11SUB-QUERIES
Eentãovamospegaraquantidadedematriculas:
SELECTCOUNT(m.id)FROMmatriculam;
TemostodososSELECTsqueresolvemumdeterminadoproblema,ouseja,agoraprecisamosjuntar
todos eles para resolver a nova necessidade. Então vamos adicionar as duas queries que contam as
matrículaseasrespostasdentrodaqueryprincipal,ouseja,aqueretornaosalunos:
SELECTa.nome,
(SELECTCOUNT(m.id)FROMmatriculamWHEREm.aluno_id=a.id)ASquantidade_matricula,
(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASquantidade_respostas
FROMalunoa;
+------------------+----------------------+----------------------+
|nome|quantidade_matricula|quantidade_respostas|
+------------------+----------------------+----------------------+
|JoãodaSilva|2|7|
|FredericoJosé|3|4|
|AlbertoSantos|2|9|
|RenataAlonso|2|7|
|PaulodaSilva|0|0|
|CarlosCunha|0|0|
|PauloJosé|1|0|
|ManoelSantos|2|0|
|RenataFerreira|1|0|
|PaulaSoares|1|0|
|JosedaSilva|0|0|
|DaniloCunha|0|0|
|ZilmiraJosé|0|0|
|CristaldoSantos|0|0|
|OsmirFerreira|0|0|
|ClaudioSoares|0|0|
+------------------+----------------------+----------------------+
11.1RESUMINDO
Vimosquenessecapítuloaprendemosautilizarsubqueriespararesolverdiversosproblemas,como
por exemplo contar a quantidade de matriculas ou de respostas de um aluno. Também vimos que
podemosaplicaroperaçõesaritméticasutilizandosubqueries,porémésempreimportantelembrarque
sópodemosrealizaressetipodeoperaçõesdesdequeasubquerieretorneumaúnicalinha.Entãovamos
paraosexercícios?
EXERCÍCIOS
1. Exibaamédiadasnotasporaluno,alémdeumacolunacomadiferençaentreamédiadoalunoea
médiageral.Usesub-queriesparaisso.
2. Qualéoproblemadeseusarsub-queries?
3. Exiba a quantidade de matrículas por curso. Além disso, exiba a divisão entre matrículas naquele
cursoematrículastotais.
11.1RESUMINDO
101
102
11.1RESUMINDO
CAPÍTULO12
ENTENDENDOOLEFTJOIN
Osinstrutoresdainstituiçãopediramumrelatóriocomosalunosquesãomaisparticipativosnasalade
aula, ou seja, queremos retornar os alunos que responderam mais exercícios. Consequentemente
encontraremos também os alunos que não estão participando muito, então já aproveitamos e
conversamoscomelesparaentenderoqueestáacontecendo.Entãocomeçaremosretornandooaluno:
SELECTa.nomeFROMalunon;
Agora vamos contar a quantidade de respostas por meio da função COUNT() e agrupando pelo
nomedoaluno:
SELECTa.nome,COUNT(r.id)ASrespostas
FROMalunoa
JOINrespostarONr.aluno_id=a.id
GROUPBYa.nome;
+-----------------+-----------+
|nome|respostas|
+-----------------+-----------+
|AlbertoSantos|9|
|FredericoJosé|4|
|JoãodaSilva|7|
|RenataAlonso|7|
+-----------------+-----------+
Mas onde estão todos os meus alunos? Fugiram? Aparentemente essa query não está trazendo
exatamenteoqueagenteesperava...Vamoscontaraquantidadedealunosexistentes:
SELECTCOUNT(a.id)FROMalunoa;
+-------------+
|COUNT(a.id)|
+-------------+
|16|
+-------------+
Observe que existe 16 alunos no banco de dados, porém só foram retornados 4 alunos e suas
respostas. Provavelmente não está sendo retornando os alunos que não possuem respostas! Vamos
verificaroqueestáacontecendoexatamente.Vamospegarumalunoquenãofoiretornado,comoode
id5.Quantasrespostaseletem?
SELECTr.idFROMrespostarWHEREr.aluno_id=5;
+------------------+
|id|
+------------------+
12ENTENDENDOOLEFTJOIN
103
|0|
+------------------+
Tudo bem, ele não respondeu, mas como ele foi desaparecer daquela query nossa? Vamos pegar
outroalunoquedesapareceu,odeid6:
SELECTr.idFROMrespostarWHEREr.aluno_id=6;
+------------------+
|id|
+------------------+
|0|
+------------------+
Opa,parecequeencontramosumpadrão.
SELECTr.idFROMrespostarWHEREr.aluno_id=7;
+------------------+
|id|
+------------------+
|0|
+------------------+
Sim,encontramosumpadrão.Seráqueéverdadeiroessateoriaqueestásurgindonaminhacabeça?
Alunos sem resposta desapareceram? Vamos procurar todos os alunos que não possuem nenhuma
resposta.Istoéselecionarosalunosquenãoexiste,respostadestealuno:
SELECTa.nomeFROMalunoaWHERENOTEXISTS(SELECTr.idFROMrespostarWHEREr.aluno_id=a.id);
+------------------+
|nome|
+------------------+
|PaulodaSilva|
|CarlosCunha|
|PauloJosé|
|ManoelSantos|
|RenataFerreira|
|PaulaSoares|
|JosedaSilva|
|DaniloCunha|
|ZilmiraJosé|
|CristaldoSantos|
|OsmirFerreira|
|ClaudioSoares|
+------------------+
Se verificarmos os nomes, realmente, todos os alunos que não tem respostas não estão sendo
retornadosnaquelaprimeiraquery.Porémnósqueremostambémqueretorneosalunossemrespostas...
Vamostentardeumaoutramaneira,vamosretornaronomedoalunoearespostaqueelerespondeu:
SELECTa.nome,r.resposta_dadaFROMalunoa
JOINrespostarONr.aluno_id=a.id;
+-----------------+----------------------------------------------------------------------------------+
|nome|resposta_dada
|
104
12ENTENDENDOOLEFTJOIN
+-----------------+----------------------------------------------------------------------------------+
|JoãodaSilva|umaselecao
|
|JoãodaSilva|ixi,naosei
|
|JoãodaSilva|alterardados
|
|JoãodaSilva|eskecerowhereealterartudo
|
|JoãodaSilva|apagarcoisas
|
|JoãodaSilva|tbnaopodeeskecerowhere
|
|JoãodaSilva|inserirdados
|
|FredericoJosé|buscardados
|
|FredericoJosé|selectcamposfromtabela
|
|FredericoJosé|alterarcoisas
|
|FredericoJosé|ixi,naosei
|
|AlbertoSantos|tempoprafazeralgo
|
|AlbertoSantos|1a4semanas
|
|AlbertoSantos|melhoriadoprocesso
|
|AlbertoSantos|tododia
|
|AlbertoSantos|reuniaodestatus
|
|AlbertoSantos|tododia
|
|AlbertoSantos|oquadrobranco
|
|AlbertoSantos|ummetodoagil
|
|AlbertoSantos|temvariosoutros
|
|RenataAlonso|ehainternet
|
|RenataAlonso|browserfazrequisicao,servidormandaresposta
|
|RenataAlonso|ehoservidorquelidacomhttp
|
|RenataAlonso|naosei
|
|RenataAlonso|bancodedados!
|
|RenataAlonso|ehcolocaraappnainternet
|
|RenataAlonso|dependedatecnologia,masgeralmenteehlevarpraumservidorquetanainternet
|
+-----------------+----------------------------------------------------------------------------------+
Agoravamosadicionaracolunaiddatabelaalunoealuno_iddatabelaresposta:
SELECTa.id,a.nome,r.aluno_id,r.resposta_dadaFROMalunoa
12ENTENDENDOOLEFTJOIN
105
JOINrespostarONr.aluno_id=a.id;
+----+-----------------+----------+----------------------------------------------------------------------------------+
|id|nome|aluno_id|resposta_dada
|
+----+-----------------+----------+----------------------------------------------------------------------------------+
|1|JoãodaSilva|1|umaselecao
|
|1|JoãodaSilva|1|ixi,naosei
|
|1|JoãodaSilva|1|alterardados
|
|1|JoãodaSilva|1|eskecerowhereealterartudo
|
|1|JoãodaSilva|1|apagarcoisas
|
|1|JoãodaSilva|1|tbnaopodeeskecerowhere
|
|1|JoãodaSilva|1|inserirdados
|
|2|FredericoJosé|2|buscardados
|
|2|FredericoJosé|2|selectcamposfromtabela
|
|2|FredericoJosé|2|alterarcoisas
|
|2|FredericoJosé|2|ixi,naosei
|
|3|AlbertoSantos|3|tempoprafazeralgo
|
|3|AlbertoSantos|3|1a4semanas
|
|3|AlbertoSantos|3|melhoriadoprocesso
|
|3|AlbertoSantos|3|tododia
|
|3|AlbertoSantos|3|reuniaodestatus
|
|3|AlbertoSantos|3|tododia
|
|3|AlbertoSantos|3|oquadrobranco
|
|3|AlbertoSantos|3|ummetodoagil
|
|3|AlbertoSantos|3|temvariosoutros
|
|4|RenataAlonso|4|ehainternet
|
|4|RenataAlonso|4|browserfazrequisicao,servidormandaresposta
|
|4|RenataAlonso|4|ehoservidorquelidacomhttp
|
|4|RenataAlonso|4|naosei
|
|4|RenataAlonso|4|bancodedados!
|
|4|RenataAlonso|4|ehcolocaraappnainternet
|
|4|RenataAlonso|4|dependedatecnologia,masgeralmenteehlevarpraumservidorqu
etanainternet|
+----+-----------------+----------+------------------------------------------------------------------
106
12ENTENDENDOOLEFTJOIN
-----------------+
Perceba que separamos as informações dos alunos de um lado e a das respostas do outro lado.
Analisando esses dados podemos verificar que quando fizemos o JOIN entre a tabela aluno e
resposta estamos trazendo apenas todos os registros que possuem o id da tabela aluno e o
aluno_iddatabelaresposta.
OqueoSQLfaztambéméquetodososalunoscujoo idnão estejanacoluna aluno_id não
serãoretornados!Istoé,elesótraráparanósalguémqueo JOINtenhavalorigualnasduastabelas.Se
sóestápresenteemumadastabelas,eleignora.
EmSQL,existeum JOINdiferentequepermiteoretornodealunosquetambémnãopossuamo
id na tabela que está sendo associada. Queremos pegar todo mundo da tabela da esquerda,
independentementedeexistirounãoumvalornatabeladadireita.Éumtaldejoindeesquerda,oLEFT
JOIN,ouseja,eletrarátodososregistrosdatabeladaesquerdamesmoquenãoexistaumaassociação
natabeladadireita:
SELECTa.id,a.nome,r.aluno_id,r.resposta_dadaFROMalunoa
LEFTJOINrespostarONr.aluno_id=a.id;
+----+------------------+----------+----------------------------------------------------------------------------------+
|id|nome|aluno_id|resposta_dada
|
+----+------------------+----------+----------------------------------------------------------------------------------+
|1|JoãodaSilva|1|umaselecao
|
|1|JoãodaSilva|1|ixi,naosei
|
|1|JoãodaSilva|1|alterardados
|
|1|JoãodaSilva|1|eskecerowhereealterartudo
|
|1|JoãodaSilva|1|apagarcoisas
|
|1|JoãodaSilva|1|tbnaopodeeskecerowhere
|
|1|JoãodaSilva|1|inserirdados
|
|2|FredericoJosé|2|buscardados
|
|2|FredericoJosé|2|selectcamposfromtabela
|
|2|FredericoJosé|2|alterarcoisas
|
|2|FredericoJosé|2|ixi,naosei
|
|3|AlbertoSantos|3|tempoprafazeralgo
|
|3|AlbertoSantos|3|1a4semanas
|
|3|AlbertoSantos|3|melhoriadoprocesso
|
|3|AlbertoSantos|3|tododia
|
12ENTENDENDOOLEFTJOIN
107
|3|AlbertoSantos|3|reuniaodestatus
|
|3|AlbertoSantos|3|tododia
|
|3|AlbertoSantos|3|oquadrobranco
|
|3|AlbertoSantos|3|ummetodoagil
|
|3|AlbertoSantos|3|temvariosoutros
|
|4|RenataAlonso|4|ehainternet
|
|4|RenataAlonso|4|browserfazrequisicao,servidormandaresposta
|
|4|RenataAlonso|4|ehoservidorquelidacomhttp
|
|4|RenataAlonso|4|naosei
|
|4|RenataAlonso|4|bancodedados!
|
|4|RenataAlonso|4|ehcolocaraappnainternet
|
|4|RenataAlonso|4|dependedatecnologia,masgeralmenteehlevarpraumservidorq
uetanainternet|
|5|PaulodaSilva|NULL|NULL
|
|6|CarlosCunha|NULL|NULL
|
|7|PauloJosé|NULL|NULL
|
|8|ManoelSantos|NULL|NULL
|
|9|RenataFerreira|NULL|NULL
|
|10|PaulaSoares|NULL|NULL
|
|11|JosedaSilva|NULL|NULL
|
|12|DaniloCunha|NULL|NULL
|
|13|ZilmiraJosé|NULL|NULL
|
|14|CristaldoSantos|NULL|NULL
|
|15|OsmirFerreira|NULL|NULL
|
|16|ClaudioSoares|NULL|NULL
|
+----+------------------+----------+----------------------------------------------------------------------------------+
Conseguimosretornartodososregistros.Entãoagoravamostentarcontarnovamentesasrespostas,
agrupandopelonome:
SELECTa.nome,COUNT(r.id)ASrespostas
FROMalunoa
LEFTJOINrespostarONr.aluno_id=a.id
GROUPBYa.nome;
+------------------+-----------+
|nome|respostas|
+------------------+-----------+
108
12ENTENDENDOOLEFTJOIN
|AlbertoSantos|9|
|CarlosCunha|0|
|ClaudioSoares|0|
|CristaldoSantos|0|
|DaniloCunha|0|
|FredericoJosé|4|
|JoãodaSilva|7|
|JosedaSilva|0|
|ManoelSantos|0|
|OsmirFerreira|0|
|PaulaSoares|0|
|PaulodaSilva|0|
|PauloJosé|0|
|RenataAlonso|7|
|RenataFerreira|0|
|ZilmiraJosé|0|
+------------------+-----------+
Agoraconseguimosretornartodososalunoseaquantidadederespostas,mesmoqueoalunonão
tenharespondidopelomenosumaresposta.
12.1RIGHTJOIN
Vamos supor que ao invés de retornar todos os alunos e suas respostas, mesmo que o aluno não
tenha nenhuma resposta, queremos faer o contrário, ou seja, retornar todos as respostas que foram
respondidas e as que não foram respondidas. Vamos verificar se existe alguma resposta que não foi
respondidaporumaluno:
SELECTr.idFROMrespostar
WHEREr.aluno_idISNULL;
Emptyset(0,00sec)
Nãoexisteexercíciosemresposta,entãovamosinserirumarespostasemassociaraumaluno:
INSERTINTOresposta(resposta_dada)VALUES('xvale15.');
QueryOK,1rowaffected(0,01sec)
Severificarmosnovamenteseexisteumarespostaquenãofoirespondidaporumaluno:
SELECTr.idFROMrespostar
WHEREr.aluno_idISNULL;
+----+
|id|
+----+
|28|
+----+
Agoraexisteumarespostaquenãofoiassociadaaumaluno.Damesmaformaqueutilizamosum
JOIN diferente para pegar todos os dados da tabela da esquerda (LEFT) mesmo que não tenha
associaçãocomatabelaqueestásendojuntada,existetambémoJOINquefaráoprocedimento,porém
paraatabeladadireita(RIGHT),queéotaldoRIGHTJOIN:
12.1RIGHTJOIN
109
SELECTa.nome,r.resposta_dada
FROMalunoa
RIGHTJOINrespostarONr.aluno_id=a.id;
+-----------------+----------------------------------------------------------------------------------+
|nome|resposta_dada
|
+-----------------+----------------------------------------------------------------------------------+
|JoãodaSilva|umaselecao
|
|JoãodaSilva|ixi,naosei
|
|JoãodaSilva|alterardados
|
|JoãodaSilva|eskecerowhereealterartudo
|
|JoãodaSilva|apagarcoisas
|
|JoãodaSilva|tbnaopodeeskecerowhere
|
|JoãodaSilva|inserirdados
|
|FredericoJosé|buscardados
|
|FredericoJosé|selectcamposfromtabela
|
|FredericoJosé|alterarcoisas
|
|FredericoJosé|ixi,naosei
|
|AlbertoSantos|tempoprafazeralgo
|
|AlbertoSantos|1a4semanas
|
|AlbertoSantos|melhoriadoprocesso
|
|AlbertoSantos|tododia
|
|AlbertoSantos|reuniaodestatus
|
|AlbertoSantos|tododia
|
|AlbertoSantos|oquadrobranco
|
|AlbertoSantos|ummetodoagil
|
|AlbertoSantos|temvariosoutros
|
|RenataAlonso|ehainternet
|
|RenataAlonso|browserfazrequisicao,servidormandaresposta
|
|RenataAlonso|ehoservidorquelidacomhttp
|
|RenataAlonso|naosei
|
|RenataAlonso|bancodedados!
|
|RenataAlonso|ehcolocaraappnainternet
|
|RenataAlonso|dependedatecnologia,masgeralmenteehlevarpraumservidorquetanainternet
110
12.1RIGHTJOIN
|
|NULL|xvale15
|
+-----------------+----------------------------------------------------------------------------------+
Observequefoiretornadaarespostaemquenãofoirespondidaporumaluno.
Quandoutilizamosapenaso JOINsignificaquequeremosretornartodososregistrosquetenham
umaassociação,ouseja,queexistatantonatabeladaesquerdaquantonatabeladadireita,esse JOIN
tambéméconhecidocomoINNERJOIN.VamosverificaroresultadoutilizandooINNERJOIN:
SELECTa.nome,COUNT(r.id)ASrespostas
FROMalunoa
INNERJOINrespostarONr.aluno_id=a.id
GROUPBYa.nome;
+-----------------+-----------+
|nome|respostas|
+-----------------+-----------+
|AlbertoSantos|9|
|FredericoJosé|4|
|JoãodaSilva|7|
|RenataAlonso|7|
+-----------------+-----------+
Ele trouxe apenas os alunos que possuem ao menos uma resposta, ou seja, que exista a associação
entreatabeladaesquerda(aluno)eatabeladadireita(resposta).
12.2JOINOUSUBQUERY?
Nocapítuloanteriortivemosquefazerumaquerypararetornartodososalunoseaquantidadede
matrículas, porém utilizamos subqueries para resolver o nosso problema. Podemos também, construir
essaqueryapenascomJOINs.Vamostentar:
SELECTa.nome,COUNT(m.id)ASqtd_matriculaFROMalunoa
JOINmatriculamONm.aluno_id=a.id
GROUPBYa.nome;
+-----------------+---------------+
|nome|qtd_matricula|
+-----------------+---------------+
|AlbertoSantos|2|
|FredericoJosé|3|
|JoãodaSilva|2|
|ManoelSantos|2|
|PaulaSoares|1|
|PauloJosé|1|
|RenataAlonso|2|
|RenataFerreira|1|
+-----------------+---------------+
Aparentemente não retornou os alunos que não possuem matrícula, porém utilizamos apenas o
JOIN,ouseja,o INNERJOIN.Aoinvésdo INNERJOINqueretornaapenasseexistiraassociação
entre as tabelas da esquerda (aluno) e a da direita(matricula), nós queremos retornar todos os
12.2JOINOUSUBQUERY?
111
alunos,mesmoquenãopossuammatrículas,ouseja,tabeladaesquerda.Entãovamostentaragoracom
oLEFTJOIN:
SELECTa.nome,COUNT(m.id)ASqtd_matriculaFROMalunoa
LEFTJOINmatriculamONm.aluno_id=a.id
GROUPBYa.nome;
+------------------+---------------+
|nome|qtd_matricula|
+------------------+---------------+
|AlbertoSantos|2|
|CarlosCunha|0|
|ClaudioSoares|0|
|CristaldoSantos|0|
|DaniloCunha|0|
|FredericoJosé|3|
|JoãodaSilva|2|
|JosedaSilva|0|
|ManoelSantos|2|
|OsmirFerreira|0|
|PaulaSoares|1|
|PaulodaSilva|0|
|PauloJosé|1|
|RenataAlonso|2|
|RenataFerreira|1|
|ZilmiraJosé|0|
+------------------+---------------+
Damesmaformaqueconseguimospegartodososalunoseaquantidadederespostasmesmoqueo
alunonãotenharespondidonenhumarespostautilizandoo LEFTJOINpoderíamostambémresolver
utilizandoumasubqueryparecidacomqualretornavatodososalunoseaquantidadedematrículas:
SELECTa.nome,
(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASrespostas
FROMalunoa;
+------------------+-----------+
|nome|respostas|
+------------------+-----------+
|JoãodaSilva|7|
|FredericoJosé|4|
|AlbertoSantos|9|
|RenataAlonso|7|
|PaulodaSilva|0|
|CarlosCunha|0|
|PauloJosé|0|
|ManoelSantos|0|
|RenataFerreira|0|
|PaulaSoares|0|
|JosedaSilva|0|
|DaniloCunha|0|
|ZilmiraJosé|0|
|CristaldoSantos|0|
|OsmirFerreira|0|
|ClaudioSoares|0|
+------------------+-----------+
Oresultadoéomesmo!Seoresultadoéomesmo,quandoeudevoutilizaroJOINouassubqueries?
Aparentementeasubqueryémaisenxutaemaisfácildeserescrita,porémosSGBDssempreterãoum
112
12.2JOINOUSUBQUERY?
desempenho melhor para JOIN em relação a subqueries, então prefira o uso de JOINs ao invés de
subquery.
Vamos tentar juntar as queries que fizemos agora pouco, porém em uma única query. Veja o
exemploemumaplanilha.
Figura12.1:Planilhaexemplo
Primeirovamosfazercomsubqueries.Entãocomeçaremosretornandoonomedoaluno:
SELECTa.nomeFROMalunoa;
Agoravamoscontartodasasrespostasetestaroresultado:
SELECTa.nome,
(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASqtd_respostas
FROMalunoa;
+------------------+---------------+
|nome|qtd_respostas|
+------------------+---------------+
|JoãodaSilva|7|
|FredericoJosé|4|
|AlbertoSantos|9|
|RenataAlonso|7|
|PaulodaSilva|0|
|CarlosCunha|0|
|PauloJosé|0|
|ManoelSantos|0|
|RenataFerreira|0|
|PaulaSoares|0|
|JosedaSilva|0|
|DaniloCunha|0|
|ZilmiraJosé|0|
|CristaldoSantos|0|
|OsmirFerreira|0|
|ClaudioSoares|0|
+------------------+---------------+
Porfim,vamoscontarasmatrículaseretornaracontagem:
SELECTa.nome,
(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASqtd_respostas,
(SELECTCOUNT(m.id)FROMmatriculamWHEREm.aluno_id=a.id)ASqtd_matriculas
FROMalunoa;
+------------------+---------------+----------------+
|nome|qtd_respostas|qtd_matriculas|
+------------------+---------------+----------------+
|JoãodaSilva|7|2|
|FredericoJosé|4|3|
|AlbertoSantos|9|2|
|RenataAlonso|7|2|
12.2JOINOUSUBQUERY?
113
|PaulodaSilva|0|0|
|CarlosCunha|0|0|
|PauloJosé|0|1|
|ManoelSantos|0|2|
|RenataFerreira|0|1|
|PaulaSoares|0|1|
|JosedaSilva|0|0|
|DaniloCunha|0|0|
|ZilmiraJosé|0|0|
|CristaldoSantos|0|0|
|OsmirFerreira|0|0|
|ClaudioSoares|0|0|
+------------------+---------------+----------------+
Conseguimosoresultadoesperadoutilizandoassubqueries,vamostentarcomo LEFTJOIN? Da
mesmaformaquefizemosanteriormente,começaremosretornandoosalunos:
SELECTa.nomeFROMalunoa;
Agoravamosjuntarastabelaalunocomastabelasrespostaematricula:
SELECTa.nome,r.idASqtd_respostas,
m.idASqtd_matriculas
FROMalunoa
LEFTJOINrespostarONr.aluno_id=a.id
LEFTJOINmatriculamONm.aluno_id=a.id;
+------------------+---------------+----------------+
|nome|qtd_respostas|qtd_matriculas|
+------------------+---------------+----------------+
|JoãodaSilva|1|1|
|JoãodaSilva|2|1|
|JoãodaSilva|3|1|
|JoãodaSilva|4|1|
|JoãodaSilva|5|1|
|JoãodaSilva|6|1|
|JoãodaSilva|7|1|
|FredericoJosé|8|2|
|FredericoJosé|9|2|
|FredericoJosé|10|2|
|FredericoJosé|11|2|
|AlbertoSantos|12|3|
|AlbertoSantos|13|3|
|AlbertoSantos|14|3|
|AlbertoSantos|15|3|
|AlbertoSantos|16|3|
|AlbertoSantos|17|3|
|AlbertoSantos|18|3|
|AlbertoSantos|19|3|
|AlbertoSantos|20|3|
|RenataAlonso|21|4|
|RenataAlonso|22|4|
|RenataAlonso|23|4|
|RenataAlonso|24|4|
|RenataAlonso|25|4|
|RenataAlonso|26|4|
|RenataAlonso|27|4|
|RenataAlonso|21|9|
|RenataAlonso|22|9|
|RenataAlonso|23|9|
|RenataAlonso|24|9|
114
12.2JOINOUSUBQUERY?
|RenataAlonso|25|9|
|RenataAlonso|26|9|
|RenataAlonso|27|9|
|JoãodaSilva|1|11|
|JoãodaSilva|2|11|
|JoãodaSilva|3|11|
|JoãodaSilva|4|11|
|JoãodaSilva|5|11|
|JoãodaSilva|6|11|
|JoãodaSilva|7|11|
|FredericoJosé|8|12|
|FredericoJosé|9|12|
|FredericoJosé|10|12|
|FredericoJosé|11|12|
|AlbertoSantos|12|13|
|AlbertoSantos|13|13|
|AlbertoSantos|14|13|
|AlbertoSantos|15|13|
|AlbertoSantos|16|13|
|AlbertoSantos|17|13|
|AlbertoSantos|18|13|
|AlbertoSantos|19|13|
|AlbertoSantos|20|13|
|FredericoJosé|8|14|
|FredericoJosé|9|14|
|FredericoJosé|10|14|
|FredericoJosé|11|14|
|PauloJosé|NULL|5|
|ManoelSantos|NULL|6|
|RenataFerreira|NULL|7|
|PaulaSoares|NULL|8|
|ManoelSantos|NULL|10|
|PaulodaSilva|NULL|NULL|
|CarlosCunha|NULL|NULL|
|JosedaSilva|NULL|NULL|
|DaniloCunha|NULL|NULL|
|ZilmiraJosé|NULL|NULL|
|CristaldoSantos|NULL|NULL|
|OsmirFerreira|NULL|NULL|
|ClaudioSoares|NULL|NULL|
+------------------+---------------+----------------+
Antes de contarmos as colunas de qtd_resposas e qtd_matricula, vamos analisar um pouco esse
resultado. Note que o aluno João da Silva retornou 14 vezes, parece que tem alguma coisa estranha.
VamospegaroiddoJoãodaSilvaevamosverificarosregistrosdelenatabela respostaenatabela
matricula:
SELECTa.idFROMalunoaWHEREa.nome='JoãodaSilva';
+----+
|id|
+----+
|1|
+----+
Agoravamosverificartodososregistrosdelenatabelaresposta:
SELECTr.idFROMrespostar
WHEREr.aluno_id=1;
12.2JOINOUSUBQUERY?
115
+----+
|id|
+----+
|1|
|2|
|3|
|4|
|5|
|6|
|7|
+----+
Foramretornados7registros,agoravamosverificarnatabelamatricula:
SELECTm.idFROMmatriculam
WHEREm.aluno_id=1;
+----+
|id|
+----+
|1|
|11|
+----+
Foramretornados2registros.Seanalisarmosumpoucoessestrêsresultadoschegamosaosseguintes
números:
aluno=1respostas=7matrículas=2
Vamos executar novamente a nossa query que retorna o aluno e a contagem de respostas e
matrículas:
SELECTa.nome,r.idASqtd_respostas,
m.idASqtd_matriculas
FROMalunoa
LEFTJOINrespostarONr.aluno_id=a.id
LEFTJOINmatriculamONm.aluno_id=a.id;
+------------------+---------------+----------------+
|nome|qtd_respostas|qtd_matriculas|
+------------------+---------------+----------------+
|JoãodaSilva|1|1|
|JoãodaSilva|2|1|
|JoãodaSilva|3|1|
|JoãodaSilva|4|1|
|JoãodaSilva|5|1|
|JoãodaSilva|6|1|
|JoãodaSilva|7|1|
|FredericoJosé|8|2|
|FredericoJosé|9|2|
|FredericoJosé|10|2|
|FredericoJosé|11|2|
|AlbertoSantos|12|3|
|AlbertoSantos|13|3|
|AlbertoSantos|14|3|
|AlbertoSantos|15|3|
|AlbertoSantos|16|3|
|AlbertoSantos|17|3|
|AlbertoSantos|18|3|
|AlbertoSantos|19|3|
116
12.2JOINOUSUBQUERY?
|AlbertoSantos|20|3|
|RenataAlonso|21|4|
|RenataAlonso|22|4|
|RenataAlonso|23|4|
|RenataAlonso|24|4|
|RenataAlonso|25|4|
|RenataAlonso|26|4|
|RenataAlonso|27|4|
|RenataAlonso|21|9|
|RenataAlonso|22|9|
|RenataAlonso|23|9|
|RenataAlonso|24|9|
|RenataAlonso|25|9|
|RenataAlonso|26|9|
|RenataAlonso|27|9|
|JoãodaSilva|1|11|
|JoãodaSilva|2|11|
|JoãodaSilva|3|11|
|JoãodaSilva|4|11|
|JoãodaSilva|5|11|
|JoãodaSilva|6|11|
|JoãodaSilva|7|11|
|FredericoJosé|8|12|
|FredericoJosé|9|12|
|FredericoJosé|10|12|
|FredericoJosé|11|12|
|AlbertoSantos|12|13|
|AlbertoSantos|13|13|
|AlbertoSantos|14|13|
|AlbertoSantos|15|13|
|AlbertoSantos|16|13|
|AlbertoSantos|17|13|
|AlbertoSantos|18|13|
|AlbertoSantos|19|13|
|AlbertoSantos|20|13|
|FredericoJosé|8|14|
|FredericoJosé|9|14|
|FredericoJosé|10|14|
|FredericoJosé|11|14|
|PauloJosé|NULL|5|
|ManoelSantos|NULL|6|
|RenataFerreira|NULL|7|
|PaulaSoares|NULL|8|
|ManoelSantos|NULL|10|
|PaulodaSilva|NULL|NULL|
|CarlosCunha|NULL|NULL|
|JosedaSilva|NULL|NULL|
|DaniloCunha|NULL|NULL|
|ZilmiraJosé|NULL|NULL|
|CristaldoSantos|NULL|NULL|
|OsmirFerreira|NULL|NULL|
|ClaudioSoares|NULL|NULL|
+------------------+---------------+----------------+
Reparequeanossaqueryassociandoumaluno1,comaresposta1ematrícula1,omesmoaluno1,
comresposta1ematrícula11eassimsucessivamente...Issosignificaqueessaqueryestámultiplicandoo
aluno(1) x respostas(7) x matrículas(2)... Com certeza essa contagem não funcionará! Precisamos de
resultados distintos, ou seja, iremos utilizar o DISTINCT para evitar esse problema. Agora podemos
contarasrespostaseasmatrículas:
12.2JOINOUSUBQUERY?
117
SELECTa.nome,COUNT(DISTINCTr.id)ASqtd_respostas,
COUNT(DISTINCTm.id)ASqtd_matriculas
FROMalunoa
LEFTJOINrespostarONr.aluno_id=a.id
LEFTJOINmatriculamONm.aluno_id=a.id
GROUPBYa.nome;
+------------------+---------------+----------------+
|nome|qtd_respostas|qtd_matriculas|
+------------------+---------------+----------------+
|AlbertoSantos|9|2|
|CarlosCunha|0|0|
|ClaudioSoares|0|0|
|CristaldoSantos|0|0|
|DaniloCunha|0|0|
|FredericoJosé|4|3|
|JoãodaSilva|7|2|
|JosedaSilva|0|0|
|ManoelSantos|0|2|
|OsmirFerreira|0|0|
|PaulaSoares|0|1|
|PaulodaSilva|0|0|
|PauloJosé|0|1|
|RenataAlonso|7|2|
|RenataFerreira|0|1|
|ZilmiraJosé|0|0|
+------------------+---------------+----------------+
EsenãoadicionássemosainstruçãoDISTINCT?Oqueaconteceria?Vamostestar:
SELECTa.nome,COUNT(r.id)ASqtd_respostas,
COUNT(m.id)ASqtd_matriculas
FROMalunoa
LEFTJOINrespostarONr.aluno_id=a.id
LEFTJOINmatriculamONm.aluno_id=a.id
GROUPBYa.nome;
+------------------+---------------+----------------+
|nome|qtd_respostas|qtd_matriculas|
+------------------+---------------+----------------+
|AlbertoSantos|18|18|
|CarlosCunha|0|0|
|ClaudioSoares|0|0|
|CristaldoSantos|0|0|
|DaniloCunha|0|0|
|FredericoJosé|12|12|
|JoãodaSilva|14|14|
|JosedaSilva|0|0|
|ManoelSantos|0|2|
|OsmirFerreira|0|0|
|PaulaSoares|0|1|
|PaulodaSilva|0|0|
|PauloJosé|0|1|
|RenataAlonso|14|14|
|RenataFerreira|0|1|
|ZilmiraJosé|0|0|
+------------------+---------------+----------------+
Oresultadoalémdeserbemmaiorqueoesperado,repetenasduascolunas,poisestáacontecendo
aqueleproblemadamultiplicaçãodaslinhas!Percebaquesóconseguimosverificardeumaformarápida
oproblemaqueaconteceu,poisfizemosaquerypasso-a-passo,verificandocadaresultadoe,aomesmo
118
12.2JOINOUSUBQUERY?
tempo,corringoosproblemasquesurgiam.
12.3RESUMINDO
Nestecapítuloaprendemoscomoutilizarosdiferentestiposde JOINs,comoporexemploo LEFT
JOINqueretornaosregistrosdatabelaaesquerdaeo RIGHTJOINqueretornaosdadireitamesmo
quenãotenhamassociações.VimostambémqueoJOINtambéméconhecidocomoINNERJOINque
retorna apenas os registros que estão associados. Além disso, vimos que algumas queries podem ser
resolvidas utilizando subqueries ou LEFT/RIGHT JOIN, porém é importante lembrar que os SGBDs
sempre terão melhor desempenho com o os JOINs, por isso é recomendado que utilize os JOINs.
Vamosparaosexercícios?
EXERCÍCIOS
1. Exibatodososalunosesuaspossíveisrespostas.Exibatodososalunos,mesmoqueelesnãotenham
respondidonenhumapergunta.
2. ExibaagoratodososalunosesuaspossíveisrespostasparaoexercíciocomID=1.Exibatodosos
alunosmesmoqueelenãotenharespondidooexercício.
Lembre-sedeusaracondiçãonoJOIN.
1. QualadiferençaentreoJOINconvencional(muitasvezeschamadotambémdeINNERJOIN)para
oLEFTJOIN?
12.3RESUMINDO
119
CAPÍTULO13
MUITOSALUNOSEOLIMIT
Precisamosdeumrelatórioqueretornetodososalunos,algocomoselecionaronomedetodoseles,com
umSELECTsimples:
SELECTa.nomeFROMalunoa;
+------------------+
|nome|
+------------------+
|JoãodaSilva|
|FredericoJosé|
|AlbertoSantos|
|RenataAlonso|
|PaulodaSilva|
|CarlosCunha|
|PauloJosé|
|ManoelSantos|
|RenataFerreira|
|PaulaSoares|
|JosedaSilva|
|DaniloCunha|
|ZilmiraJosé|
|CristaldoSantos|
|OsmirFerreira|
|ClaudioSoares|
+------------------+
Paramelhoraroresultadopodemosordernaraqueryporordemalfabéticadonome:
SELECTa.nomeFROMalunoaORDERBYa.nome;
+------------------+
|nome|
+------------------+
|AlbertoSantos|
|CarlosCunha|
|ClaudioSoares|
|CristaldoSantos|
|DaniloCunha|
|FredericoJosé|
|JoãodaSilva|
|JosedaSilva|
|ManoelSantos|
|OsmirFerreira|
|PaulaSoares|
|PaulodaSilva|
|PauloJosé|
|RenataAlonso|
|RenataFerreira|
|ZilmiraJosé|
120
13MUITOSALUNOSEOLIMIT
+------------------+
Vamosverificaragoraquantosalunosestãocadastrados:
SELECTcount(*)FROMaluno;
+----------+
|count(*)|
+----------+
|16|
+----------+
Como podemos ver, é uma quantidade relativamente baixa, pois quando estamos trabalhando em
umaaplicaçãoreal,geralmenteovolumedeinformaçõesémuitomaior.
Nofacebook,porexemplo,quantosamigosvocêtem?Quandovocêentranofacebook,aparecetodas
as atualizações dos seus amigos de uma vez? Todas as milhares de atualizações de uma única vez?
Imagine a loucura que é trazer milhares de dados de uma única vez para você ver apenas 5, 10
notificações.Provavelmentevaiaparecendoaospoucos,certo?Entãoquetalmostrarmososalunosaos
poucostambém?Ouseja,fazermosumapaginaçãononossorelatório,algocomo5alunos"porpágina".
Mas como podemos fazer isso? No MySQL, podemos limitar em 5 a quantidade de registros que
desejamosretornar:
SELECTa.nomeFROMalunoa
ORDERBYa.nome
LIMIT5;
+------------------+
|nome|
+------------------+
|AlbertoSantos|
|CarlosCunha|
|ClaudioSoares|
|CristaldoSantos|
|DaniloCunha|
+------------------+
Nessecasoretornamososprimeiros5alunosemordemalfabética.Oatodelimitaréextremamente
importanteamedidaqueosdadoscrescem.Sevocêtemmilmensagensantigas,nãovaiquererveras
mildeumavezsó,tragasomenteas10primeirase,setiverinteresse,mais10,mais10etc.
13.1 LIMITANDO E BUSCANDO A PARTIR DE UMA QUANTIDADE
ESPECÍFICA
Agora vamos pegar os próximos 5 alunos, isto é, senhor MySQL, ignore os 5 primeiros, e depois
pegueparamimospróximos5:
SELECTa.nomeFROMalunoa
ORDERBYa.nome
LIMIT5,5;
+-----------------+
|nome|
13.1LIMITANDOEBUSCANDOAPARTIRDEUMAQUANTIDADEESPECÍFICA
121
+-----------------+
|FredericoJosé|
|JoãodaSilva|
|JosedaSilva|
|ManoelSantos|
|OsmirFerreira|
+-----------------+
LIMIT 5 ? LIMIT 5,5 ?
utilizamos
o
Parece um pouco estranho, o que será que isso significa? Quando
funciona
da
seguinte
maneira:
LIMIT
linha_inicial,qtd_de_linhas_para_avançar, ou seja, quando fizemos LIMIT 5, informamos ao
MySQLqueavançe5linhasapenas,poisporpadrãoeleiniciarápelaprimeiralinha,chamadadelinha
LIMIT
0.SefizéssemosLIMIT0,5,porexemplo:
SELECTa.nomeFROMalunoa
ORDERBYa.nome
LIMIT0,5;
+------------------+
|nome|
+------------------+
|AlbertoSantos|
|CarlosCunha|
|ClaudioSoares|
|CristaldoSantos|
|DaniloCunha|
+------------------+
PercebaqueoresultadoéomesmoqueLIMIT5!SepedimosLIMIT5,10oqueelenostrás?
SELECTa.nomeFROMalunoa
ORDERBYa.nome
LIMIT5,10;
+-----------------+
|nome|
+-----------------+
|FredericoJosé|
|JoãodaSilva|
|JosedaSilva|
|ManoelSantos|
|OsmirFerreira|
|PaulaSoares|
|PaulodaSilva|
|PauloJosé|
|RenataAlonso|
|RenataFerreira|
+-----------------+
O resultado iniciará após a linha 5, ou seja, linha 6 e avançará 10 linhas. Vamos demonstrar os
exemplostodosdeumaúnicavez:
Todososalunos:
SELECTa.nomeFROMalunoa;
+------------------+
|nome|
122
13.1LIMITANDOEBUSCANDOAPARTIRDEUMAQUANTIDADEESPECÍFICA
+------------------+
|AlbertoSantos|
|CarlosCunha|
|ClaudioSoares|
|CristaldoSantos|
|DaniloCunha|
|FredericoJosé|
|JoãodaSilva|
|JosedaSilva|
|ManoelSantos|
|OsmirFerreira|
|PaulaSoares|
|PaulodaSilva|
|PauloJosé|
|RenataAlonso|
|RenataFerreira|
|ZilmiraJosé|
+------------------+
Pegandoos5primeiros:
SELECTa.nomeFROMalunoa
ORDERBYa.nome
LIMIT5;
+------------------+
|nome|
+------------------+
|AlbertoSantos|
|CarlosCunha|
|ClaudioSoares|
|CristaldoSantos|
|DaniloCunha|
+------------------+
Ignorandoos5primeiros,pegandoospróximos5alunos:
SELECTa.nomeFROMalunoa
ORDERBYa.nome
LIMIT5,5;
+-----------------+
|nome|
+-----------------+
|FredericoJosé|
|JoãodaSilva|
|JosedaSilva|
|ManoelSantos|
|OsmirFerreira|
+-----------------+
13.2RESUMINDO
Nesse capítulo vimos como nem sempre retornar todos os registros das tabelas são necessários,
algumasvezes,precisamosfiltraraquantidadedelinhas,poisemumaaplicaçãoreal,podemoslidarcom
uma quantidade bem grande de dados. Justamente por esse caso, podemos limitar as nossas queries
utilizandoainstruçãoLIMIT.Vamosparaosexercícios?
13.2RESUMINDO
123
EXERCÍCIOS
1. Escrevaumaqueryquetragaapenasosdoisprimeirosalunosdatabela.
2. EscrevaumaSQLquedevolvaos3primeirosalunosqueoe-mailterminecomodomínio".com".
3. Devolvaos2primeirosalunosqueoe-mailterminecom".com",ordenandopornome.
4. DevolvatodososalunosquetenhamSilvaemalgumlugarnoseunome.
124
13.2RESUMINDO
Download