sql e modelagem com bando de dados · bem vindo ao curso de banco de dados e modelagem do alura....

128

Upload: dinhdang

Post on 08-Nov-2018

220 views

Category:

Documents


1 download

TRANSCRIPT

1112

3556689

1010111314192121

23242525262727

29

Sumário

1Objetivosdocurso1.1Oqueérealmenteimportante?1.2Sobreosexercícios1.3Tirandodúvidaseindoalém

2Meuproblema2.1Criandoonossobancodedados2.2Começandoumcadernonovo:criandoobanco2.3Opadrãoutilizadonestecurso2.4Atabeladecompras2.5Conferindoaexistênciadeumatabela2.6Inserindoregistrosnobancodedados2.7Selecãosimples2.8Aformataçãodenúmerosdecimais2.9Achaveprimária2.10Recriandoatabeladozero2.11Consultascomfiltros2.12Modelandotabelas2.13Resumindo2.14Exercícios

3Atualizandoeexcluindodados3.1UtilizandooUPDATE3.2Atualizandováriascolunasaomesmotempo3.3Utilizandoumacolunacomoreferênciaparaoutracoluna3.4UtilizandooDELETE3.5CuidadoscomoDELETEeUPDATE3.6Resumindo

4Alterandoerestringindooformatodenossastabelas

SumárioAlura

3030313133

353741

42475050585960

626570

7279

818386

8793

94101

103109111119

120121123

4.1Restringindoosnulos4.2AdicionandoConstraints4.3ValoresDefault4.4Evoluçãodobanco4.5Resumindo

5Agrupandodadosefazendoconsultasmaisinteligentes5.1Ordenandoosresultados5.2Resumindo

6Juntandodadosdeváriastabelas6.1Normalizandonossomodelo6.2OnetoMany/ManytoOne6.3FOREIGNKEY6.4Determinandovaloresfixosnatabela6.5ServerSQLModes6.6Resumindo

7AlunossemmatrículaeoExists7.1Subqueries7.2Resumindo

8AgrupandodadoscomGROUPBY8.1Resumindo

9FiltrandoagregaçõeseoHAVING9.1CondiçõescomHAVING9.2Resumindo

10MúltiplosvaloresnacondiçãoeoIN10.1Resumindo

11Sub-queries11.1Resumindo

12EntendendooLEFTJOIN12.1RIGHTJOIN12.2JOINouSUBQUERY?12.3Resumindo

13MuitosalunoseoLIMIT13.1Limitandoebuscandoapartirdeumaquantidadeespecífica13.2Resumindo

AluraSumário

Versão:19.4.23

SumárioAlura

CAPÍTULO1

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ãoSQLquetodoselesaceitam.ParaacompanharaapostilasugerimosoMySQL,paraquepassepelosmesmosdesafiosesoluçõesqueencontramosnessecurso.Apósaprenderabaseatodoseles,estudardetalhesespecíficosdecadabancopassaaserbemmaissimples.

salientarqueousodealgunsconceitos,comoasvantagensedesvantagensdamodelagem,sãoentendidosporcompletodepoisdeumtempodeprática,alémdemudaremcomotempo.

mostrarquedecorarcomandosnãoéimportante.

Muitoslivros,aopassardoscapítulos,mencionamtodososdetalhesdeumaferramentajuntamentecomseus princípios básicos. Isso acaba criando muita confusão, em especial porque o estudante nãoconseguedistinguirexatamenteoqueéprimordialaprenderno início,daquiloquepodeserestudadomaisadiante.Essetipodeinformaçãoseráadquiridacomotempo,enãoénecessárionoinício:essetipode filtragem já fizemos e aplicamos aqui para você aprender a medida certa, e se aprofundar nomomentoquefizersentidocontinuandoseusestudosemoutroscursosnossosnoAlura.

Nestecurso,separamosessasinformaçõesemquadrosespeciais, jáquesãoinformaçõesextras.Ouentão, apenas citamos num exercício e deixamos para o leitor procurar informações se for de seuinteresse.

Porfim,faltamencionaralgosobreaprática,quedevesertratadaseriamente:todososexercíciossãomuitoimportantes.

Paraaquelesqueestãofazendoocursoonlinenowww.alura.com.br,recomendamosestudarememcasaaquiloquefoivistoduranteaaula,tentandoresolverosexercíciosedesafiosapresentados.

OBJETIVOSDOCURSO

1.1OQUEÉREALMENTEIMPORTANTE?

Ocurso

1.2SOBREOSEXERCÍCIOS

1OBJETIVOSDOCURSO 1

Os exercícios do curso variam de práticos até pesquisas na Internet, ou mesmo consultas sobreassuntosavançadosemdeterminadostópicosparaincitaracuriosidadedoaprendiznatecnologia.

Paratirardúvidasdosexercícios,oudedesenvolvimentoemgeral,recomendamosofórumdoGUJ(http://www.guj.com.br/).OGUJfoifundadopordesenvolvedoresdoAluraedaCaelum,ehojecontacommaisdeummilhãodemensagens.

Quandoterminaressaaventuraesperoencontrá-lonoAlura,ondepoderácontinuarcomdiversosoutroscursosnaáreadedesenvolvimentoetecnologia.

http://www.alura.com.br/

Seoquevocêestábuscandosãolivrosdeapoio,sugerimosconheceraeditoraCasadoCódigo:

http://www.casadocodigo.com.br

BemvindoaocursodeBancodeDadoseModelagemdoAlura.Nessaapostilavocêvaiencontraratranscriçãodocursoquepodeserencontradoemsuaversãomaisrecenteemwww.alura.com.br.

Comodesenvolvedorpasseimaisde15anosprocurandoexemplosreaisdeensinoparapoderajudarmeus colegas desenvolvedores que estagiavam naminha equipe, e junto da equipe de instrutores doAlura,agrupeiessesexemplosnessecurso.

Nosso foco aqui émostrar o que é umbanco de dados relacional, comopensar amodelagemdenossosdadosecomocriar,inserir,atualizarepesquisarinformaçõesemumabasededados.Tudoissousandoexemplosdomundoreal,nadadeabstraçõesgenéricasquenoslevamaquestionarousorealdatecnologianodiaadia.

ComousamosoMySQLcomosoftwaredebancodedadosnestecurso,vocêpoderáexecutartodososexemplosnasuamáquinaemcasamesmo.

1.3TIRANDODÚVIDASEINDOALÉM

SOBREOCURSOBANCODEDADOSEMODELAGEM

2 1.3TIRANDODÚVIDASEINDOALÉM

CAPÍTULO2

Chegouo fimdomêsenão seidireitoondemeudinheiro foiparar,nomeubolsonãoestá.Nemnacontabancária.Éumproblemacomum,ecomopodemosfazerparacontrolarosgastos?

Amaneiramais simples de entender onde o dinheiro está indo é anotar todos os nossos gastos,sendoumadasmaistradicionaiseantigasescrevê-losemumcaderninho.Porexemplodia05/01/2016gasteiR$20emumaLanchonete,escrevoissobonitinhoemumalinha.

R$2005/01/2016Lanchonete

Tiveumoutrogastodelanchonetenodia06/01/2016novalordeR$15,voueanotonovamente:

R$2005/01/2016LanchoneteR$1506/01/2016Lanchonete

Porfim,minhaesposacomprouumguarda-roupaproquartoeeleaindanãochegou,entãoanotei:

R$2005/01/2016LanchoneteR$1506/01/2016LanchoneteR$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?Éadatadacompra?Eaterceira,éolugarquecompreiousãoanotaçõesrelativasaosgastos?Reparaquesemdarnomeascolunas,minhascomprasficamconfusas,atabelaficaestranha,epossocadavezpreenchercomalgoqueachoquedevo,aoinvésdetercertezadoquecadacamposignifica.Comoidentificarcadaumdeles? Vamos dar um nome aos três campos que compõem a minha tabela: o valor, a data e asobservaçõ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++---------+------------+--------------+--------------+

MEUPROBLEMA

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

Paramontar essa tabela emum caderno eu tenhoque ser capaz de traçar linhas bonitinhas e eu,pessoalmente,souhorrívelparaisso.Nomundotecnológicodehojeemdia,anotaríamosessatabelaemumaplanilhaeletrônicacomooExcel:

Figura2.1:Planilhaexemplo

Porém, alémde guardar as informações, eu queromanter umhistórico, tirarmédia semestral oumensal, saberquantoqueeugasteicomlanchoneteoumercadoduranteumperíodoerealizar tarefasfuturas como relatórios complexos. O Excel é uma ferramenta bem poderosa e flexível, porém,dependendodaquantidadederegistros,amanipulaçãodosdadoscomeçaaficarumpoucocomplicadaemumaplanilhaeletrônica.

Parafacilitaronossotrabalho,existemsoftwaresparaarmazenar,guardarosdados.Essesbancosdedados são sistemas que gerenciam desde a forma de armazenar a informação como também comoconsultá-lademaneiraeficiente.

OschamadosSistemadeGerenciamentodeBancodeDados(SGBD),nospermitemrealizartodasessastarefassemnospreocuparmoscomocaderninhoouacaneta.Claroquesevamosnoscomunicarcomalguémque vai gerenciar nossos dados precisamos falar uma língua comum, conversar emumalinguagempadrãoqueváriosbancosutilizem.

Umpadrãoquesurgiuparaacessarmosepesquisarmosdadosarmazenadoseestruturadosdeumaforma específica é uma linguagem de consultas estruturadas, Structured Query Language, ousimplesmenteSQL.Paraconseguirmosutilizaressalinguagem,precisamosinstalarumSGBDqueseráonossoservidordebancodedados,comoporexemplooMySQL,OracleouSQLServer.

UMBANCOPARAOSDADOS

4 2MEUPROBLEMA

NestecursoutilizaremosoMySQL,umSGBDgratuitoquepodeserinstaladoseguindoasmaneirastradicionaisdecadasistemaoperacional.Porexemplo,noWindows,oprocessoérealizadoatravésdodownloaddeumarquivo.msi,enquantonoLinux(versõesbaseadasemDebian),podeserfeitoatravésde um simples comando apt-get, e noMacOS pode ser instalado com um pacote .dmg. Baixe o seuinstaladoremhttp://dev.mysql.com/downloads/mysql/

Durante todo o curso usaremos o terminal do MySQL. Existe também a interface gráfica doWorkbenchdoMySQL,quenãoutilizaremos.Aoutilizarmosoterminalnãonospreocupamoscomoaprendizadodemaisumaferramentaqueéopcional,podendonosconcentrarnoSQL,queéoobjetivodestecurso.

Abraoterminaldoseusistemaoperacional.NoWindows,digitecmdnoExecutar.NoMaceLinux,abraoterminal.Nele,vamosentrarnoMySQLusandoousuárioroot:

mysql-uroot-p

O-u indicaousuárioroot, e o-p é porquedigitaremos a senha.Use a senhaquedefiniudurantea instalaçãodoMySQL,notequeporpadrãoasenhapodeserembrancoe,nessecaso,bastapressionarenter.

AgoraqueestamosconectadosaoMySQLprecisamosdizeraelequequeremosumcadernonovo.Começaremos com um que gerenciará todos os dados relativos aos nossos gastos, permitindo quecontrolemos melhor nossos gastos, portanto quero um banco de dados chamado ControleDeGastos,podeparecerestranho,masnãoexisteumpadrãoparanomearumbancodedados,porissoutilizamosopadrãoCamelCase.ParacriarumbancodedadosbastamandaroMySQLcriarumbanco:

mysql>CREATEDATABASE

Masqualonomemesmo?ControleDeGastos?Então:

mysql>CREATEDATABASEControleDeGastos

DouentereoMySQLficaesperandomaisinformações:

mysql>CREATEDATABASEControleDeGastos->

AcontecequeemgeralprecisamosnotificaroMySQLqueocomandoquedesejamosjáfoidigitadoporcompleto.Parafazerissousamosocaracterepontoevírgula(;):

mysql>CREATEDATABASEControleDeGastos->;QueryOK,1rowaffected(0.01sec)

2.1CRIANDOONOSSOBANCODEDADOS

2.2COMEÇANDOUMCADERNONOVO:CRIANDOOBANCO

2.1CRIANDOONOSSOBANCODEDADOS 5

Agorasim,elepercebeuqueacabamosnossocomando(quecomeçounaprimeiralinha)eindicouqueopedido(aquery)foiexecutadacomsucesso(OK),demorou0.01segundoegerou1resultado(1rowaffected).

Damesmamaneiraquecriamosessebanco,nãoéumaregra,masécomumterumbancodedadosparacadaprojeto.Porexemplo:

mysql>CREATEDATABASEcaelum->;QueryOK,1rowaffected(0.01sec)

mysql>CREATEDATABASEalura->;QueryOK,1rowaffected(0.01sec)

mysql>CREATEDATABASEcasadocodigo->;QueryOK,1rowaffected(0.01sec)

Agoraquetenhodiversosbancos,comopossodizeraoMySQLquequeremosusaraqueleprimeirode todos?Querodizerpara ele,por favor,use o banco chamadoControleDeGastos.Usar em inglês, éUSE,portanto:

mysql>USEControleDeGastos;Databasechanged

Reparequeaspalavrasquesãodeminhaautoria,minhaescolha,deixamosemminúsculo,enquantoas palavras que são específicas da linguagem SQL deixamos emmaiúsculo. Esse é um padrão comoexistem diversos outros padrões. Adotamos este para que a qualquer momento que você veja umcomandoSQLnestecurso, identifiqueoqueépalavraimportante(palavrachave)doSQLeoqueéonomede umbancode dados, uma tabela, uma coluna ou qualquer outro tipo de palavra que é umaescolhalivreminha,comousuáriodobanco.

Agoraque já tenhoumcaderno, jáestou loucoparaescreveromeuprimeirogasto,que foinumalanchonete.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

2.3OPADRÃOUTILIZADONESTECURSO

2.4ATABELADECOMPRAS

6 2.3OPADRÃOUTILIZADONESTECURSO

bancosabequeexisteumatabeladevalor,dataeobservações?Aliás,atémesmoquandocomeceicomomeucaderno,fuieu,Guilherme,quetivequedesenharatabelacomsuastrêscoluninhasemcadaumadaspáginasqueeuquiscolocarseusdados.

Então vamos fazer amesma coisa comobanco.Vamosdizer a ele quequeremos teruma tabela:descrevemosaestruturadelaatravésdeumcomandoSQL.Nessecomandodevemosdizerquaissãooscampos(valor,dataeobservações)queserãoutilizados,paraqueelesepareoespaçoespecíficoparacadaum deles toda vez que inserimos uma nova compra, toda vez que registramos um novo dado (umregistronovo).

Paracriarumatabela,novamentefalamosinglês,porfavorsenhorMySQL,crieumatabela(CREATETABLE)chamadacompras:

mysql>CREATETABLEcompras;ERROR1113(42000):Atablemusthaveatleast1column

Comoassim erro 1113?Logo apóso códigodo erro, o banconos informouque toda tabeladeveconterpelomenosumacoluna.Verdade,nãofaleiascolunasquequeriacriar.Vamosolharnovamentenossasinformaçõ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 pontodecimal,dataéumadata,observaçõesqueéumtextolivreerecebidaqueéousimounão.

Portanto vamos dizer ao banco que queremos esses 4 campos na nossa tabela, com uma únicaressalva, que para evitar problemas de encoding usamos sempre nomes que usam caracteres simples,nadadeacentoparaosnomesdenossastabelasecolunas:

mysql>CREATETABLEcompras(valor,data,observacoes,recebida);ERROR1064(42000):YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear'data,observacoes,recebida)'atline2

Aindanão foidessavez.Reparaqueoerro1064 indicaumerrodesintaxenocomandoCREATETABLE.Naverdadeobancodedadosestáinteressadoemsaberqualotipodecadacampoejustamentepor issodevemos explicar campo a campoqual o tipode cada colunanossa.A coluna, campo,valorrecebeumvalorcompossí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 decaracteres, por issoVARCHAR. Assim como no caso do número decimal, devemos falar o tamanhomáximodessacoluna.Nonossocasoserãoaté255caracteres:

observacoesVARCHAR(255),

Porfimchegamosacolunarecebida,quedeveserrepresentadacomvaloresdotipoverdadeiro oufalso,algocomosimounão.Nopadrãodebancorelacionaisnãoexisteumcampoqueaceiteosvaloresverdadeiro e falso, umcampobooleano.O que fazemos então é utilizar um campo numérico com osvalores0pararepresentaronegativoe1paraocasopositivo.Umacolunacomumnumerozinho,umTINYINT:

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.

Masvocêconfiaemtudoquetodomundofala?Vamosverserealmenteatabelafoicriadaemnossobancodedados.Sevocêpossuisseumcaderninhodetabelas,oqueeupoderiafazerparapedirparamemostrarsuatabeladecompras?Porfavor,medescrevesuatabeladecompras?

mysql>desccompras;+-------------+---------------+------+-----+---------+-------+|Field|Type|Null|Key|Default|Extra|+-------------+---------------+------+-----+---------+-------+

2.5CONFERINDOAEXISTÊNCIADEUMATABELA

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).Cadacampoédeumtipodiferente(decimal,date,varcharetinyint)epossuiatémesmoinformaçõesextrasqueutilizaremosnodecorrerdocurso!

Chegouahoradeusarnossatabela:queremosinserirdados,entãocomeçamospedindoparainseriralgoemcompras:

INSERTINTOcompras

Nãovamoscometeromesmoerrodaúltimavez.Sedesejamosfazeralgocomos4camposdatabela,temos que falar os valores que desejamos adicionar. Por exemplo, vamos pegar as informações daprimeiralinhadaplanilha:

Figura2.2:Planilhaexemplo

Então nós temos uma compra com valor 20, observação Lanchonete, data 05/01/2016, e que foirecebidacomsucesso(1):

mysql>INSERTINTOcomprasVALUES(20,'Lanchonete','05/01/2016',1);ERROR1292(22007):Incorrectdatevalue:'Lanchonete'forcolumn'data'atrow1

Parece que nosso sistema pirou. Ele achou que aLanchonete era a data.Lanchonete era o campoobservacoes.Mas...comoelepoderiasaberisso?Eunãomencioneiparaeleaordemdosdados,entãoeleassumiuumaordemdeterminada, umaordemque eunãoprestei atenção,mas foi fornecidaquandoexecutamosoDESCcompras:

|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

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 simosdados foram inseridos com sucesso.Temosumnovo registro emnossa tabela.Masporque será queoMySQLadotouo formatoano-mês-dia ao invésdedia/mês/ano?O formatoano-mês-diaéutilizadopelopadrãoSQL,ouseja,porquestõesdepadronização,eleéoformatomaisadequadoparaquefuncioneparatodos.

Como fazemos para conferir se ele foi inserido? Pedimos para o sistema de banco de dadosselecionartodososcampos(asterisco)danossatabeladecompras.Queremosfazerumaconsulta(umaquery)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 deixamosparatrás.

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

2.7SELECÃOSIMPLES

2.8AFORMATAÇÃODENÚMEROSDECIMAIS

10 2.7SELECÃOSIMPLES

Oerroindicaqueonúmerodecolunasnãocondizcomonúmerodecolunasquetemosnatabela.VamosdarumaolhadadentrodeVALUES?

(915,5,'2016-01-06','Guarda-roupa',0)

Avírgulaapareceu4vezes,portantoteríamos5camposdiferentes!Reparequeovalor915,5estáformatadonoestiloportuguêsdoBrasil,diferentedoestiloinglêsamericano,queusapontoparadefinirovalordecimal,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)

Maseseeuquisesse inserirosdadosemumaordemdiferente?Porexemplo,primeiroinformaradata,depoisasobservacoes,valor,eporfimsefoirecebidaounão?Seráquepodemosfazerisso?Quando estamos fazendoumINSERT, podemos informaroque estamosquerendo inserir pormeiodeparênteses:

mysql>INSERTINTOcompras(data,observacoes,valor,recebida)

Notequeagoraestamosinformandoexplicitamenteaordemdosvaloresqueinformaremosapósainstruçã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,comovoufalarsobreumadasminhascompras?Tenhoquefalar"lembradacompranalanchonetefeitanodia2016-01-05novalorde20.00quejáfoirecebida?".Ficadifícilreferenciarcadalinhadatabelaportodososvaloresdela.Seriamuitomaisfácilseeupudessefalar:"sabeacompra15?"ou"sabeacompra37654?".

Fica difícil falar sobre um registro se eu não tenho nada identificando ele demaneira única. Porexemplo, comovocê sabeque eu souoGuilherme, enãooPaulo?PoismeuCPF éum identificadorúnico, (por exemplo) 222.222.222-22. Só eu tenho esseCPF e eleme identifica.OPaulo tem o dele,333.333.333-33.

O mesmo vale para computadores de uma marca, por exemplo, o meu tem número de série16X000015, e o computador que está ao lado do meu tem o número de série 16X000016. A únicamaneiradeidentificarunicamenteumacoisaétendoumachaveimportantíssima,umachavequeétãoimportante,queéprimáriaaosbrasileiros,aoscomputadores...easminhascompras.

Mas como definir esses números únicos? Devo usar o ano de fabricação como no caso docomputadorquecomeçacom16(ano2016)?OusequenciascomnúmerosdiferentescomonocasodoCPF?

Existemdiversasmaneirasdegerar essesnúmerosúnicos,mas amais simplesde todas é: começacom 1.A primeira compra é a compra 1.A segunda compra é a 2.A terceira é a 3.Uma sequencianatural,queéincrementadaautomaticamenteacadanovacompra,acadanovoregistro.

Éissoquequeremos,umcampoquesejanossachaveprimária(PRIMARYKEY),queéumnúmerointeiro(INT),equesejaautomaticamenteincrementado(AUTO_INCREMENT).Sófaltadefinironomedele, que como identifica nossa compra, usamos a abreviação id. Esse é um padrão amplamenteutilizado.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

Claroqueopadrãonãoécriaroiddepoisdeumano.Emgeralcriamosumatabelajácomoseucampoid.Vamos então adaptar nossa query para criar a tabela com todos os campos, inclusive ocampoid:

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,vejamosadescriçã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

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áquefoiinseridotambém?Vejamos:

mysql>SELECT*FROMcompras;+----+-------+------------+-------------+----------+|id|valor|data|observacoes|recebida|+----+-------+------------+-------------+----------+|1|20.00|2016-01-05|Lanchonete|1|+----+-------+------------+-------------+----------+1rowinset(0,00sec)

Ele foi inseridoautomaticamente!Mas como issoaconteceu?Lembraquedefinimosoid comoAUTO_INCREMENT? Aquela propriedade que incrementa automaticamente a cada inserção. Então, éexatamenteelaqueincrementouoidpranós!Agorabastaapenasinserirasdemaiscompraseverificaroresultado:

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!

Aprendemosacriarasnossastabelaseinserirregistrosnelas,entãovamosadicionartodasasnossascompras. Eu já tenho um arquivo com as minhas compras irei executá-lo, não se preocupe, nosexercíciosforneceremosolinkdoarquivo.Saiadoterminalcomocomandoexit:

mysql>exit

2.11CONSULTASCOMFILTROS

14 2.11CONSULTASCOMFILTROS

Executeocomando:

mysql-uroot-pControleDeGastos<compras.sql

Agora todas as compras foram registradas no nosso banco de dados! Vamos consultar todas ascomprasnovamente:

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"ounocaso, comvalor abaixode500.EmSQLpodemosadicionar filtros como argumentoWHERE:

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,comvalor acimade1500?UsamosoWHERE novamente indicandoque agoraqueremos valores acimade1500:

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ãoforam entregues aomesmo tempo. Para isso precisamos adicionarmais um filtro, ou seja, filtrar porcomprasacimade1500etambé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(abaixode500)emaiscaras(acimade1500):

mysql>SELECT*FROMcomprasWHEREvalor<500ANDvalor>1500;Emptyset(0,00sec)

Parecequenão funcionouadicionarmaisdeumfiltroparaamesmacoluna...Vamosanalisarumpouco anossaquery, será que realmente está fazendo sentido esse nosso filtro?Observe que estamostentandopegarcomprasquetenhamovalorabaixode500eaomesmotempotenhaumvaloracimade1500.Seovalorfor300éabaixode500,porémnãoéacimade1500,seovalorfor1800éacimade1500,porémnãoéabaixode500,ouseja,éimpossívelqueessefiltrosejaválido.Podemosfazerumpequenoajustenessefiltro,podemosindicarquequeremosvaloresquesejammenoresque500oumaioresque1500:

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,equeremossaberapenasascomprasquetiveramessevalor.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áriasqueries commuitos filtros, porém, euquero saberdentre as comprasrealizadas,quaisforamemLanchonete.EntãovamosverificartodasasobservaçõesquesejamiguaisaLanchonete:

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 asminhas compras que foramParcelas. Então novamente eu usarei omesmofiltro,porémparaParcelas:

mysql>SELECT*FROMcomprasWHEREobservacoes='Parcelas';Emptyset(0,00sec)

Queestranhoeulembrodeteralgumregistrodeparcela...Sim,existemregistrosdeparcelas,porémnã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 umpedaço do texto que queremos na colunadesejada.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 LIKEsignifica 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" eleretornaráparanós,massuponhamosquequiséssemossabertodasascomprascomobservaçõesemqueo"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"nacolunaobservacoesindependentedeondeesteja.

Mascomofoiquechegamosnatabeladecomprasmesmo?Começamoscomumaidéiadosdadosque gostaríamos de armazenar: nossas compras. Cada uma delas tem um valor, uma data, umaobservação e se ela já foi recebidaounão. Seguindo essa idéiamoldamosuma estruturaque compõeumacompra,omoldedeumacompra:

Compra==>temumvalor,comcasasdecimais==>temumadata==>temumaobservação,texto==>podetersidorecebidaounão

Costumamosabreviaromodelocomo:

Compra-valor-data-observacoes-recebida

Eexistemdiversasmaneirasderepresentartalmodeloatravésdediagramas.NossofocomaioraquiseráempensarmosomodeloecomotrabalharmoscomoSQL,porissonãonospreocuparemostantocomcriarcentenasdediagramas.

Após definirmos os campos importantes para nosso negócio, nossa empresa (o business),percebemos que era necessário identificar cada compra demaneira única, e para isso definimos umachaveprimária,oid.

Tenho umnovo cliente emminha empresa de desenvolvimento de software e este cliente é umaescolaprimária.Elatemalunosbemnovoseprecisamanterumbancocomasinformaçõesdelespara

2.12MODELANDOTABELAS

2.12MODELANDOTABELAS 19

fazerdiversaspesquisas.

Pensandosozinho,nomeutrabalho,tentomodelarumaluno:

Aluno-id-nome-serie-sala-email-telefone-endereco

Pronto,apresentoomodelodomeubancoparaoclienteeelereclamamuito.Adiretoracomentaquequandotemquesecomunicararespeitodoalunonãoéotelefoneouemaildelequeelaprecisa.Elaprecisaconversarcomospaisepor issoénecessáriooscamposdecontatodospais.Alteramosentãonossomodelo:

Aluno-id-nome-serie-sala-email-telefone-endereco-nomeDoPai-nomeDaMae-telefoneDoPai-telefoneDaMae

Umoutrodetalhequesumiuéaquestãodavacinação.Adiretoragostadeconversarcomospaispara indicar quando é épocade vacinação.Ela sabeda importância do efeito de vacinação emgrupo(todosdevemservacinados,seumnãoé,existechancede infectarmuitos).Por issoelasempreenviacartas (campo endereco) para os pais (campo nomeDoPai e nomeDaMae). Mas... como saber qualvacinaeleprecisatomar?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á naquartasérie!Tantoasériequantoasalasãovaloresquemudamanualmente,eprecisamosatualizá-lossempre, quase que todos os alunos tem sua série e sala atualizadas de uma vez só (algumas escolaspermitem a troca de salas nomeio 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éoquechamamosdemodelagemdedados.Naverdadeestamosmodelandoaestruturaqueserácapazdereceberosdados.Enãoexistemregras 100% fixas nesse processo, por issomesmo é importantíssimo levantarmos as necessidades, osrequisitosdosnossosclientes juntoaeles.Conversandocomeles,vendootrabalhodelesnodiaadia,entendemosoqueelesprecisamecomomodelaressasinformaçõesnobanco.Emesmotendomodeladoumavez,podeserquedaquiumanotemosqueevoluirnossomodelo.

Nósaprendemosacriarumbancodedados,criartabelascomoasplanilhasdoExceleagoraestamosaprendendoamanipularinformaçõesfazendofiltrocomWHERE,AND,OReLIKE.Vamosparaosexercícios?

1. Instale o servidor doMySQL em suamáquina. Qual o sistema operacional em que você fez suainstalação? Sentiuque algopodia ser simplificadonoprocessode instalação?Lembre-se que vocêpoderealizarodownloaddeambosemhttp://MySQL.com/downloads/MySQL.

Vocêpodeoptarporbaixaraversãomaisatual.

1. LoguenoMySQL,ecomececriandoobancodedados:

mysql-uroot-pCREATEDATABASEControleDeGastos;USEControleDeGastos;

Agoravamoscriaratabela.Elaprecisaterosseguintescampos:idinteiro,valornúmerocomvírgula,data,observacoeseumbooleanoparamarcarseacomprafoirecebida.Atabeladeve-sechamar"compras".

1. Cliqueaquiefaçaodownloaddoarquivo.sql,eimportenoMySQL:

2.13RESUMINDO

2.14EXERCÍCIOS

2.13RESUMINDO 21

mysql-uroot-pControleDeGastos<compras.sql

Emseguida,executeoSELECTparagarantirquetodasasinformaçoesforamadicionadas:

SELECT*FROMcompras;

DICA: Salve o arquivocompras.sql emumapastade fácil acessona linhade comando.Alémdisso,oarquivodeveestarnomesmolugarondevocêexecutaráocomando.

1. Selecione valor e observacoes de todas as compras cuja data seja maior-ou-igual que15/12/2012.

2. Qual o comando SQL para juntar duas condições diferentes? Por exemplo, SELECT * FROMTABELAWHEREcampo>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 que15/12/2014.

5. SelecionetodasascomprascujovalorestejaentreR$15,00eR$35,00eaobservaçãocomececomapalavra'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

Cadastramos todas as nossas compras no banco de dados, porém, nomeu rascunho onde eu colocotodasasminhascomprasalgunsvaloresnãoestãobatendo.Vamosselecionarovaloreaobservaçãodetodasascomprascomvaloresentre1000e2000:

mysql>SELECTvalor,observacoesFROMcomprasWHEREvalor>=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,quandoqueremosfiltrarumintervalo,podemosutilizarooperadorBETWEEN:

mysql>SELECTvalor,observacoesFROMcomprasWHEREvalorBETWEEN1000AND2000;+---------+------------------------+|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ãovamosadicionarmaisumfiltropegandoointervalode01/01/2013e31/12/2013:

mysql>SELECTvalor,observacoesFROMcomprasWHEREvalorBETWEEN1000AND2000ANDdataBETWEEN'2013-01-01'AND'2013-12-31';+---------+-------------+|valor|observacoes|+---------+-------------+|1203.00|Quartos|+---------+-------------+1rowinset(0,00sec)

ATUALIZANDOEEXCLUINDODADOS

3ATUALIZANDOEEXCLUINDODADOS 23

Encontreiacompraquefoifeitacomovalorerrado,nomeurascunhoacompradeQuartosovaloréde1500.Entãoagoraprecisamosalteraressevalornanossatabela.Paraalterar/atualizarvaloresemSQLutilizamosainstruçãoUPDATE:

UPDATEcompras

AgoraprecisamosadicionaroquequeremosalterarpormeiodainstruçãoSET:

UPDATEcomprasSETvalor=1500;

Precisamos sempre tomar cuidado com a instrução UPDATE , pois o exemplo acima executanormalmente,porémoqueelevaiatualizar?Nãoinformamosemmomentoalgumqualcompraprecisaseratualizar,ouseja,eleiriaatualizarTODASascomprasenãoéissoquequeremos.AntesdeexecutaroUPDATE,precisamosverificaroiddacompraquequeremosalterar:

mysql>SELECTid,valor,observacoesFROMcomprasWHEREvalorBETWEEN1000AND2000ANDdataBETWEEN'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,observacoesFROMcomprasWHEREvalorBETWEEN1000AND2000ANDdataBETWEEN'2013-01-01'AND'2013-12-31';+---------+-------------+|valor|observacoes|+---------+-------------+|1500.00|Quartos|+---------+-------------+1rowinset(0,00sec)

Analisandomelhor essa compra eu sei que se refere aosmóveis do quarto,mas uma observaçãocomo"Quartos"quedizeroque?Nãoéclaraosuficiente,podeserquedaquia6meses,1anooumaistempoeunemsaibamaisoqueserefereessaobservaçãoevouacabarpensando:"Seráqueeucompreimóveisoucompreiumquartotodo?".Umtantoconfuso...Entãovamosalterartambémasobservaçõesedeixaralgomaisclaro,comoporexemplo:"Reformadequartos".

UPDATEcompraSETobservacoes='Reformadequartos'WHEREid=11;

Verificandoanovaobservação:

3.1UTILIZANDOOUPDATE

24 3.1UTILIZANDOOUPDATE

mysql>SELECTvalor,observacoesFROMcompras->WHEREvalorBETWEEN1000AND2000->ANDdataBETWEEN'2013-01-01'AND'2013-12-31';+---------+--------------------+|valor|observacoes|+---------+--------------------+|1500.00|Reformadequartos|+---------+--------------------+1rowinset(0,00sec)

AssimcomousamosoUPDATEparaatualizarumaúnicacoluna,poderíamosatualizardoisoumaisvalores,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,nocasodoSETnãoutilizeooperadorANDparaatribuirvalores,comonoexemploaseguir,queNÃOéoquequeremos:

errado:UPDATEcomprasSETvalor=1500ANDobservacoes='Reformadequartosnovos'WHEREid=8;certo:UPDATEcomprasSETvalor=1500,observacoes='Reformadequartosnovos'WHEREid=8;

Esquecide adicionar 10%de impostoquepagueina comprade id 11... portantoquero fazer algocomo:

mysql>SELECTvalorFROMcomprasWHEREid=11;+---------+|valor|+---------+|1500.00|+---------+1rowinset(0,00sec)

Descobriovaloratual,agoramultiplicopor1.1paraaumentar10%:1500*1.1 são1650reais.Entãoatualizoagoramanualmente:

UPDATEcomprasSETvalor=1650ANDobservacoes='Reformadequartosnovos'WHEREid=11;

3.2ATUALIZANDOVÁRIASCOLUNASAOMESMOTEMPO

3.3 UTILIZANDO UMA COLUNA COMO REFERÊNCIA PARA OUTRACOLUNA

3.2ATUALIZANDOVÁRIASCOLUNASAOMESMOTEMPO 25

Maseseeuquisessefazeromesmoparadiversaslinhas?

mysql>SELECTvalorFROMcomprasWHEREid>11ANDid<=14;+--------+|valor|+--------+|402.90||54.98||12.34|+--------+3rowsinset(0,00sec)

UPDATE....eagora???

Eagora?Vou terque calcularnamão cadaumdos casos, com10%amais, e fazeruma linhadeUPDATEparacadaum?AsoluçãoéfazerumúnicoUPDATEemquedigoquequeroalterarovalorparaovalordelemesmo,vezesosmeus1.1quejáqueriaantes:

UPDATEcomprasSETvalor=valor*1.1WHEREid>=11ANDid<=14;

Nessaqueryficaclaroqueeupossousarovalordeumcampo(qualquer)paraatualizarumcampodamesmalinha.Nonossocasousamosovalororiginalparacalcularonovovalor.Masestoulivreparausaroutroscamposdamesmalinha,desdequefaçasentidoparaomeuproblema,claro.Porexemplo,seeutenhoumatabeladeprodutoscomoscamposprecoLiquidoeupossoatualizaroprecoBrutoquandooimpostomudarpara15%:

UPDATEprodutosSETprecoBruto=precoLiquido*1.15;

Observei omeu rascunho e percebi que essa compra eu não devia ter sido cadastrada naminhatabeladecompras,ou seja, euprecisoexcluir esse registrodomeubancodedados.EmSQL,quandoqueremosexcluiralgumregistro,utilizamosainstruçãoDELETE:

DELETEFROMcompras;

ODELETEtemocomportamentosimilaraoUPDATE,ouseja,precisamossempretomarcuidadoquando queremos excluir algum registro da tabela. Damesma forma que fizemos com o UPDATE,precisamos adicionar a instrução WHERE para informa o que queremos excluir, justamente paraevitamosaexclusãodetodososdados:

mysql>DELETEFROMcomprasWHEREid=11;QueryOK,1rowaffected(0,01sec)

Severificarmosnovamenteseexisteoregistrodessacompra:

mysql>SELECTvalor,observacoesFROMcomprasWHEREvalorBETWEEN1000AND2000ANDdataBETWEEN'2013-01-01'AND'2013-12-31';Emptyset(0,00sec)

3.4UTILIZANDOODELETE

26 3.4UTILIZANDOODELETE

Acomprafoiexcluídaconformeoesperado!

VimoscomooDELETEeUPDATEpodemserperigososembancodedados,senãodefinirmosumacondição para cada uma dessas instruções podemos excluir/alterarTODAS as informações da nossatabela.AboapráticaparaexecutaressasinstruçõesésempreescreverainstruçãoWHEREantes,ouseja,definirprimeiroqualseráacondiçãoparaexecutaressasinstruções:

WHEREid=11;

EntãoadicionamosoDELETE/UPDATE:

DELETEFROMcomprasWHEREid=11;

UPDATEcomprasSETvalor=1500WHEREid=11;

Dessaformagarantimosqueonossobancodedadosnãotenharisco.

NessecapítuloaprendemoscomofazerqueriespormeiodeintervalosutilizandooBETWEENecomoalterar/excluirosdadosdanossatabelautilizandooDELETEeoUPDATE.Vamosparaosexercícios?

1. Altereascompras,colocandoaobservação'preparandoonatal'paratodasasqueforamefetuadasnodia20/12/2014.

2. AltereoVALORdascomprasfeitasantesde01/06/2013.SomeR$10,00aovaloratual.

3. Atualizetodasascomprasfeitasentre01/07/2013e01/07/2014paraqueelastenhamaobservação'entregueantesde2014'eacolunarecebidacomovalorTRUE.

4. EmumcomandoWHEREépossívelespecificarumintervalodevalores.Paratanto,éprecisodizerqualovalormínimoeovalormáximoquedefineointervalo.Qualéooperadorqueéusadoparaisso?

5. Qualoperadorvocêusapararemoverlinhasdecomprasdesuatabela?

6. Excluaascomprasrealizadasentre05e20marçode2013.

7. Existe um operador lógico chamado NOT. Esse operador pode ser usado para negar qualquercondição.Porexemplo,paraselecionarqualquerregistrocomdatadiferentede03/11/2014,podeserconstruídooseguinteWHERE:

3.5CUIDADOSCOMODELETEEUPDATE

3.6RESUMINDO

EXERCÍCIOS

3.5CUIDADOSCOMODELETEEUPDATE 27

WHERENOTDATA='2011-11-03'

UseooperadorNOTemonteumSELECTqueretornatodasascomprascomvalordiferentedeR$108,00.

28 3.6RESUMINDO

CAPÍTULO4

Muitasvezesqueinserimosdadosemumbancodedados,precisamossaberquaissãoostiposdedadosde cada coluna da tabela que queremos popular.Vamos dar uma olhada novamente na estrutura danossatabelapormeiodainstruçã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, oMySQLdemonstra algumas características das colunas da nossa tabela.VamosverificaracolunaNull,oqueseráqueelasignifica?Seráqueestádizendoquenossascolunasaceitamvaloresvazios?Entãovamostentarinserirumacompracomobservacoesvazia,ouseja,nula,comovalornull:

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.MasemqualmomentoinformamosaoMySQLquequeriámosvaloresnulos?Emnenhummomento!Porém,quandocriamosuma tabela noMySQL e não informamos se queremos ou não valores nulos para uma determinadacoluna,porpadrão,oMySQLaceitaosvaloresnulos.

Notequeumtextovazioédiferentedenãoternada!Édiferentedeumtextovaziocomo ''.Nuloé

ALTERANDOERESTRINGINDOOFORMATODENOSSASTABELAS

4ALTERANDOERESTRINGINDOOFORMATODENOSSASTABELAS 29

nada,éainexistênciadeumvalor.Umtextosemcaractereséumtextocomzerocaracteres.Umvalornulo nem texto é, nemnada é.Nulo é o não ser.Uma questão filosóficamilenar, discutida entre osmelhoresfilósofoseprogramadores,alémdeserfontedemuitosbugs.Cuidado.

Para resolver esse problema podemos criar restrições, Constraints, que tem a capacidade dedeterminarasregrasqueascolunasdenossas tabelas terão.AntesdeconfiguraroConstraints,vamosverificartodososregistrosquetiveremobservaçõesnulasevamosapagá-los.Queremosselecionartodasasobservaçõ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)

PodemosdefinirConstraintsnomomentodacriaçãodatabela,comonocasodedefinirquenossovaloredatanãopodemsernulos(NOTNULL):

CREATETABLEcompras(idINTAUTO_INCREMENTPRIMARYKEY,valorDECIMAL(18,2)NOTNULL,dataDATENOTNULL,...

Porém,atabelajáexiste!Enãoéumaboapráticaexcluiratabelaecria-lanovamente,poispodemosperder registros! Para fazer alterações na estrutura da tabela, podemos utilizar a instrução ALTERTABLEquevimosantes:

ALTERTABLEcompras;

Precisamosespecificaroquequeremosfazernatabela,nessecasomodificarumacolunaeadicionarumaConstraints:

mysql>ALTERTABLEcomprasMODIFYCOLUMNobservacoesVARCHAR(255)NOTNULL;QueryOK,45rowsaffected(0,04sec)Records:45Duplicates:0Warnings:0

Observequeforamalteradastodasas45linhasexistentesnonossobancodedados.Severificarmosaestruturadanossatabelanovamente:

4.1RESTRINGINDOOSNULOS

4.2ADICIONANDOCONSTRAINTS

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)

NacolunaNull e na linhadasobservacoes está informandoquenão émais permitido valoresnulos.Vamostestar:

mysql>INSERTINTOcompras(valor,data,recebida,observacoes)VALUES(150,'2016-01-04',1,NULL);ERROR1048(23000):Column'observacoes'cannotbenull

VamossuporqueamaioriadascomprasqueregistramosnãosãoentreguesequequeremosqueopróprioMySQLentendaque,quandoeunãoinformaracolunarecebida,elasejapopuladacomovalor0. NoMySQL, além deConstraints, podemos adicionar valores padrões, no inglêsDefault, em umacolunautilizandoainstruçã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.3VALORESDEFAULT

4.4EVOLUÇÃODOBANCO

4.3VALORESDEFAULT 31

Nossa escola deseja agora entrar em contato por email com todos os pais de alunos de umdeterminado bairro devido a maior taxa de ocorrência de dengue. Como fazer isso se o campo deendereçonãoobrigavaobairro?Podemosapartirdeagoraadicionarumnovocampo:

Aluno-id-nascimento-nome-serieAtual-salaAtual-email-telefone-bairro-endereco-nomeDoPai-nomeDaMae-telefoneDoPai-telefoneDaMae

Alémdisso,depoisdeumanodescobrimosumbugnosistema.Algunsalunossairamdomesmo,comorepresentarquenãoexistemaissérieatualenemsalaatualparaessesalunos?Podemosescolhertrêsabordagens:

1. marcaroscamposserieAtualesalaAtualcomoNULL2. marcaroscamposcomoNULLecolocarumcampoativocomo0ou13. colocarumcampoativocomo0ou1

Reparequeoprimeiroeoúltimoparecemsersimplesdeimplementar,claramentemaissimplesqueimplementar os dois ao mesmo tempo (item 2). Mas cada um deles implica em um problema demodelagemdiferente.

Seassumimos(1)ecolocamoscamposserieAtualesalaAtualcomoNULL,todavezquequeremossaberquemestáinativotemosumaquerybemestranha,quenãoparecefazerisso:

select*fromAlunoswhereserieAtualisnullandsalaAtualisnull

Sériomesmo?Issosignificaqueoalunoestáinativo?Doisnulos?Alémdisso,seeupermitonulos,eupossoterumcasomuitoestranhocomooalunoaseguir:

GuilhermeSilveira,seriaAtual8,salaAtualnull

Comoassim?Éumalunonovoquealguém/osistemaesqueceudecolocara sala?Ouéumalunoantigoquealguém/osistemaesqueceuderemoverasérie?

Poroutrolado,naterceiraabordagem,adocampoativo,solto,geraumpossívelalunocomo:

GuilhermeSilveira,serieAtual8,salaAtualB,ativo0

Comoassim,oGuilhermeestáinativomasestána8Baomesmotempo?Oueleestána8Bounãoestá,nãotemasduascoisasaomesmotempo.Modeloestranho.

32 4.4EVOLUÇÃODOBANCO

Por fim, a abordagemdemodelagem 2, que utiliza o novo campo e o valor nulo, garante que asqueriessejamsimples:

select*fromAlunoswhereativo=false;

Masaindapermitecasosestranhoscomo:

GuilhermeSilveira,serieAtual8,salaAtualB,ativo0GuilhermeSilveira,serieAtualNULL,salaAtualB,ativo0GuilhermeSilveira,serieAtualNULL,salaAtualNULL,ativo1

Poderíamosusarrecursosbemmaisavançadosparatentarnosprotegerdealgunsdessescasos,masessesrecursossão,emgeral,específicosdeumbancodeterminado.Porexemplo,funcionamnoOraclemasnãonoMySQL.OunoMySQLmasnãonoPostgreSQL.NessecursofocamosempadrõesdoSQLqueconseguimosaplicar,emgeral,paratodoseles.

Eagora?Trêsmodelagensdiferentes, todas resolvemoproblemae todasgeramoutrosproblemastécnicos.Modelarbancosétomardecisõesqueapresentamvantagensedesvantagens.Nãoháregraemqual das três soluções devemos escolher, escolha a que você julgar melhor e mais justa para suaempresa/sistema/ocasião. O mais importante é tentar prever os possíveis pontos de erro como osmencionadosaqui.Conheceras falhasemnossodesenhodemodelagem(design)deantemãopermiteque não tenhamos uma surpresa desagradável quando descobrirmos elas de umamaneira infeliz nofuturo.

Vimosquealémdecriarastabelasemnossobancodedadosprecisamosverificarsealgumacolunapode receber valores nulos ou não, podemos determinar essas regras por meio de Constraints nomomento da criação da tabela ou utilizando a instrução ALTER TABLE se caso a tabela exista equeremosmodificar sua estrutura. Também vimos que em alguns casos os valores de cadaINSERTpodeserepetirmuitoeporissopodemosdefinirvalorespadrõescomainstruçãoDEFAULT.

1. Configureovalorpadrãoparaacolunarecebida.

2. Configureacolunaobservacoesparanãoaceitarvaloresnulos.

3. Nonossomodeloatual,qualcampovocêdeixariacomvaloresDEFAULTequaisnão?Justifiquesuadecisão. Note que como estamos falando de modelagem, não existe uma regra absoluta, existevantagensedesvantagensnadecisãoquetomar,tentecitá-las.

4. NOTNULLeDEFAULT podemserusados tambémnoCREATETABLE?Crieuma tabelanova eadicioneConstraintsevaloresDAFAULT.

4.5RESUMINDO

EXERCÍCIOS

4.5RESUMINDO 33

5. ReescrevaoCREATETABLE do começo do curso,marcando observacoes como nulo e valorpadrãodorecebidacomo1.

34 4.5RESUMINDO

CAPÍTULO5

Jáadicionamosmuitosdadosemnossobancodedadoseseriamaisinteressantefazermosqueriesmaisrobustas,comoporexemplo,saberototalquejágastei.OMySQLforneceafunçãoSUM()quesomatodosdosvaloresdeumacoluna:

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áascomprasrecebidaseasuasomaeemoutraasnãorecebidasesuasoma:

AGRUPANDODADOSEFAZENDOCONSULTASMAISINTELIGENTES

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çãoSUM()doMySQLelasomatodososvaloresdacolunaeretornaapenasumaúnicalinha,poiséumafunçãodeagregração!Pararesolvermosesseproblema,podemosutilizarainstruçãoGROUPBY que indica comoa somaprecisa ser agrupada, ou seja, some todas as comprasrecebidaseagrupeemumalinha,sometodasascomprasnãorecebidaseagrupeemoutralinha:

mysql>SELECTrecebida,SUM(valor)FROMcomprasGROUPBYrecebida;+----------+------------+|recebida|SUM(valor)|+----------+------------+|0|12281.16||1|31686.75|+----------+------------+2rowsinset(0,00sec)

Oresultadofoiconformeoesperado,porémnotequeonomedacolunaparaasomaestáumpoucoestranho,poisestamosaplicandoumafunçãoaoinvésderetornarumacoluna,seriamelhorseonomeretornadofosseapenas"soma".PodemosnomearascolunaspormeiodainstruçãoAS:

mysql>SELECTrecebida,SUM(valor)ASsomaFROMcomprasGROUPBYrecebida;+----------+----------+|recebida|soma|+----------+----------+|0|12281.16||1|31686.75|+----------+----------+2rowsinset(0,00sec)

Tambémpodemosaplicarfiltrosemqueriesqueutilizamfunçõesdeagregação:

mysql>SELECTrecebida,SUM(valor)ASsomaFROMcomprasWHEREvalor<1000GROUPBYrecebida;+----------+---------+|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 foientregueounãoeovalordasoma.PodemosretornarainformaçãodeanoutilizandoafunçãoYEAR()eainformaçãodemêsutilizandoafunçãoMONTH():

mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,SUM(valor)ASsomaFROMcomprasGROUPBYrecebida;

36 5AGRUPANDODADOSEFAZENDOCONSULTASMAISINTELIGENTES

+------+------+----------+----------+|mes|ano|recebida|soma|+------+------+----------+----------+|1|2016|0|12281.16||1|2016|1|31686.75|+------+------+----------+----------+2rowsinset(0,00sec)

Lembre-sequeestamoslidandocomumafunçãodeagregação!Porissoprecisamosinformartodasascolunasquequeremosagrupar,ouseja,acolunademêsedeano:

mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,SUM(valor)ASsomaFROMcomprasGROUPBYrecebida,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)

Conseguimos criar uma query bem robusta, mas perceba que as informações estão bemdesordenadas,aprimeiravistaédifícildeverasomadomêsdejaneiroemtodososanos,comotambém

5.1ORDENANDOOSRESULTADOS

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)ASsomaFROMcomprasGROUPBYrecebida,mes,anoORDERBYmes;+------+------+----------+----------+|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)ASsomaFROMcomprasGROUPBYrecebida,mes,anoORDERBYmes,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ãoconseguimosverificardeumaformaclaraasomadecadamêsduranteumano.PercebaqueainstruçãoORDERBYpriorizaascolunaspelaordememquesãoinformadadas,ouseja,quandofizemos:

ORDERBYmes,ano;

Informamosquequeremosquedêprioridadeàordenaçãodacolunamêseasdemaiscolunassejamordenadasdeacordocomomês!IssosignificaqueparaordenarmosoanoedepoisomêsbastaapenascolocaroanonoiníciodoORDERBY:

mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,SUM(valor)ASsomaFROMcomprasGROUPBYrecebida,mes,anoORDERBYano,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 dosmeses em cada ano.Alémda somapodemos também utilizar outras funções de agragação como por exemplo, a AVG() que retorna amédiadeumacoluna:

mysql>SELECTMONTH(data)asmes,YEAR(data)asano,recebida,AVG(valor)ASsomaFROMcomprasGROUPBYrecebida,mes,anoORDERBYano,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)

Vimos como podemos fazerqueriesmais robustas e inteligentes utilizando funções de agregação,comoporexemplo,aSUM()parasomareaAVG()para tiraramédia.Vimos tambémquequandoqueremos retornar outras colunas ao utilizar funções de agregação, precisamos utilizar a instruçãoGROUPBYparadeterminarquais serãoascolunasquequeremosqueseja feitaoagrupamentoequenem sempre o resultado vem organizado e por isso, em determinados casos, precisamos utilizar ainstruçãoORDERBYparaordenaranossaquerypormeiodeumacoluna.

1. Calculeamédiadetodasascomprascomdatasinferioresa12/05/2013.

2. Calculeaquantidadedecomprascomdatasinferioresa12/05/2013equejáforamrecebidas.

3. Calculeasomadetodasascompras,agrupadasseacomprarecebidaounão.

5.2RESUMINDO

EXERCÍCIOS

5.2RESUMINDO 41

CAPÍTULO6

Anossatabeladecomprasestábempopulada,commuitasinformaçõesdascomprasrealizadas,porémestá faltando uma informação muito importante, que são os compradores. Por vezes foi eu quemcomprou algo, mas também omeu irmão pode ter comprado ou até mesmo omeu primo... Comopodemos fazer para identificar o compradordeuma compra?De acordo comoque vimos até agorapodemosadicionarumanovacolunachamadacompradorcomotipovarchar(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 adicionarmosprecisamos verificar novamente verificar as informações das compras e identificar quem foi quecomprou:

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|

JUNTANDODADOSDEVÁRIASTABELAS

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)

Agoraque já seias informaçõesdascompraspossoadicionarosseuscompradores.Começaremospelas5primeirascompras:

|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ãoVieira,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 osobrenomeminú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ãoficaratento, com certeza acontecerá uma caca gigante no meu banco. Além disso, eu preciso tambémadicionarotelefonedocompradorparaumdia,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 semcomprador, então continuaremos adicionando os compradores. A próxima compra foi omeu primoAlexVieiraquecomprou.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 domeu primo acabei colocando omeu...Quanto sofrimento por causa de 2 colunas novas! Além de ter que me preocupar se os nomes doscompradores estão sendo exatamente iguais, agora eu precisome preocupar se os telefones tambémestãocorretosemais,todasasvezesqueeuprecisoinserirumcompradoreuprecisolembrardeinseriroseu telefone... E se agora eu precisasse adicionar o endereço, e-mail ou quaisquer informações do

46 6JUNTANDODADOSDEVÁRIASTABELAS

comprador?Euteriaquelembrartodasessasinformaçõescadavezqueeuinserirapenasumacompra!Quehorror!

Vejaoquãoproblemáticoestá sendomanteras informaçõesdeumcompradoremuma tabeladecompras.Alémdetrabalhosa,ainserçãoébemproblemática,poisháumgranderiscodefalhasnomeubancodedadoscomoporexemplo: informações redundantes (que se repetem)ouentão informaçõesincoerentes(omesmocompradorcomalgumainformaçãodiferente).Comtodacertezanãoéumaboasoluçãoparaanossanecessidade!Seráquenãoexisteumaformadiferentederesolverisso?

Reparaquetemosdoiselementos,duasentidades,emumaunica tabela:acompraeocomprador.Quandonosdeparamoscomesses tiposdeproblemascriamosnovastabelas.Entãovamoscriarumatabelachamadacompradores.

Nonossocasoqueremosquecadacompradorsejarepresentadopeloseunome,endereçoetelefone.Como já pensamos em modelagem antes, aqui fica o nosso modelo de tabela para representar oscompradores:

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 comprasforamfeitasapenaspormimepelomeutioJoã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

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 tabelacompradores.Entãonãoprecisamosmaisdasinforçõesdoscompradoresnatabelacompras,ouseja,vamos excluir as colunas comprador e telefone , mas como podemos excluir uma coluna?Precisamosalteraraestruturadatabela,entãocomeçaremospeloALTERTABLE:

ALTERTABLEcompras

Separaadicionarumacolunautilizamosa instruçãoADDCOLUMN, 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 ,precisamosdealguma formaassociarumacompracomumcomprador.Entãovamoscriarumanovacolunanatabelacompraseadicionaroiddocompradornessacoluna:

mysql>ALTERTABLEcomprasADDCOLUMNid_compradoresint;QueryOK,0rowsaffected(0,03sec)Records:0Duplicates:0Warnings:0

Porqueoiddoscompradoresprecisaficarnatabelacomprasenãooiddatabelacomprasnatabela compradores? Precisamos pensar seguinte forma: uma compra só pode ser de 1 únicocompradoreocompradorpodeter1oumaiscompras,comopodemosgarantirque1compraperteça

48 6.1NORMALIZANDONOSSOMODELO

apenas a 1 comprador? Fazendo comque a compra saiba quem é o seuúnico comprador!Ou seja,recebendoachavedoseudono!Porissoadicionamosoiddoscompradoresnatabeladecompras,sefizéssemosocontrário, ou seja, adicionássemosoid da comprana tabelacompradores, iríamospermitirque1únicacomprafosserealizadapor1oumaiscompradores,oquenãofazsentidoalgumsabendoqueasminhascomprassóforamrealizadaspormimapenaseasdomeutioporeleapenas.

Temos a nossa nova coluna para associar o comprador com a sua compra, então vamos fazerUPDATEna tabelacompras inserindoocompradordeid 1 para a primeirametadedas compras einserindoasegundametadeparaocompradordeid2.Primeirovamosverificarquantascomprasnóstemosusandoafunçã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)

Reparequeoque fizemos foideixar claroqueumcompradorpode termuitas compras (umparamuitos),ouaindaquemuitascompraspodemvirdomesmocomprador(manytoone),sódependedopontodevista.ChamamosentãodeumarelaçãoOnetoMany(ouManytoOne).

Conseguimosassociarumacompracomumcomprador,entãoagoravamosbuscarasinformaçõesdascompraseseusrespectivoscompradores:

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|

6.2ONETOMANY/MANYTOONE

6.3FOREIGNKEY

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 issoque euqueria, euqueroque, emapenasumaquery, nesse casoumSELECT,retornetantoas informaçõesdatabelacomprasedatabelacompradores. Será quepodemos fazerisso?Sim,podemos!Lembrada instruçãoFROM que indicaqual é a tabela que estamosbuscando asinformações?Alémdebuscarporumaúnicatabela,podemosindicarquequeremosbuscarpormaisde1tabelaseparandoastabelaspor",":

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|AlexFelipe|RuaVergueiro,3185|5571-2751||1|20.00|2016-01-05|Lanchonete|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||2|15.00|2016-01-06|Lanchonete|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||2|15.00|2016-01-06|Lanchonete|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||3|915.50|2016-01-06|Guarda-roupa|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||3|915.50|2016-01-06|Guarda-roupa|0|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||4|949.99|2016-01-10|Smartphone|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||4|949.99|2016-01-10|Smartphone|0|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||5|200.00|2012-02-19|Materialescolar|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||5|200.00|2012-02-19|Materialescolar|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||6|3500.00|2012-05-21|Televisao|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||6|3500.00|2012-05-21|Televisao|0|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||7|1576.40|2012-04-30|Materialdeconstrucao|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||7|1576.40|2012-04-30|Materialdeconstrucao|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||8|163.45|2012-12-15|Pizzaprafamilia|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||8|163.45|2012-12-15|Pizzaprafamilia|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||9|4780.00|2013-01-23|Saladeestar|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||9|4780.00|2013-01-23|Saladeestar|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||10|392.15|2013-03-03|Quartos|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||10|392.15|2013-03-03|Quartos|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||12|402.90|2013-03-21|Copa|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||12|402.90|2013-03-21|Copa|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||13|54.98|2013-04-12|Lanchonete|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||13|54.98|2013-04-12|Lanchonete|0|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||14|12.34|2013-05-23|Lanchonete|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||14|12.34|2013-05-23|Lanchonete|0|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||15|78.65|2013-12-04|Lanchonete|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||15|78.65|2013-12-04|Lanchonete|0|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||16|12.39|2013-01-06|Sorvetenoparque|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||16|12.39|2013-01-06|Sorvetenoparque|0|1|2|JoãodaSilva|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ãodaSilva|Av.Paulista,6544|2220-4156||18|2498.00|2013-01-12|Comprasdejaneiro|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||18|2498.00|2013-01-12|Comprasdejaneiro|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||19|3212.40|2013-11-13|Comprasdomes|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||19|3212.40|2013-11-13|Comprasdomes|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||20|223.09|2013-12-17|Comprasdenatal|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||20|223.09|2013-12-17|Comprasdenatal|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||21|768.90|2013-01-16|Festa|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||21|768.90|2013-01-16|Festa|1|1|2|JoãodaSilva|Av.Paulista,6544|2220-4156||22|827.50|2014-01-09|Festa|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||22|827.50|2014-01-09|Festa|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||23|12.00|2014-02-19|Salgadonoaeroporto|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||23|12.00|2014-02-19|Salgadonoaeroporto|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||24|678.43|2014-05-21|PassagempraBahia|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||24|678.43|2014-05-21|PassagempraBahia|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||25|10937.12|2014-04-30|CarnavalemCancun|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||25|10937.12|2014-04-30|CarnavalemCancun|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||26|1501.00|2014-06-22|Presentedasogra|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||26|1501.00|2014-06-22|Presentedasogra|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||27|1709.00|2014-08-25|Parceladacasa|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||27|1709.00|2014-08-25|Parceladacasa|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||28|567.09|2014-09-25|Parceladocarro|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||28|567.09|2014-09-25|Parceladocarro|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||29|631.53|2014-10-12|IPTU|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||29|631.53|2014-10-12|IPTU|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||30|909.11|2014-02-11|IPVA|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||30|909.11|2014-02-11|IPVA|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156|

6.3FOREIGNKEY 53

|33|115.90|2014-06-12|Diadosnamorados|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||33|115.90|2014-06-12|Diadosnamorados|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||34|98.00|2014-10-12|Diadascrianças|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||34|98.00|2014-10-12|Diadascrianças|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||35|253.70|2014-12-20|Natal-presentes|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||35|253.70|2014-12-20|Natal-presentes|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||36|370.15|2014-12-25|Comprasdenatal|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||36|370.15|2014-12-25|Comprasdenatal|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||37|32.09|2015-07-02|Lanchonete|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||37|32.09|2015-07-02|Lanchonete|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||39|98.70|2015-02-07|Lanchonete|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||39|98.70|2015-02-07|Lanchonete|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||40|213.50|2015-09-25|Roupas|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||40|213.50|2015-09-25|Roupas|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||41|1245.20|2015-10-17|Roupas|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||41|1245.20|2015-10-17|Roupas|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||42|23.78|2015-12-18|LanchonetedoZé|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||42|23.78|2015-12-18|LanchonetedoZé|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||43|576.12|2015-09-13|Sapatos|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||43|576.12|2015-09-13|Sapatos|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||44|12.34|2015-07-19|Canetas|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||44|12.34|2015-07-19|Canetas|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||45|87.43|2015-05-10|Gravata|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||45|87.43|2015-05-10|Gravata|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||46|887.66|2015-02-02|Presenteparaofilhao|1|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||46|887.66|2015-02-02|Presenteparaofilhao|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||48|150.00|2016-01-05|Compradeteste|0|2|1|AlexFelipe|RuaVergueiro,3185|5571-2751||48|150.00|2016-01-05|Compradeteste|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156|+----+----------+------------+------------------------------+----------+----------------+----+----------------+---------------------+-----------+92rowsinset(0,00sec)

54 6.3FOREIGNKEY

Opa!Observequeessaqueryestáumpoucoestranha,poiseladevolveu92linhas,sendoquetemosapenas46comprasregistradas!Duplicouosnossosregistros...Esseproblemaaconteceu,poisoMySQLnãosabecomoassociaratabelacompraseatabelacompradores.PrecisamosinformaraoMySQLpormeiodequalcolunaelefaráessaassociação.Percebaqueacolunaquecontémachave(primarykey)docomprador é justamente a id_compradores, chamamos esse tipo de coluna de FOREIGNKEY ouchaveestrangeira.Parajuntarmosachaveestrangeiracomachaveprimáriadeumatabela,utilizamosainstruçã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|AlexFelipe|RuaVergueiro,3185|5571-2751||2|15.00|2016-01-06|Lanchonete|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||3|915.50|2016-01-06|Guarda-roupa|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||4|949.99|2016-01-10|Smartphone|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||5|200.00|2012-02-19|Materialescolar|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||6|3500.00|2012-05-21|Televisao|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||7|1576.40|2012-04-30|Materialdeconstrucao|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||8|163.45|2012-12-15|Pizzaprafamilia|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||9|4780.00|2013-01-23|Saladeestar|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||10|392.15|2013-03-03|Quartos|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||12|402.90|2013-03-21|Copa|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||13|54.98|2013-04-12|Lanchonete|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||14|12.34|2013-05-23|Lanchonete|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||15|78.65|2013-12-04|Lanchonete|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||16|12.39|2013-01-06|Sorvetenoparque|0|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||17|98.12|2013-07-09|HopiHari|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||18|2498.00|2013-01-12|Comprasdejaneiro|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||19|3212.40|2013-11-13|Comprasdomes|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||20|223.09|2013-12-17|Comprasdenatal|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||21|768.90|2013-01-16|Festa|1|1|1|AlexFelipe|RuaVergueiro,3185|5571-2751||22|827.50|2014-01-09|Festa|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||23|12.00|2014-02-19|Salgadonoaeroporto|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156|

6.3FOREIGNKEY 55

|24|678.43|2014-05-21|PassagempraBahia|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||25|10937.12|2014-04-30|CarnavalemCancun|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||26|1501.00|2014-06-22|Presentedasogra|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||27|1709.00|2014-08-25|Parceladacasa|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||28|567.09|2014-09-25|Parceladocarro|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||29|631.53|2014-10-12|IPTU|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||30|909.11|2014-02-11|IPVA|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||31|768.18|2014-04-10|GasolinaviagemPortoAlegre|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||32|434.00|2014-04-01|RodeiointeriordeSaoPaulo|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||33|115.90|2014-06-12|Diadosnamorados|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||34|98.00|2014-10-12|Diadascrianças|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||35|253.70|2014-12-20|Natal-presentes|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||36|370.15|2014-12-25|Comprasdenatal|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||37|32.09|2015-07-02|Lanchonete|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||38|954.12|2015-11-03|ShowdaIveteSangalo|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||39|98.70|2015-02-07|Lanchonete|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||40|213.50|2015-09-25|Roupas|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||41|1245.20|2015-10-17|Roupas|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||42|23.78|2015-12-18|LanchonetedoZé|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||43|576.12|2015-09-13|Sapatos|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||44|12.34|2015-07-19|Canetas|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||45|87.43|2015-05-10|Gravata|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||46|887.66|2015-02-02|Presenteparaofilhao|1|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156||48|150.00|2016-01-05|Compradeteste|0|2|2|JoãodaSilva|Av.Paulista,6544|2220-4156|+----+----------+------------+------------------------------+----------+----------------+----+----------------+---------------------+-----------+46rowsinset(0,01sec)

AinstruçãoJOINesperaumatabelaqueprecisaserjuntada:FROMcomprasJOINcompradores.Nessecasoestamosjuntandoatabelacomprascomatabelacompradores.Parapassarmosocritériodejunção, utilizamos a instrução ON : ON compras.id_compradores = compradores.id . NessemomentoestamosinformandoaFOREIGNKEYdatabelacompras(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)

Notequenãoexisteo comprador comid 100,mas,mesmoassim, conseguimos adicioná-lonanossatabeladecompras.Precisamosgarantiraintegridadedosnossosdados,informandoaoMySQLqueacolunaid_compradoresdatabelacompraséumaFOREIGNKEYdatabelacompradores,ouseja,só poderemos adicionar um id apenas se estiver registrado na tabela compradores. Antes deadicionarmosaFOREIGNKEY,precisamosexcluiroregistrocomoid_compradores=100,poisnãoépossíveladicionarumaFOREIGNKEYcomdadosquenãoexistanatabelaqueiremosreferenciar.

mysql>DELETEFROMcomprasWHEREid_compradores=100;QueryOK,1rowaffected(0,01sec)

QuandoadicionamosumaFOREIGNKEY emuma tabela, estamos adicionandoumaConstraints,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(`ControleDeGastos`.`compras`,CONSTRAINT`fk_compradores`FOREIGNKEY(`id_compradores`)REFERENCES`compradores`(`id`))

Agoraonossobancodedadosnãopermiteainserçãoderegistroscomcompradoresinexistentes!Setentarmosadicionaressamesmacompracomumcompradorexistente:

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)

Conseguimosdeixar a nossa base de dados bem robusta, restringindo as nossas colunas paranãopermitirvaloresnulosenãoaceitarainserçãodecompradoresinexistentes,masagorasurgiuumanovanecessidadequeprecisaserimplementada,precisamosinformartambémqualéaformadepagamentoque foi realizada na compra, por exemplo, existem 2 formas de pagamento, boleto e crédito. Comopoderíamosimplementaressanovainformaçãonanossatabelaatualmente?CriamosumanovacolunacomotipoVARCHARecorresmosoriscodeinserirumaformadepagamentoinválida?Nãopareceumaboa solução... Que tal criarmos uma nova tabela chamada id_forma_de_pagto e fazermos umaFOREIGNKEY?Aparentementeéumaboasolução,porémiremoscriarumanovatabelapararesolverumproblemabempequeno?Lembre-sequeacadaFOREIGNKEYmaisJOINsasnossasqueriesterão...NoMySQL,existeotipodedadoENUMquepermitequeinformemosquaisserãoosdadosqueelepodeaceitar.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)

6.4DETERMINANDOVALORESFIXOSNATABELA

58 6.4DETERMINANDOVALORESFIXOSNATABELA

OMySQL impediuque fosseadicionadoumvalordiferentede "BOLETO"ou "CREDITO",masoquerealmenteprecisamoséqueelesimplesmentenãodeixeadicionarumacompraquenãotenhapelomenos uma dessas formas de pagamento. Além das configurações das tabelas, podemos tambémconfigurar o próprio servidor doMySQL. O servidor doMySQL opera em diferentes SQL modes edentre esses modos, existe o strict mode que tem a finalidade de tratar valores inválidos queconfiguramosemnossas tabelaspara instruçõesdeINSERTeUPDATE, comopor exemplo, onossoENUM.ParahabilitarostrictmodeprecisamosalteraroSQLmodedanossasessão.Nessecasousaremosomodo"STRICT_ALL_TABLES":

mysql>SETSESSIONsql_mode='STRICT_ALL_TABLES';QueryOK,0rowsaffected(0,00sec)

SequisermosverificarseomodofoimodificadopodemosretornaressevalorpormeiodainstruçãoSELECT:

mysql>SELECT@@SESSION.sql_mode;+--------------------+|@@SESSION.sql_mode|+--------------------+|STRICT_ALL_TABLES|+--------------------+1rowinset(0,00sec)

AgoraqueconfiguramosoSQLmodedoMySQLparaimpedirainserçãodevaloresinválidos,vamosapagaroú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 oMySQL impediu que a linha fosse inserida, pois o valor não é válido para acolunaforma_pagto.

OSQLmodeéumaconfiguraçãodoservidorenósalteramosapenasasessãoqueestávamoslogado,oque aconteceria se caso saímos da sessão que configuramos e entrássemos emuma nova?OMySQLadotariaoSQLmodepadrã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

6.5SERVERSQLMODES 59

|@@GLOBAL.sql_mode|+-------------------+|STRICT_ALL_TABLES|+-------------------+1rowinset(0,00sec)

Agora,todasasvezesqueentrarmosnoMySQL,seráadotadoostrictmode.

OENUMéumaboasoluçãoquandoqueremosrestringirvaloresespecíficose jáesperadosemumcoluna,porémnãofazpartedopadrãoANSIqueéopadrãoparaaescritadeinstruçõesSQL,ouseja,éumrecursoexclusivodoMySQLecadabancodedadospossuiasuaprópriaimplementaçãoparaessamesmafuncionadalidade.

Nesse capítulo aprendemos a fazer uma relação entre duas tabelas utilizando FOREIGN KEYS.Vimostambémqueparafazermosqueriescomduastabelasdiferentesutilizamosaschavesestrangeiraspormeioda instruçãoJOIN que informaaoMySQLquais serãoos critériosparaassociar as tabelasdistintas.Esempreprecisamoslembrarque,quandoestamoslidandocomFOREIGNKEY, precisamoscriarumaConstraintparagarantirquetodasaschavesestrangeirasprecisaexistirnatabelaquefazemosreferência.Alémdisso,vimostambémquequandoqueremosadicionarumacolunanovaparaqueaceiteapenas determinados valores já esperado por nós, como é o caso da forma de pagamento, podemosutilizaroENUMdoMySQLcomoumsolução,poréméimportantelembrarqueéumasoluçãoquenãofazpartedopadrãoANSI,ouseja,cadabancodedadospossuisuaprópriaimplementação.Vamosparaosexercícios?

Vamosparaosexercícios?

1. Crieatabelacompradorescomid,nome,enderecoetelefone.

2. Insiraoscompradores,GuilhermeeJoãodaSilva.

3. Adicioneacolunaid_compradoresnatabelacompras e defina a chave estrangeira (FOREIGNKEY)referenciandooiddatabelacompradores.

4. Atualizeatabelacompraseinsiraoiddoscompradoresnacolunaid_compradores.

5. ExibaoNOMEdocompradoreoVALORdetodasascomprasfeitasantesde09/08/2014.

6. ExibatodasascomprasdocompradorquepossuiIDiguala2.

7. Exibatodasascompras(massemosdadosdocomprador),cujocompradortenhanomequecomeçacom'GUILHERME'.

6.6RESUMINDO

EXERCÍCIOS

60 6.6RESUMINDO

8. Exibaonomedocompradoreasomadetodasassuascompras.

9. A tabelacompras foi alterada para conter umaFOREIGNKEY referenciando a colunaid databela compradores . O objetivo é deixar claro para o banco de dados quecompras.id_compradoresestádealgumaformarelacionadocomatabelacompradoresatravésdacolunacompradores.id.MesmosemcriaraFOREIGNKEYépossívelrelacionartabelasatravésdocomandoJOIN.

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', porexemplo,'DINHEIRO'everifiqueseoMySQLrecusaainserção.

14. AdicioneasformasdepagamentoparatodasascompraspormeiodainstruçãoUPDATE.

15. FaçaaconfiguraçãoglobaldoMySQLparaqueelesempreentrenostrictmode.

6.6RESUMINDO 61

CAPÍTULO7

Paraasegundapartedenossocursoutilizaremosumconjuntodedadosdeumsistemadeensinoonlinecomoexemplo.Nãosepreocupe,poisdisponilizaremosoarquivoparaquevocêbaixeeexecuteoscriptcomtodasastabelas.

Continuaremosusandoo terminaldoMySQLduranteo curso,porém, se vocêprefereumaoutrainterface,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 doterminaleexecuteoarquivonabasededadosescola.

mysql-uroot-pescola<escola.sql

Comoarquivo importado,podemosabrirnovamenteoMySQL,porémselecioneabasededadosescoola.

mysql-uroot-pescola

ParaverificartodasastabelasdanossabasededadospodemosutilizaroinstruçãoSHOWTABLESdoMySQL:

SHOWTABLES;

+------------------+|Tables_in_escola|+------------------+|aluno||curso||exercicio||matricula||nota||resposta||secao|+------------------+7rowsinset(0,00sec)

Sabemosquais sãoas tabelas,porémprecisamossabermais sobreaestruturadessas tabelas,entãovamosutilizarainstruçãoDESC.Vamosverificarprimeiroatabelaaluno:

ALUNOSSEMMATRÍCULAEOEXISTS

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

Damesma forma que a tabelaaluno, a tabelacurso armazenada apenas as informações doscursos.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 atabelaalunoeacolunacurso_idreferenciaatabelacurso.Entãovamosverificarquaissãotodososcursosdeumaluno.Paraumamelhorcompreensãodecomoéoresultadoesperadodessaquery,vejaaplanilhaaseguir:

Figura7.1:Planilhaexemplo

Comopodemosver,nóstemosa listadealunoseseusrespectivoscursos.Entãovamoscomeçararetornar todosos alunosquepossuemumamatrícula, ou seja, vamos fazerumJOIN entre a tabelaalunoematricula:

7ALUNOSSEMMATRÍCULAEOEXISTS 63

SELECTa.nomeFROMalunoaJOINmatriculamONm.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?Quandoescrevemosalunoa,significaqueestamos"apelidando"atabelaaluno,ouseja,todasasvezesqueutilizarmosa.alguma_coisa,estaremospegandoalgumacolunadatabelaaluno!EmSQL,esses"apelidos"sãoconhecidoscomoAlias.

Os alunos foram retornados. Então agora vamos juntar a tabelacurso ealuno com a tabelamatriculaaomesmotempo,porém,dessavezvamosretornaronomedoalunoeonomedocurso:

SELECTa.nome,c.nomeFROMalunoaJOINmatriculamONm.aluno_id=a.idJOINcursocONm.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|+----------+

Observeque foramretornadas14 linhasquandobuscamos todosos alunos e seus cursos,ou seja,existem alunos que não temmatrícula! Como podemos verificar quais são os alunos que não estãomatriculados?NoMySQL,podemosutilizarafunçãoEXISTS()paraverificarseexistealgumregistrodeacordocomumadeterminadaquery:

SELECTa.nomeFROMalunoaWHEREEXISTS(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 querychamamosdesubquery.Masoqueaconteceuexatamentenessaquery?QuandoutilizamosoEXISTS()indicamosquequeremosoretornodetodososalunosnomesdosalunos(a.nome)queestãonatabelaaluno,porém,queremosapenasseexistirumamatrículaparaessealunoEXISTS(SELECTm.idFROMmatriculamWHEREm.aluno_id=a.id).

Perceba que novamente estamos retornando os alunos matriculados sendo que precisamos dosalunosquenãoestãomatriculados.Nessecaso,podemosutilizarainstruçãoNOTparafazeranegação,ouseja,pararetornarosalunosquenãopossuemmatrícula:

SELECTa.nomeFROMalunoaWHERENOTEXISTS(SELECTm.idFROMmatriculamWHEREm.aluno_id=a.id);

+------------------+|nome|+------------------+|PaulodaSilva||CarlosCunha||JosedaSilva||DaniloCunha||ZilmiraJosé||CristaldoSantos||OsmirFerreira||ClaudioSoares|+------------------+

Conseguimosachartodosalunosquefazempartedosistemaequenãopossuemumamatrícula,algo

7.1SUBQUERIES

7.1SUBQUERIES 65

quenãoéesperado...Provavelmenteessesalunoshaviamcanceladoamatrículamasaindasim,existiaocadastro deles, sabendo dessas informações temos a capacidade de criar relatórios com informaçõesrelevantespara,porexemplo,osetorcomercialdessainstituiçãoentraremcontatocomosalunosnãomatriculadosetentarefetuarumavenda.

Ainstituiçãosubiualgunsexercícioseprecisasaberquaisdessesexercíciosnãoforamrespondidos.Vamosnovamenteobservaroresultadoqueseesperautilizandoumaplanilhacomoexemplo:

Figura7.2:Planilhaexemplo

Comohavíamos visto anteriormente, existem as tabelas,exercicio e resposta, vamos umaolhadanatabelaexercicio:

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ãovamospegartodososexercíciosquenãoforamrespondidosutilizandonovamenteoNOTEXISTS:

SELECT*FROMexercicioeWHERENOTEXISTS(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 oscamposdesejados:

SELECTe.id,e.perguntaFROMexercicioeWHERENOTEXISTS(SELECTr.idFROMrespostarWHEREr.exercicio_id=e.id);

+----+------------------------------+|id|pergunta|+----+------------------------------+|8|comofunciona?||9|Comofuncionaaweb?||10|Quelinguagenspossoajudar?||11|OqueehMVC?||12|Frameworksqueusam?||14|Oqueéuminterceptor?||15|quandousar?|+----+------------------------------+

Opessoaldocomercialda instituição, informouqueexistemalgunscursosquenãotemnenhumamatrícula.Vamosverificarcomoéesperadodesseresultadopelanossaplanilha:

Figura7.3:Planilhaexemplo

Damesma forma que retornamos todos os exercícios que não tinha respostas, podemos retornartodososcursosquenãopossuemmatrícula:

SELECTc.nomeFROMcursocWHERENOTEXISTS(SELECTm.idFROMmatriculamWHEREm.curso_id=c.id);

+--------------------------------+|nome|+--------------------------------+|Javaeorientaçãoaobjetos||DesenvolvimentomobilecomiOS||RubyonRails||PHPeMySql|+--------------------------------+

7.1SUBQUERIES 67

Vejaquequeriesmuitoparecidaspodemresolverproblemasdiferentes!

A instituição informouque tiveramváriosexercíciosquenão foramrespondidospelosalunosnoscursosqueforamrealizadosrecentemente.Vamosverificarquemforamessesalunos,paraverificarmosomotivo de não ter respondido, se foi um problema no sistema ou na base de dados... Novamentevamosverificaroqueseesperadesseresultadonumaplanilha:

Figura7.4:Planilhaexemplo

Vamostentarfazeressaquery.Começaremosretornandooalunojuntandoatabelaalunocomatabelamatricula.

SELECTa.nomeFROMalunoaJOINmatriculamONm.aluno_id=a.id

Agoravamosjuntartambématabelacursoeretornaroscursostambém:

SELECTa.nome,c.nomeFROMalunoaJOINmatriculamONm.aluno_id=a.idJOINcursocONm.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émaindaprecisamos informarquequeremosapenasosalunosquenãoresponderamosexercíciosdessesdealgumdessescursos.EntãoadicionaremosagoraoNOTEXISTS():

SELECTa.nome,c.nomeFROMalunoaJOINmatriculamONm.aluno_id=a.idJOINcursocONm.curso_id=c.id

68 7.1SUBQUERIES

WHERENOTEXISTS(SELECTr.aluno_idFROMrespostarWHEREr.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 matriculadosresposdamosexercícios,ouseja,nãopodeexistirumarespostanatabelarespostacomumiddeumaluno(aluno_id)quenãoestejamatriculado.Vamosprimeiroverificartodososalunosmatriculadosqueresponderamosexercícios:

SELECTr.id,a.nomeFROMalunoaJOINrespostarONr.aluno_id=a.idWHEREEXISTS(SELECTm.aluno_idFROMmatriculamWHEREm.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.Vamosverificaragoraosqueresponderam,poŕemnãoestãomatriculados:

SELECTr.id,a.nomeFROMalunoaJOINrespostarONr.aluno_id=a.id

7.1SUBQUERIES 69

WHERENOTEXISTS(SELECTm.aluno_idFROMmatriculamWHEREm.aluno_id=a.id);

Emptyset(0,00sec)

Oresultadosaiuconformeoesperado, issosignifica,quenãoexistemrespostasdealunosquenãopossuemmatrícula!

Nesse capítulo aprendemos a criar queries capazes de retornar valores caso exista, ou não, umaassociação entre duas tabelas, como por exemplo, se um aluno está matriculado ou se um alunorespondeualgumexercíciopormeiodafunçãoEXISTS().Aprendemostambémoquesãosubqueriescomono exemplo em que passamos umaquery como parâmetro para a funçãoEXISTS() resolverdiversosproblemas,alémdisso,analisamosqueváriasqueries"similadres"temacapacidadederesolverproblemasdistintosutilizandooEXISTS().Vamosparaosexercícios?

1. Baixeoschemadobancodedadosaqui

Crieobancodedadosescola:

createdatabaseescola

Importe-onoseuMysqlcomoseguintecomando,diretonoterminal:

mysql-uroot-pescola<escola.sql

FaçaumSELECTqualquerparagarantirqueosdadosestãolá.

DICA:Salveoarquivoescola.sqlemumlugardefácilacessopeloterminal.Vocêdeverodarocomandopara importar o schemanomesmo lugar onde estar o aquivoescola.sql. Por exemplo,salveoarquivoSQLnapasta\alura-mysql.Depoisabraumterminaleentrenessapasta:

cd\aula-mysqlmysql-uroot-pescola<escola.sql

NoWindowsvocêpodeusarocomandodirépara listarosarquivosdapastaatualna linhadecomando. Ou seja, ao executar dir na pasta aula-mysql você deve encontrar o arquivoescola.sql.

1. Busquetodososalunosquenãotenhamnenhumamatrículanoscursos.

2. Busquetodososalunosquenãotiveramnenhumamatrículanosúltimos45dias,usandoainstruçãoEXISTS.

7.2RESUMINDO

EXERCÍCIOS

70 7.2RESUMINDO

3. ÉpossívelfazeramesmaconsultasemusarEXISTS?Quaissão?

7.2RESUMINDO 71

CAPÍTULO8

Ainstuiçãosolicitouamédiadetodososcursosparafazerumacomparaçãodenotasparaverificarsetodososcursospossuemamesmamédia,quaiscursostemmenoresnotasequaispossuemasmaioresnotas.Vamosverificaraestruturadealgumastabelasdanossabasededados,começaremospelatabelacurso:

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á podemosperceber que existeuma relação entrecurso desecao.Vamos tambémdarumaolhadanatabelaexercicio:

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

DESCresposta

AGRUPANDODADOSCOMGROUPBY

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áassociadacomatabelaexercício.Porfim,vamosverificaratabelanota:

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ãovamosmontaranossaqueryporpartes.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, nessecaso,juntaratabelarespostacomatabelanota:

SELECTn.notaFROMnotanJOINrespostarONn.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óximoJOINentreatabelarespostaeatabelaexercicio:

SELECTn.notaFROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.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.notaFROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.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.notaFROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.id

8AGRUPANDODADOSCOMGROUPBY 75

JOINsecaosONe.secao_id=s.idJOINcursocONs.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 detodososalunosporcursoumaauma,porémnósprecisamosdamédiaporcurso!NoMySQL,podemosutilizarafunçãoAVG()paratiraramédia:

SELECTAVG(n.nota)FROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.id;

+-------------+|AVG(n.nota)|+-------------+|5.740741|+-------------+

Observequefoiretornadoapenasumvalor,seráqueessamédiaéigualparatodososcursos?Vamostentarretornaroscursoseverificarmos:

SELECTc.nome,AVG(n.nota)FROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.id;

76 8AGRUPANDODADOSCOMGROUPBY

+----------------------+-------------+|nome|AVG(n.nota)|+----------------------+-------------+|SQLebancodedados|5.740741|+----------------------+-------------+

Apenas1curso?Nãoeraesseoresultadoqueesperávamos!QuandoutilizamosafunçãoAVG()elacalculatodososvaloresexistentesdaqueryeretornaamédia,porémemapenasumalinha!ParaqueafunçãoAVG()calculeamédiadecadacurso,precisamosinformarquequeremosagruparamédiaparaumadeterminadacoluna,nessecaso,acolunac.nome,ouseja,paracadacursodiferentequeremosquecalculeamédia.ParaagruparmosumacolunautilizamosainstruçãoGROUPBY,informandoacolunaquequeremosagrupar:

SELECTc.nome,AVG(n.nota)FROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.idGROUPBYc.nome;

+---------------------------------+-------------+|nome|AVG(n.nota)|+---------------------------------+-------------+|C#eorientaçãoaobjetos|4.857143||DesenvolvimentowebcomVRaptor|8.000000||Scrumemétodoságeis|5.777778||SQLebancodedados|6.100000|+---------------------------------+-------------+

Opessoaldocomercialdainstituiçãoinformouquealgunsalunosestãoreclamandopelaquantidadede exercícios nos cursos. Então vamos verificar quantos exercícios existempara cada curso. PrimeirovamosverificarquantosexercíciosexistemnobancousandoafunçãoCOUNT():

SELECTCOUNT(*)FROMexercicio;

+----------+|COUNT(*)|+----------+|31|+----------+

Retormosaquantidadedetodososexercícios,porémnósprecisamossaberototaldeexercíciosparacadacurso,ouseja,precisamosjuntaratabelacurso.Porém,parajuntaratabelacurso,teremosquejuntaratabelasecao:

SELECTCOUNT(*)FROMexercicioeJOINsecaosONe.secao_id=s.id

Agorapodemosjuntaratabelacursoeretornaronomedocursotambém:

SELECTc.nome,COUNT(*)FROMexercicioeJOINsecaosONe.secao_id=s.idJOINcursocONs.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ãovamosadicionaroGROUPBY:

SELECTc.nome,COUNT(*)FROMexercicioeJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.idGROUPBYc.nome;

+---------------------------------+----------+|nome|COUNT(*)|+---------------------------------+----------+|C#eorientaçãoaobjetos|7||DesenvolvimentowebcomVRaptor|7||Scrumemétodoságeis|9||SQLebancodedados|8|+---------------------------------+----------+

Notequeonomedacolunaquecontatodososexercíciosestáumpoucoestranha,vamosadicionarumaliasparamelhoraroresultado:

SELECTc.nome,COUNT(*)AScontagemFROMexercicioeJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.idGROUPBYc.nome;

+---------------------------------+----------+|nome|contagem|+---------------------------------+----------+|C#eorientaçãoaobjetos|7||DesenvolvimentowebcomVRaptor|7||Scrumemétodoságeis|9||SQLebancodedados|8|+---------------------------------+----------+

Agoraorelatóriofazmuitomaissentido.

TodofinaldesemestrenósprecisamosenviarumrelatórioparaoMECinformandoquantosalunosestãomatriculadosemcadacursoda instituição.Faremosnovamenteanossaqueryporpartes,vamosretornarprimeirotodososcursos:

SELECTc.nomeFROMcursoc

Vamosjuntaratabelamatricula:

SELECTc.nomeFROMcursocJOINmatriculamONm.curso_id=c.id

Agoravamosjuntarosalunostambém:

SELECTc.nomeFROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.id;

78 8AGRUPANDODADOSCOMGROUPBY

Precisamosagoracontaraquantidadedealunos:

SELECTc.nome,COUNT(a.id)ASquantidadeFROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.id;

+----------------------+------------+|nome|quantidade|+----------------------+------------+|SQLebancodedados|14|+----------------------+------------+

Lembre-sequeprecisamosagruparacontagempelonomedocurso:

SELECTc.nome,COUNT(a.id)ASquantidadeFROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.idGROUPBYc.nome;

+------------------------------------+------------+|nome|quantidade|+------------------------------------+------------+|C#eorientaçãoaobjetos|4||DesenvolvimentomobilecomAndroid|2||DesenvolvimentowebcomVRaptor|2||Scrumemétodoságeis|2||SQLebancodedados|4|+------------------------------------+------------+

Agoraconseguimosrealizaronossorelatórioconformeoesperado.

VimosnessecapítulocomopodemosgerarrelatóriosutilizandofunçõescomoAVG()eCOUNT().Vimostambémque,seprecisamosretornarascolunasparaverificarqualéovalordecadalinha,comoporexemplo,amédiadecadacurso,precisamosagruparessascolunaspormeiodoGROUPBY.Vamosparaosexercícios?

1. Exibaamédiadasnotasporcurso.

2. Devolva o curso e asmé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 pormais de um campode uma só vez. Por exemplo, se quisermos amédia de

8.1RESUMINDO

EXERCÍCIOS

8.1RESUMINDO 79

notasporalunoporcurso,podemosfazerGROUPBYaluno.id,curso.id.

80 8.1RESUMINDO

CAPÍTULO9

Todo o fimde semestre, a instituição de ensino precisamontar os boletins dos alunos. Então vamosmontaraqueryqueretornará todasas informaçõesparamontaroboletim.Começaremosretornandotodasasnotasdosalunos:

SELECTn.notaFROMnotan

Agoravamosassociaracomasrespostascomasnotas:

SELECTn.notaFROMnotanJOINrespostarONr.id=n.resposta_id

Associaremosagoracomosexercícioscomasrespostas:

SELECTn.notaFROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_id

Agoraassociaremosaseçãocomosexercícios:

SELECTn.notaFROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_id

Agoraocursocomaseção:

SELECTn.notaFROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_id

Porfim,arespostacomoaluno:

SELECTn.notaFROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_idJOINalunoaONa.id=r.aluno_id;

Verificandooresultado:

+-------+|nota|+-------+|8.00|

FILTRANDOAGREGAÇÕESEOHAVING

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

Observequeestamos fazendoqueries grandes, aconselhamosque,nomomentoqueprecisar fazerumaquery complexa, faça queries menores e testes seus resultados, pois se existir alguma instruçãoerrada,émaisfácildeidentificaroproblema.

Agora que associamos todas as nossas tabelas necessárias, vamos tirar amédia com a função deagregaçãoAVG()queécapazdetirarmédias,conformevistoduranteocurso:

SELECTAVG(n.nota)FROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_idJOINalunoaONa.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 doscursos.Entãovamosadicioná-los:

SELECTa.nome,c.nome,AVG(n.nota)FROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_idJOINalunoaONa.id=r.aluno_id;

+----------------+----------------------+-------------+

82 9FILTRANDOAGREGAÇÕESEOHAVING

|nome|nome|AVG(n.nota)|+----------------+----------------------+-------------+|JoãodaSilva|SQLebancodedados|5.740741|+----------------+----------------------+-------------+

Lembre-sequeestamoslidandocomumafunçãodeagregação,ouseja,senãoinformarmosaformaqueelaprecisaagruparascolunas,elaretornaráapenasumalinha!Porém,precisamossemprepensaremqual tipode agrupamento énecessário,nesse casoqueremosquemostre amédiade acadaaluno,entãoagruparemospelosalunos,porémtambémquequeremosqueacadacursoqueoalunosfezmostreasua:

SELECTa.nome,c.nome,AVG(n.nota)FROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_idJOINalunoaONa.id=r.aluno_idGROUPBYa.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|+-----------------+---------------------------------+-------------+

Retornamostodasasmédiasdosalunos,porémainstituiçãoprecisadeumrelatórioseparadoparatodososalunosquereprovaram,ouseja,quetiraramnotabaixa,nessecasomédiasmenoresque5.DeacordocomoquevimosatéagorabastariaadicionarmosumWHERE:

SELECTa.nome,c.nome,AVG(n.nota)FROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_idJOINalunoaONa.id=r.aluno_idWHEREAVG(n.nota)<5GROUPBYa.nome,c.nome;

ERROR1111(HY000):Invaliduseofgroupfunction

Nesse caso, estamos tentandoadicionar condiçõesparauma funçãodeagregação,porém,quandoqueremosadicionarcondiçõesparafunçõesdeagregaçãoprecisamosutilizaroHAVING ao invésdeWHERE:

SELECTa.nome,c.nome,AVG(n.nota)FROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_id

9.1CONDIÇÕESCOMHAVING

9.1CONDIÇÕESCOMHAVING 83

JOINalunoaONa.id=r.aluno_idHAVINGAVG(n.nota)<5GROUPBYa.nome,c.nome;

ERROR1064(42000):YouhaveanerrorinyourSQLsyntax;checkthemanualthatcorrespondstoyourMySQLserverversionfortherightsyntaxtousenear'GROUPBYa.nome,c.nome'atline8

AlémdeutilizarmosoHAVING existeumpequenodetalhe, precisamos sempre agrupar antes ascolunaspeloGROUPBYparadepoisutilizarmosoHAVING:

SELECTa.nome,c.nome,AVG(n.nota)FROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_idJOINalunoaONa.id=r.aluno_idGROUPBYa.nome,c.nomeHAVINGAVG(n.nota)<5;

+---------------+-----------------------------+-------------+|nome|nome|AVG(n.nota)|+---------------+-----------------------------+-------------+|RenataAlonso|C#eorientaçãoaobjetos|4.857143|+---------------+-----------------------------+-------------+

Agoraconseguimosretornaroalunoqueteveamédiaabaixode5.Esequiséssemospegartodososalunosqueaprovaram?Ésimples,bastariaalterarosinalpara>=:

SELECTa.nome,c.nome,AVG(n.nota)FROMnotanJOINrespostarONr.id=n.resposta_idJOINexercicioeONe.id=r.exercicio_idJOINsecaosONs.id=e.secao_idJOINcursocONc.id=s.curso_idJOINalunoaONa.id=r.aluno_idGROUPBYa.nome,c.nomeHAVINGAVG(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 poucosalunosparatomarumadecisãosevaimanteroscursosouseirácancelá-los.Entãovamosnovamentefazeranossaqueryporpassos,primeirovamoscomeçarselecionandooscursos:

SELECTc.nomeFROMcursoc

Agoravamosjuntarocursocomamatrículaeamatrículacomoalunoeverificaroresultado:

SELECTc.nomeFROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.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)FROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.id;

Háumdetalhenessaquery,poisqueremoscontartodososalunosdecadacurso,ouseja,precisamosagruparoscursos!

SELECTc.nome,COUNT(a.id)FROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.idGROUPBYc.nome;

+------------------------------------+-------------+|nome|COUNT(a.id)|+------------------------------------+-------------+|C#eorientaçãoaobjetos|4||DesenvolvimentomobilecomAndroid|2||DesenvolvimentowebcomVRaptor|2||Scrumemétodoságeis|2||SQLebancodedados|4|+------------------------------------+-------------+

Aquery funcionou,porémprecisamos saber apenasos cursosque tempoucos alunos,nesse caso,cursosquetenhammenosde10alunos:

SELECTc.nome,COUNT(a.id)FROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.idGROUPBYc.nomeHAVINGCOUNT(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.

Sabemos que para adicionarmos filtros apenas para colunas utilizamos a instrução WHERE eindicamostodasaspeculiaridadesnecessárias,porémquandoprecisamosadicionarfiltrosparafunçõesde agregação, comopor exemplo oAVG(), precisamos utilizar a instruçãoHAVING. Além disso, ésempre bom lembrar que, quando estamos desenvolvendo queries grandes, é recomendado que façapassa-a-passoqueriesmenores, ou seja, resolvaosmenoresproblemas juntando cada tabelapor vez eteste para verificar se está funcionando, pois isso ajuda a verificar aonde está o problema da query.Vamosparaosexercícios?

1. Qualéaprincipaldiferençaentreasinstruçõeshavingewheredosql?

2. Devolva todos os alunos, cursos e amédia de suas notas. Lembre-se de agrupar por aluno e porcurso.Filtretambémpelanota:sómostrealunoscomnotamédiamenordoque5.

3. Exibatodososcursoseasuaquantidadedematrículas.Mas,exibasomentecursosquetenhammaisde1matrícula.

4. Exibaonomedocursoeaquantidadede seçõesqueexistenele.Mostre sócursoscommaisde3seções.

9.2RESUMINDO

EXERCÍCIOS

86 9.2RESUMINDO

CAPÍTULO10

Osetordefinanceirodessainstituição,solicitouumrelatórioinformandotodasasformasdepagamentocadastradasnobancodedadosparaverificar seestádeacordocomoqueeles trabalham.Nabasededados,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.Eprecisamospegarumrelató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|

MÚLTIPLOSVALORESNACONDIÇÃOEOIN

10MÚLTIPLOSVALORESNACONDIÇÃOEOIN 87

|PAGA_PF||PAGA_PJ|+-------------+

Vejaqueforamretornadostiposdepagamentoiguais,porémprecisamosenviarumrelatórioapenascomos tipos de pagamentodistintos. Para retornamos os valores distintos de uma coluna podemosutilizarainstruçãoDISTINCT:

SELECTDISTINCTm.tipoFROMmatriculam;

+-------------+|tipo|+-------------+|PAGA_PF||PAGA_PJ||PAGA_CHEQUE||PAGA_BOLETO|+-------------+

Conseguimosretornarorelatóriodasformasdepagamento,porémosetorfinanceiroaindaprecisasaberdemaisinformações.AgorafoisolicitadoqueenviasseumrelatóriocomoscursoseaquantidadedealunosquepossuemotipodepagamentoPJ.Vamosverificaroexemploemumaplanilha:

Figura10.2:Planilhaexemplo

SabemosqueorelatórioésobreaquantidadedematrículasqueforampagascomoPJ,precisamoscontar,ouseja,usaremosafunçãoCOUNT().Vamoscomeçaracontaraquantidadedematrículas:

SELECTCOUNT(m.id)FROMmatriculam;

+-------------+|COUNT(m.id)|+-------------+|14|+-------------+

Agoravamosjuntarcomatabelacursoeexibironomedocurso:

SELECTc.nome,COUNT(m.id)FROMmatriculamJOINcursocONm.curso_id=c.id;

+----------------------+-------------+|nome|COUNT(m.id)|+----------------------+-------------+|SQLebancodedados|14|+----------------------+-------------+

Observequefoiretornadoapenasumalinha!IssosignificaqueafunçãoCOUNT() tambéméumafunçãodeagregação,ouseja,sequeremosadicionarmaiscolunasnanossaquery,precisamosagrupá-las.Entãovamosagruparonomedocurso:

88 10MÚLTIPLOSVALORESNACONDIÇÃOEOIN

SELECTc.nome,COUNT(m.id)FROMmatriculamJOINcursocONm.curso_id=c.idGROUPBYc.nome;

+------------------------------------+-------------+|nome|COUNT(m.id)|+------------------------------------+-------------+|C#eorientaçãoaobjetos|4||DesenvolvimentomobilecomAndroid|2||DesenvolvimentowebcomVRaptor|2||Scrumemétodoságeis|2||SQLebancodedados|4|+------------------------------------+-------------+

Conseguimos retornar todasos cursoseaquantidadedematrículas,porémprecisamos filtrarportipodepagamentoPJ.EntãovamosadicionarumWHERE:

SELECTc.nome,COUNT(m.id)FROMmatriculamJOINcursocONm.curso_id=c.idWHEREm.tipo='PAGA_PJ'GROUPBYc.nome;

+------------------------------------+-------------+|nome|COUNT(m.id)|+------------------------------------+-------------+|C#eorientaçãoaobjetos|1||DesenvolvimentomobilecomAndroid|2||DesenvolvimentowebcomVRaptor|1||Scrumemétodoságeis|1||SQLebancodedados|1|+------------------------------------+-------------+

Osetorfinanceirodainstituiçãoprecisademaisdetalhessobreostiposdepagamentodecadacurso,elesprecisamdeumrelatóriosimilaraoquefizemos,porémparatodosquesejampagamentoPJePF.Paradiferenciarotipodepagamento,precisaremosadicionaracolunadom.tipo:

SELECTc.nome,COUNT(m.id),m.tipoFROMmatriculamJOINcursocONm.curso_id=c.idWHEREm.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|+------------------------------------+-------------+---------+

FILTROSUTILIZANDOOIN

10MÚLTIPLOSVALORESNACONDIÇÃOEOIN 89

Suponhamosqueagoraprecisamosretornar tambémosque forampagosemboletooucheque.Oquepoderíamosadicionarnaquery?MaisORs?

SELECTc.nome,COUNT(m.id),m.tipoFROMmatriculamJOINcursocONm.curso_id=c.idWHEREm.tipo='PAGA_PJ'ORm.tipo='PAGA_PF'ORm.tipo='PAGA_BOLETO'ORm.tipo='PAGA_CHEQUE'ORm.tipo='...'ORm.tipo='...'GROUPBYc.nome,m.tipo;

Resolveria,maspercebaque anossaquery a cadanovo tipodepagamento anossaquery tende acrescer,dificultandoaleitura...EmSQL,existeainstruçãoINquepermiteespecificarmosmaisdeumvalorqueprecisamosfiltraraomesmotempoparaumadeterminadacoluna:

SELECTc.nome,COUNT(m.id),m.tipoFROMmatriculamJOINcursocONm.curso_id=c.idWHEREm.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,bastaadicionarmosdentrodoINeanossaqueryfuncionarácorretamente.

Ainstituiçãonomeou3alunoscomoosmaisdestacadosnosúltimoscursosrealizamosegostariadesaberquaisforamtodososcursosqueelesfizeram.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.Agoraquesabemososidspodemosverificarosseuscursos.Entãovamoscomeçaranossaqueryretornandotodososcursos:

SELECTc.nomeFROMcursoc;

Agoravamosjuntarocursocomamatrícula:

SELECTc.nomeFROMcursocJOINmatriculamONm.curso_id=c.id;

Porfim,vamosjuntaramatriculacomoalunoeretornaronomedoaluno:

SELECTa.nome,c.nomeFROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.id;

Fizemostodasasjunções,agorasóprecisamosdofiltro.Precisamosretornaroscursosdos3alunosaomesmotempo,podemosutilizarainstruçãoIN:

SELECTa.nome,c.nomeFROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.idWHEREa.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 todosos cursosdos3 alunos,porémainda táumpoucodesorganizado, entãovamosordenarpelonomedosalunosutilizandooORDERBY:

SELECTa.nome,c.nomeFROMcursocJOINmatriculamONm.curso_id=c.idJOINalunoaONm.aluno_id=a.idWHEREa.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.NETeopessoaldocomercialprecisadivulgaressescursosparaosex-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.nomeFROMalunoaJOINmatriculamONm.aluno_id=a.idJOINcursocONm.curso_id=c.id;

AgorautilizaremosofiltropararetornartantoocursodeSQL(1),quantoocursodeC#(4):

SELECTa.nome,c.nomeFROMalunoaJOINmatriculamONm.aluno_id=a.idJOINcursocONm.curso_id=c.idWHEREc.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.nomeFROMalunoaJOINmatriculamONm.aluno_id=a.idJOINcursocONm.curso_id=c.idWHEREc.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 pararealizarosnovoscursosde.NET.

Nesse capítulo vimos que quando precisamos saber todos os valores de uma determinada colunapodemosutilizarainstruçãoDISTINCTpararetornartodososvaloresdistintos,ouseja,semnenhumarepetiçã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 sejaretornado,aoinvésdeficarpreenchendoanossaquerycomváriosORs.Vamosparaosexercí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 doscursoscomID1e3.

10.1RESUMINDO

EXERCÍCIOS

10.1RESUMINDO 93

CAPÍTULO11

Ainstituiçãoprecisadeumrelatóriomaisrobusto,comasseguintesinformações:Precisadonomedoalunoecurso,amédiadoalunoemrelaçãoaocursoeadiferençaentreamédiadoalunoeamédiageraldocurso.Demonstrandoemumaplanilha,oresultadoqueseesperaéoseguinte:

Figura11.1:Planilhaexemplo

ObservequeoalunoAlexfezocursodeJavatirou6demédiaeadiferençaentreamédiadeleeamédiageralparaocursodeJavafoi-1,issosignificaqueamédiageraldocursodeJavaé7,ouseja,6-7.VamosanalisaroalunoGuilherme,vejaqueeletirou6demédiaeadiferençafoi0,poisamédiageraldo cursode SQL é 6, ou seja, 6 - 6. Por fim, a alunaRenata tiroumédia 7no cursodeC#, porémadiferençafoi1,issosignificaqueamédiageralé6,ouseja,7-6.

Comovocêmontariaessaquery?Aparentementeéumpoucocomplexa...Entãovamoscomeçarporpartesdamesmaformaquefizemosanteriormente.Começaremospelatabelanota:

SELECTn.notaFROMnotan;

Agoravamosjuntarastabelasrespostaeexercicioevamosverificaroresultado:

SELECTn.notaFROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.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|

SUB-QUERIES

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.Vamosadicionarastabelasdesecaoecurso,porém,dessavezvamosadicionaronomedocurso:

SELECTc.nome,n.notaFROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.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.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.idJOINalunoaONr.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ãodetodasasnotas.EntãovamosutilizarafunçãoAVG()pararetornaramédiadoaluno.Lembre-sequeafunçãoAVG()éumafunçãodeagregação,ouseja,precisamosagruparoalunoeocursotambém:

SELECTa.nome,c.nome,AVG(n.nota)FROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.idJOINalunoaONr.aluno_id=a.idGROUPBYa.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émainda não temos amédia geral, então como podemos pegar amédia geral? Vamos verificar a tabelanota:

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,parapegarmosamédiageralusaremosoAVG():

SELECTAVG(n.nota)FROMnotan;

+-------------+|AVG(n.nota)|+-------------+|5.740741|+-------------+

Conseguimosamédiageral,agoravamosadicionaracolunadiferença.Antesdecomeçarafazeracolunadiferençavamosnomearacolunademédiadoalunoparamelhoraravisualização:

SELECTa.nome,c.nome,AVG(n.nota)asmedia_alunoFROMnota...

Acolunadiferençaprecisadainformaçãodamedia_aluno -mediageral,porém,nósnão temosnenhumacolunaparaamédiageral,eoresultadoqueprecisamosestáemumaquerydiferente...Comopodemos 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)asdiferencaFROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.idJOINalunoaONr.aluno_id=a.idGROUPBYa.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 talretornamosamédiageraltambém?

SELECTa.nome,c.nome,AVG(n.nota)asmedia_aluno,(SELECTAVG(n.nota)FROMnotan)asmedia_geral,AVG(n.nota)-(SELECTAVG(n.nota)FROMnotan)asdiferencaFROMnotanJOINrespostarONn.resposta_id=r.idJOINexercicioeONr.exercicio_id=e.idJOINsecaosONe.secao_id=s.idJOINcursocONs.curso_id=c.idJOINalunoaONr.aluno_id=a.idGROUPBYa.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 oresultadodasubquery(SELECTAVG(n.nota)FROMnotan)foideapenasumalinhaeéjustamenteporessemotivoqueconseguimosefetuaroperaçõesaritméticascomo,nessecaso,asubtração.Seoresultadofossemaisdeumalinha,nãoseriapossívelrealizaroperações.

Ainstituiçãoprecisadeumrelatóriodoaproveitamentodosalunosnoscursos,ouseja,precisamossaberseelesestãorespondendotodososexercícios,entãoiremosbuscaronúmeroderespostasquecadarespondeualunoindividualmente.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 aquery que conta as respostas e sabemos aquery que retornamos alunos, então vamosadicionar a query que conta as respostas dentro da que retorna os alunos, ou seja, vamos fazernovamenteumasubquery!

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

Observequeosresultadosdaquantidadederespostas foramiguaispara todososalunos,poisnãoadicionamos nenhum filtro na subquery. Para resolver o problema, basta adicionar um WHEREindicandooqueprecisaserfiltrado,nessecaso,oiddosalunosretornadosnaqueryprincipal:

SELECTa.nome,(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASquantidade_respostasFROMalunoa;

+------------------+----------------------+

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,elaprecisasaberquantasmatrículasumalunotem,ouseja,aoínvesderesposta,informaremosasmatrículas.Entãovamosapenassubstituirasinformaçõesdasrespostaspelasinformaçõesdamatrícula:

SELECTa.nome,(SELECTCOUNT(m.id)FROMmatriculamWHEREm.aluno_id=a.id)ASquantidade_matriculaFROMalunoa;

+------------------+----------------------+|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émfizemosisso separadamente, porém agora precisamos juntar essas informações para montar em um únicorelatórioquemostre,onomedoaluno,aquantidadederespostaseaquantidadedematrículas.Entãovamospartirdoprincípio,ouseja,fazeraqueryqueretornatodososalunos:

SELECTa.nomeFROMalunoa;

Agoravamospegaraquantidadederespostas:

SELECTCOUNT(r.id)FROMrespostar;

100 11SUB-QUERIES

Eentãovamospegaraquantidadedematriculas:

SELECTCOUNT(m.id)FROMmatriculam;

TemostodososSELECTsqueresolvemumdeterminadoproblema,ouseja,agoraprecisamosjuntartodos eles para resolver a nova necessidade. Então vamos adicionar as duas queries que contam asmatrículaseasrespostasdentrodaqueryprincipal,ouseja,aqueretornaosalunos:

SELECTa.nome,(SELECTCOUNT(m.id)FROMmatriculamWHEREm.aluno_id=a.id)ASquantidade_matricula,(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASquantidade_respostasFROMalunoa;

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

Vimosquenessecapítuloaprendemosautilizarsubqueriespararesolverdiversosproblemas,comopor exemplo contar a quantidade de matriculas ou de respostas de um aluno. Também vimos quepodemosaplicaroperaçõesaritméticasutilizandosubqueries,porémésempre importante lembrarquesópodemosrealizaressetipodeoperaçõesdesdequeasubquerieretorneumaúnicalinha.Entãovamosparaosexercícios?

1. Exibaamédiadasnotasporaluno,alémdeumacolunacomadiferençaentreamédiadoalunoeamédiageral.Usesub-queriesparaisso.

2. Qualéoproblemadeseusarsub-queries?

3. Exiba a quantidadedematrículas por curso.Alémdisso, exiba a divisão entrematrículas naquelecursoematrículastotais.

11.1RESUMINDO

EXERCÍCIOS

11.1RESUMINDO 101

102 11.1RESUMINDO

CAPÍTULO12

Osinstrutoresdainstituiçãopediramumrelatóriocomosalunosquesãomaisparticipativosnasaladeaula, ou seja, queremos retornar os alunos que responderam mais exercícios. Consequentementeencontraremos também os alunos que não estão participando muito, então já aproveitamos econversamoscomelesparaentenderoqueestáacontecendo.Entãocomeçaremosretornandooaluno:

SELECTa.nomeFROMalunon;

Agora vamos contar a quantidade de respostas pormeio da funçãoCOUNT() e agrupando pelonomedoaluno:

SELECTa.nome,COUNT(r.id)ASrespostasFROMalunoaJOINrespostarONr.aluno_id=a.idGROUPBYa.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á trazendoexatamenteoqueagenteesperava...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 suasrespostas. Provavelmente não está sendo retornando os alunos que não possuem respostas! Vamosverificaroqueestáacontecendoexatamente.Vamospegarumalunoquenãofoiretornado,comoodeid5.Quantasrespostaseletem?

SELECTr.idFROMrespostarWHEREr.aluno_id=5;

+------------------+|id|+------------------+

ENTENDENDOOLEFTJOIN

12ENTENDENDOOLEFTJOIN 103

|0|+------------------+

Tudo bem, ele não respondeu,mas como ele foi desaparecer daquela query nossa?Vamos pegaroutroalunoquedesapareceu,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 nenhumaresposta.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 sendoretornadosnaquelaprimeiraquery.Porémnósqueremostambémqueretorneosalunossemrespostas...Vamostentardeumaoutramaneira,vamosretornaronomedoalunoearespostaqueelerespondeu:

SELECTa.nome,r.resposta_dadaFROMalunoaJOINrespostarONr.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,masgeralmenteehlevarpraumservidorquetanainternet|+----+-----------------+----------+------------------------------------------------------------------

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 eresposta estamos trazendo apenas todos os registros que possuem o id da tabela aluno e oaluno_iddatabelaresposta.

OqueoSQLfaz tambéméquetodososalunoscujooidnãoestejanacolunaaluno_id nãoserãoretornados!Istoé,elesótraráparanósalguémqueoJOINtenhavalorigualnasduastabelas.Sesóestápresenteemumadastabelas,eleignora.

EmSQL,existeumJOINdiferentequepermiteoretornodealunosquetambémnãopossuamo id na tabela que está sendo associada. Queremos pegar todo mundo da tabela da esquerda,independentementedeexistirounãoumvalornatabeladadireita.Éumtaldejoindeesquerda,oLEFTJOIN,ouseja,eletrarátodososregistrosdatabeladaesquerdamesmoquenãoexistaumaassociaçãonatabeladadireita:

SELECTa.id,a.nome,r.aluno_id,r.resposta_dadaFROMalunoaLEFTJOINrespostarONr.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,masgeralmenteehlevarpraumservidorquetanainternet||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)ASrespostasFROMalunoaLEFTJOINrespostarONr.aluno_id=a.idGROUPBYa.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ãotenharespondidopelomenosumaresposta.

Vamos supor que ao invés de retornar todos os alunos e suas respostas,mesmoque o alunonãotenha nenhuma resposta, queremos faer o contrário, ou seja, retornar todos as respostas que foramrespondidas e as que não foram respondidas. Vamos verificar se existe alguma resposta que não foirespondidaporumaluno:

SELECTr.idFROMrespostarWHEREr.aluno_idISNULL;

Emptyset(0,00sec)

Nãoexisteexercíciosemresposta,entãovamosinserirumarespostasemassociaraumaluno:

INSERTINTOresposta(resposta_dada)VALUES('xvale15.');

QueryOK,1rowaffected(0,01sec)

Severificarmosnovamenteseexisteumarespostaquenãofoirespondidaporumaluno:

SELECTr.idFROMrespostarWHEREr.aluno_idISNULL;

+----+|id|+----+|28|+----+

Agoraexisteumarespostaquenãofoiassociadaaumaluno.DamesmaformaqueutilizamosumJOIN diferente para pegar todos os dados da tabela da esquerda (LEFT) mesmo que não tenhaassociaçãocomatabelaqueestásendojuntada,existetambémoJOINquefaráoprocedimento,porémparaatabeladadireita(RIGHT),queéotaldoRIGHTJOIN:

12.1RIGHTJOIN

12.1RIGHTJOIN 109

SELECTa.nome,r.resposta_dadaFROMalunoaRIGHTJOINrespostarONr.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.

QuandoutilizamosapenasoJOINsignificaquequeremosretornartodososregistrosquetenhamumaassociação,ouseja,queexistatantonatabeladaesquerdaquantonatabeladadireita,esseJOINtambéméconhecidocomoINNERJOIN.VamosverificaroresultadoutilizandooINNERJOIN:

SELECTa.nome,COUNT(r.id)ASrespostasFROMalunoaINNERJOINrespostarONr.aluno_id=a.idGROUPBYa.nome;

+-----------------+-----------+|nome|respostas|+-----------------+-----------+|AlbertoSantos|9||FredericoJosé|4||JoãodaSilva|7||RenataAlonso|7|+-----------------+-----------+

Ele trouxeapenasosalunosquepossuemaomenosumaresposta,ouseja,queexistaaassociaçãoentreatabeladaesquerda(aluno)eatabeladadireita(resposta).

Nocapítuloanteriortivemosquefazerumaquerypararetornartodososalunoseaquantidadedematrículas,porémutilizamos subqueries para resolveronossoproblema.Podemos também, construiressaqueryapenascomJOINs.Vamostentar:

SELECTa.nome,COUNT(m.id)ASqtd_matriculaFROMalunoaJOINmatriculamONm.aluno_id=a.idGROUPBYa.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 possuemmatrícula, porém utilizamos apenas oJOIN,ouseja,oINNERJOIN.AoinvésdoINNERJOINqueretornaapenasseexistiraassociaçãoentre as tabelas da esquerda (aluno) e a da direita(matricula), nós queremos retornar todos os

12.2JOINOUSUBQUERY?

12.2JOINOUSUBQUERY? 111

alunos,mesmoquenãopossuammatrículas,ouseja,tabeladaesquerda.EntãovamostentaragoracomoLEFTJOIN:

SELECTa.nome,COUNT(m.id)ASqtd_matriculaFROMalunoaLEFTJOINmatriculamONm.aluno_id=a.idGROUPBYa.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|+------------------+---------------+

DamesmaformaqueconseguimospegartodososalunoseaquantidadederespostasmesmoqueoalunonãotenharespondidonenhumarespostautilizandooLEFTJOINpoderíamostambémresolverutilizandoumasubqueryparecidacomqualretornavatodososalunoseaquantidadedematrículas:

SELECTa.nome,(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASrespostasFROMalunoa;

+------------------+-----------+|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?

desempenhomelhorparaJOIN emrelaçãoa subqueries, entãoprefira ousodeJOINs ao invésdesubquery.

Vamos tentar juntar as queries que fizemos agora pouco, porém em uma única query. Veja oexemploemumaplanilha.

Figura12.1:Planilhaexemplo

Primeirovamosfazercomsubqueries.Entãocomeçaremosretornandoonomedoaluno:

SELECTa.nomeFROMalunoa;

Agoravamoscontartodasasrespostasetestaroresultado:

SELECTa.nome,(SELECTCOUNT(r.id)FROMrespostarWHEREr.aluno_id=a.id)ASqtd_respostasFROMalunoa;

+------------------+---------------+|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_matriculasFROMalunoa;

+------------------+---------------+----------------+|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,vamostentarcomoLEFTJOIN?Damesmaformaquefizemosanteriormente,começaremosretornandoosalunos:

SELECTa.nomeFROMalunoa;

Agoravamosjuntarastabelaalunocomastabelasrespostaematricula:

SELECTa.nome,r.idASqtd_respostas,m.idASqtd_matriculasFROMalunoaLEFTJOINrespostarONr.aluno_id=a.idLEFTJOINmatriculamONm.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 esseresultado.Note que o aluno João da Silva retornou 14 vezes, parece que tem alguma coisa estranha.VamospegaroiddoJoãodaSilvaevamosverificarosregistrosdelenatabelarespostaenatabelamatricula:

SELECTa.idFROMalunoaWHEREa.nome='JoãodaSilva';

+----+|id|+----+|1|+----+

Agoravamosverificartodososregistrosdelenatabelaresposta:

SELECTr.idFROMrespostarWHEREr.aluno_id=1;

12.2JOINOUSUBQUERY? 115

+----+|id|+----+|1||2||3||4||5||6||7|+----+

Foramretornados7registros,agoravamosverificarnatabelamatricula:

SELECTm.idFROMmatriculamWHEREm.aluno_id=1;

+----+|id|+----+|1||11|+----+

Foramretornados2registros.Seanalisarmosumpoucoessestrêsresultadoschegamosaosseguintesnúmeros:

aluno=1respostas=7matrículas=2

Vamos executar novamente a nossa query que retorna o aluno e a contagem de respostas ematrículas:

SELECTa.nome,r.idASqtd_respostas,m.idASqtd_matriculasFROMalunoaLEFTJOINrespostarONr.aluno_id=a.idLEFTJOINmatriculamONm.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ámultiplicandooaluno(1) x respostas(7) xmatrículas(2)... Com certeza essa contagem não funcionará! Precisamos deresultadosdistintos, ou seja, iremosutilizar oDISTINCT para evitar esse problema.Agora podemoscontarasrespostaseasmatrículas:

12.2JOINOUSUBQUERY? 117

SELECTa.nome,COUNT(DISTINCTr.id)ASqtd_respostas,COUNT(DISTINCTm.id)ASqtd_matriculasFROMalunoaLEFTJOINrespostarONr.aluno_id=a.idLEFTJOINmatriculamONm.aluno_id=a.idGROUPBYa.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_matriculasFROMalunoaLEFTJOINrespostarONr.aluno_id=a.idLEFTJOINmatriculamONm.aluno_id=a.idGROUPBYa.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áacontecendoaqueleproblemadamultiplicaçãodaslinhas!Percebaquesóconseguimosverificardeumaformarápidaoproblemaqueaconteceu,poisfizemosaquerypasso-a-passo,verificandocadaresultadoe,aomesmo

118 12.2JOINOUSUBQUERY?

tempo,corringoosproblemasquesurgiam.

NestecapítuloaprendemoscomoutilizarosdiferentestiposdeJOINs,comoporexemplooLEFTJOINqueretornaosregistrosdatabelaaesquerdaeoRIGHTJOINqueretornaosdadireitamesmoquenãotenhamassociações.VimostambémqueoJOINtambéméconhecidocomoINNERJOINqueretorna apenas os registros que estão associados. Além disso, vimos que algumas queries podem serresolvidasutilizando subqueries ouLEFT/RIGHTJOIN, porém é importante lembrar que os SGBDssempre terãomelhordesempenhocomoosJOINs, por isso é recomendadoqueutilize osJOINs.Vamosparaosexercícios?

1. Exibatodososalunosesuaspossíveisrespostas.Exibatodososalunos,mesmoqueelesnãotenhamrespondidonenhumapergunta.

2. ExibaagoratodososalunosesuaspossíveisrespostasparaoexercíciocomID=1.Exibatodososalunosmesmoqueelenãotenharespondidooexercício.

Lembre-sedeusaracondiçãonoJOIN.

1. QualadiferençaentreoJOINconvencional(muitasvezeschamadotambémdeINNERJOIN)paraoLEFTJOIN?

12.3RESUMINDO

EXERCÍCIOS

12.3RESUMINDO 119

CAPÍTULO13

Precisamosdeumrelatórioqueretornetodososalunos,algocomoselecionaronomedetodoseles,comumSELECTsimples:

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

MUITOSALUNOSEOLIMIT

120 13MUITOSALUNOSEOLIMIT

+------------------+

Vamosverificaragoraquantosalunosestãocadastrados:

SELECTcount(*)FROMaluno;

+----------+|count(*)|+----------+|16|+----------+

Comopodemosver, éumaquantidade relativamentebaixa,poisquandoestamos trabalhandoemumaaplicaçãoreal,geralmenteovolumedeinformaçõesémuitomaior.

Nofacebook,porexemplo,quantosamigosvocêtem?Quandovocêentranofacebook,aparecetodasas 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, 10notificações.Provavelmentevaiaparecendoaospoucos,certo?Entãoquetalmostrarmososalunosaospoucostambém?Ouseja,fazermosumapaginaçãononossorelatório,algocomo5alunos"porpágina".Mas como podemos fazer isso? No MySQL, podemos limitar em 5 a quantidade de registros quedesejamosretornar:

SELECTa.nomeFROMalunoaORDERBYa.nomeLIMIT5;

+------------------+|nome|+------------------+|AlbertoSantos||CarlosCunha||ClaudioSoares||CristaldoSantos||DaniloCunha|+------------------+

Nessecasoretornamososprimeiros5alunosemordemalfabética.Oatodelimitaréextremamenteimportanteamedidaqueosdadoscrescem.Sevocêtemmilmensagensantigas,nãovaiquererverasmildeumavezsó,tragasomenteas10primeirase,setiverinteresse,mais10,mais10etc.

Agora vamos pegar os próximos 5 alunos, isto é, senhorMySQL, ignore os 5 primeiros, e depoispegueparamimospróximos5:

SELECTa.nomeFROMalunoaORDERBYa.nomeLIMIT5,5;

+-----------------+|nome|

13.1 LIMITANDO E BUSCANDO A PARTIR DE UMA QUANTIDADEESPECÍFICA

13.1LIMITANDOEBUSCANDOAPARTIRDEUMAQUANTIDADEESPECÍFICA 121

+-----------------+|FredericoJosé||JoãodaSilva||JosedaSilva||ManoelSantos||OsmirFerreira|+-----------------+

LIMIT 5? LIMIT 5,5? Parece um pouco estranho, o que será que isso significa? Quandoutilizamos o LIMIT funciona da seguinte maneira: LIMIT

linha_inicial,qtd_de_linhas_para_avançar, ou seja,quando fizemosLIMIT5, informamos aoMySQLqueavançe5linhasapenas,poisporpadrãoeleiniciarápelaprimeiralinha,chamadadelinha0.SefizéssemosLIMIT0,5,porexemplo:

SELECTa.nomeFROMalunoaORDERBYa.nomeLIMIT0,5;

+------------------+|nome|+------------------+|AlbertoSantos||CarlosCunha||ClaudioSoares||CristaldoSantos||DaniloCunha|+------------------+

PercebaqueoresultadoéomesmoqueLIMIT5!SepedimosLIMIT5,10oqueelenostrás?

SELECTa.nomeFROMalunoaORDERBYa.nomeLIMIT5,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 osexemplostodosdeumaú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.nomeFROMalunoaORDERBYa.nomeLIMIT5;

+------------------+|nome|+------------------+|AlbertoSantos||CarlosCunha||ClaudioSoares||CristaldoSantos||DaniloCunha|+------------------+

Ignorandoos5primeiros,pegandoospróximos5alunos:

SELECTa.nomeFROMalunoaORDERBYa.nomeLIMIT5,5;

+-----------------+|nome|+-----------------+|FredericoJosé||JoãodaSilva||JosedaSilva||ManoelSantos||OsmirFerreira|+-----------------+

Nesse capítulo vimos como nem sempre retornar todos os registros das tabelas são necessários,algumasvezes,precisamosfiltraraquantidadedelinhas,poisemumaaplicaçãoreal,podemoslidarcomuma quantidade bem grande de dados. Justamente por esse caso, podemos limitar as nossas queriesutilizandoainstruçãoLIMIT.Vamosparaosexercícios?

13.2RESUMINDO

13.2RESUMINDO 123

1. Escrevaumaqueryquetragaapenasosdoisprimeirosalunosdatabela.

2. EscrevaumaSQLquedevolvaos3primeirosalunosqueoe-mailterminecomodomínio".com".

3. Devolvaos2primeirosalunosqueoe-mailterminecom".com",ordenandopornome.

4. DevolvatodososalunosquetenhamSilvaemalgumlugarnoseunome.

EXERCÍCIOS

124 13.2RESUMINDO