Vamos dar uns toques de SQL? IX
Por Pedro Gaspar para o PPLWARE
INNER, LEFT e RIGHT JOIN
Depois de várias semanas consecutivas terem pedido para falar aqui de JOINs esta semana vamos abordar este tema. JOIN ou junções de tabelas, consiste em juntar uma ou mais tabelas para obter resultados mais complexos. Neste artigo iremos focar-nos no INNER, LEFT e RIGHT JOIN.
Antes de mais gostaria de esclarecer uma duvida que tem havido nos artigos anteriores. Nestes artigos eu tenho-me baseado em MySQL, logo é normal que um ou outro parâmetro sejam específicos de MySQL. No entanto vou tentar fazer os artigos o mais imparciais possíveis conforme os meus conhecimentos e claro, conto sempre com a ajuda de todos os leitores para discutirmos o melhor uso de determinadas funções. Voltando ao exemplo da semana passada, vamos supor que a nossa base de dados pertence a uma empresa, onde temos para já duas tabelas: a tbl_pplware onde temos os nossos funcionários com os seguintes dados:
E a tabela horas onde vamos registar as horas de trabalho que os nossos funcionários realizam. Vamos considerar que de momento existem os seguintes registos:
Para juntarmos as duas tabelas precisamos de ter um ponto de união entre ambas, e neste caso o ponto de união está entre o id da tabela tbl_pplware e o id_p da tabela horas.
Sintaxe de Utilização do JOIN
SELECT nome_tabela.nome_campo [nome_tabela2.nome_campo2] FROM nome_tabela [INNER/LEFT/RIGHT] JOIN nome_tabela2 ON nome_tabela.campo_uniao=nome_tabela2.campo_uniao |
EXEMPLO INNER JOIN
Objectivo: Apresentar o nome das pessoas e o turno que fizeram Query:
SELECT tbl_pplware.nome AS nome, horas.turno FROM `tbl_pplware` INNER JOIN horas ON tbl_pplware.id_pessoa = horas.id_p |
Resultado:
O INNER JOIN junta o resultado das duas tabelas, mas apenas mostra os resultados se existirem registos em comum em ambas as tabelas. Como podem reparar, o registo Rui Lopes e Sílvia Lopes não aparecem nos resultados, pois não têm horas registadas.
EXEMPLO LEFT JOIN Objectivo: Apresentar o nome de todas as pessoas e os turnos que fizeram Query:
SELECT tbl_pplware.nome AS nome, horas.turno FROM `tbl_pplware` LEFT JOIN horas ON tbl_pplware.id_pessoa = horas.id_p |
Como podem repara,r com o LEFT JOIN já apresenta também os resultados que não apareceram no exemplo com o INNER JOIN, isto porque com o LEFT JOIN, é dada prioridade a primeira tabela que chamamos (neste caso tbl_pplware) ou seja, mesmo que não existam registos em comum na segunda tabela é-nos sempre apresentado os resultados da primeira tabela. Com o RIGHT JOIN acontece exactamente o contrario, é dada prioridade a tabela que faz o JOIN.
EXEMPLO3
Objectivo: Apresentar o nome e as horas feitas de todos os funcionários Query:
SELECT tbl_pplware.nome AS nome, SUM( HOUR( horas.data_saida ) - HOUR( horas.data_entrada ) ) AS horas FROM `tbl_pplware` LEFT JOIN horas ON tbl_pplware.id_pessoa = horas.id_p GROUP BY tbl_pplware.id_pessoa |
Resultado:
Espero ter conseguido ser o mais esclarecedor possível e para a semana continuamos a trabalhar nos JOIN’s. Bom fim de semana !!!
Artigos relacionados
- Vamos dar uns toques em queries SQL?
- Vamos dar uns toques em queries SQL? II
- Vamos dar uns toques em queries SQL? III
- Vamos dar uns toques em queries SQL? IV
- Vamos dar uns toques de SQL? V
- Vamos dar uns toques de SQL? VI
- Vamos dar uns toques de SQL? VII
- Vamos dar uns toques de SQL? VIII
- Cábula” do MySQL
Este artigo tem mais de um ano
Dúvida minha…
Em mysql não existe cross join tal como em T-SQL????
A função ‘HOUR()’ não faz parte do standard SQL. O mais parecido é a função EXTRACT(), que o MySQL não implementa.
Segundo o standard SQL, seria assim:
SELECT tbl_pplware.nome AS nome,
SUM(
EXTRACT( HOUR FROM horas.data_saida ) –
EXTRACT( HOUR FROM horas.data_entrada ) ) AS horas
FROM `tbl_pplware`
LEFT JOIN horas ON tbl_pplware.id_pessoa = horas.id_p
GROUP BY tbl_pplware.id_pessoa
Existe, pelo menos, pelo PostgreSQL e Oracle. Não existe nem em SyBase, nem MSSQL, nem no MySQL.
Em T-SQL ou MSSQL a função mais utilizada para fazer este tipo de operações é DATEPART(, )…
http://msdn.microsoft.com/en-us/library/ms174420.aspx
mas creio que em MySQL existe a função HOUR() tal como por exemplo indica este site…
http://dev.mysql.com/doc/refman/4.1/pt/date-and-time-functions.html
Logo como este tutorial é sobre MySQL a Query apresentada penso ser valida…
Creio que estás enganado…
O título do tutorial não refere nenhum dialecto do SQL em particular, daí o meu comentário.
No segundo parágrafo do artigo diz isto:
“Antes de mais gostaria de esclarecer uma duvida que tem havido nos artigos anteriores. Nestes artigos eu tenho-me baseado em MySQL, logo é normal que um ou outro parâmetro sejam específicos de MySQL.”
Parece que não foste o único com essa duvida…
Pra mim o problema está na imagem do Artigo
Temos de mudar a imagem 🙂
Não é só a imagem, será o título também 😉
O SQL é uma linguagem de programação (apesar de muito mal tratada pelos implementadores), e tem dois standards aprovados: SQL 92 e SQL99.
Se esta série se pretende referir ao “SQL” (como o título e a imagem sugerem) acho que deveriam tentar seguir os standards, sem prejuízo de referirem uma ou outra das implementações “desviantes” (e não conheço nenhum DBMS que siga o standard a 100%).
O meu comentário apenas pretendia colmatar essa lacuna, referindo o padrão SQL.
Aproveito para sugerir que tentem exemplificar as várias sugestões usando o site referido pelo Duarte Brito (http://sqlzoo.net) para testarem (e testarmos) os exemplos.
Ainda mais acrescento….
Usando este site para fazer o teste:
http://sqlzoo.net/howto/source/z.dir/tip816560/mysql
e correndo o seguinte comando:
CREATE TABLE t_dttest(
a DATE,
b TIME,
c DATETIME);
INSERT INTO t_dttest VALUES (
DATE ‘1962-05-20′,
TIME ’10:32:16’,
TIMESTAMP ‘1962-05-20 10:32:16’);
SELECT * FROM t_dttest;
SELECT HOUR(B), HOUR(C) FROM t_dttest;
Podemos validar a função HOUR() a funcionar.
Já agora, aproveito para acrescentar mais um DBMS que também implementa o standard: Ingres.
Desconhecia o site, achei-o muito didáctico. Pena algumas Bases de Dados estarem em baixo.
Excelente dica Duarte. Obrigado, muito bom!
Muito bom 🙂 parabens
Eu tenho um problema….
tenho uma tabela Produto composta pelos seguintes campos
Nome,preco….
a minha questao é como declaro o campo preco se float???
e como introduzo depois exemplo
insert valuer Produto (‘Arroz’,2,25) assim da erro como resolver isto???
Obrigado
Para criar usas DECIMAL(18,9)
E para inserir não tenho a certeza mas tem de ser 2.5
É importante que seja ponto o separador e não virgula…
Pegando no exemplo anterior e usando o mesmo site para testar tens aqui:
CREATE TABLE t_dttest(
a DECIMAL(18,9),
b TIME,
c DATETIME);
INSERT INTO t_dttest VALUES (
2.5,
TIME ’10:32:16′,
TIMESTAMP ‘1962-05-20 10:32:16’);
SELECT * FROM t_dttest;
http://sqlzoo.net/howto/source/z.dir/tip816560/mysql
Eu costumo, quando possível, unir as tabelas no WHERE.
Tipo:
SELECT nome, data_entrada, data_saida
FROM tbl_pplware, tbl_horas
WHERE tbl_pplware.id_pessoa = tbl_horas.id_p
Penso que seja mais rápido e fica um código mais limpo
Duvido que seja mais rápido, mas dependerá do motor de dados. Não me admiraria que, nalguns SDBMSs até seja mais lento, dado faltar a ‘dica’ para para juntar as várias tabelas.
Mais “limpo” não será de certeza… imagina só que o join envolve meia dúzia de tabelas ou mais. Além de que se torna mais difícil distinguir a parte da where clause que diz respeito ao join da que diz respeito aos limites da query.
Concordo contigo.
O que eu queria dizer era: Mesmo assim, usando INNER, LEFT e RIGHT JOIN penso que seja mais rápido e fica com um código mais limpo.
Só agora dei conta do que tinha enviado.
Relativamente à rapidez não me posso pronunciar visto que não sei os detalhes de implementação dos engines em si mas, código mais limpo? Liga 5 tabelas com nomes sensivelmente grandes e depois mete mais 4 condições de filtragem de dados, já começa a não ficar assim tão limpo 🙂
Utilizando um INNER JOIN por exemplo podes separar a parte do código que trata da criação da ponte entre as várias tabelas, acabas por ter o código quase por blocos:
/*Apresentação final dos dados*/
SELECT Foo.Name, Bar.Value ….
/*Ligar tabelas*/
FROM Foo
INNER JOIN Bar
ON Bar.ID = Foo.ID
…
/*Condições*/
WHERE Foo.Name LIKE ‘%Bla%’ OR Bar.Value = 10 …;
Pelo menos comigo é o que resulta melhor, ambas as formas dariam um resultado correcto!
É impressão minha ou o exemplo 3 não funcionaria correctamente, caso um dos “funcionários” tivesse feito o turno das 22h – 02h? É que a subtracção como está feita é directa da hora e assim dará -20! e não +4 como seria esperado. Alguém me corrija se estiver errado…