Vamos dar uns toques de SQL? IX

19 Comentários

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.

sql_00

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:

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

tbl_01

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:

tbl_01 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

tbl_03

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:

tbl_04 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

Partilhar:
Tags:

Comentários

19

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *

  1. Avatar de Duarte Brito
    Duarte Brito

    Dúvida minha…
    Em mysql não existe cross join tal como em T-SQL????

  2. Avatar de Carlos Correia

    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.

    1. Avatar de Duarte Brito
      Duarte Brito

      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…

      1. Avatar de Carlos Correia

        O título do tutorial não refere nenhum dialecto do SQL em particular, daí o meu comentário.

        1. Avatar de Duarte Brito
          Duarte Brito

          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…

          1. Avatar de Duarte Brito
            Duarte Brito

            Pra mim o problema está na imagem do Artigo

        2. Avatar de Pedro Pinto

          Temos de mudar a imagem 🙂

          1. Avatar de Carlos Correia

            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.

    2. Avatar de Duarte Brito
      Duarte Brito

      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.

      1. Avatar de Carlos Correia

        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.

      2. Avatar de Pedro Pinto

        Excelente dica Duarte. Obrigado, muito bom!

  3. Avatar de Mestre
    Mestre

    Muito bom 🙂 parabens

  4. Avatar de Tiago Silva
    Tiago Silva

    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

    1. Avatar de Duarte Brito
      Duarte Brito

      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

  5. Avatar de Trambulhao
    Trambulhao

    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

    1. Avatar de Carlos Correia

      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.

      1. Avatar de Trambulhao
        Trambulhao

        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.

    2. Avatar de NoSila

      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!

  6. Avatar de Daniel
    Daniel

    É 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…