Fechar

Entendendo Outer Joins SQL – Parte 1 (Produto Cartesiano)

Por | 22 de junho de 2009 | Desenvolvimento 33 comentários

Participo de algumas listas de discussão sobre PHP e percebi que muitos desenvolvedores têm dúvidas a respeito de comandos SQL, não sabem ou não utilizam de forma correta os outer joins.

Por isso, decidi fazer um post sobre esse assunto explicando como utilizar o left outer join, right outer join, natural join, cross join ou inner join (mais utilizado).

Para que servem os outer joins?

Servem para fazer as junções entre duas ou mais tabelas, cruzando informações, combinando registros, testando desigualdades.

Por que cruzar dados?

Quando pensamos em armazenamento de dados, não podemos sair criando tabelas sem uma avaliação e sem a criação de um modelo ER (Entidade Relacionamento), e uma das vantagens de pensar no modelo é evitar a repetição de dados. E como fazer isso? Distribuindo-os em mais de uma tabela de forma que possamos categorizar as repetições.

Se você olhar o modelo abaixo, existe uma tabela de programadores e uma de empresas, note que as duas estão relacionadas. Guardo na tabela programadores somente um código que identifica a empresa em que o programador trabalha. Fiz isso em virtude da possibilidade de ter vários programadores relacionados a mesma empresa, assim evito ter que escrever várias vezes o nome da empresa (texto ocupa mais espaço que número) e evito que a mesma empresa seja escrita de maneiras diferentes, dificultando assim, por exemplo, uma busca. Porém, agora para recuperar esses dados será necessário utilizar junções.

modelo

empresas
programadores_linguagens
programador
linguagens

Cruzando Dados

1 – Produto Cartesiano

Esse não é bem um tipo de junção, é conhecido como conexão cruzada, ou seja, resulta no cruzamento de cada linha de uma tabela com todas as linhas de outra tabela.

SELECT p.nome, l.nome FROM programadores p CROSS JOIN linguagens l;

cross

O resultado desta consulta gera 36 linhas de combinações. Obs.: Esse tipo de junção não é muito utilizado.

Para obter o mesmo resultado podemos utilizar outras duas sintaxes: retirar o CROSS JOIN e inserir uma vírgula (,) ou usar o INNER JOIN sem a condição de comparação (ON). O resultado será o mesmo.

SELECT p.nome, l.nome FROM programadores p, linguagens l;

SELECT p.nome, l.nome FROM programadores p INNER JOIN linguagens l;

Continue lendo: Parte 2 e Parte 3

Marcadores: , , , , , , ,

Outros conteúdos interessantes

Confira os 33 comentários deixados Comentar

  1. Jackson via Rec6
    9:12 em 22 de junho de 2009
    1

    Entendendo Outer Joins SQL – Parte 1 (Produto Cartesiano) | Profissionais TI…

    Participo de algumas listas de discussão sobre PHP e percebi que muitos desenvolvedores têm dúvidas a respeito de comandos SQL, não sabem ou não utilizam de forma correta os outer joins……

  2. Jackson Caset
    9:17 em 22 de junho de 2009
    2

    Paulinha,

    Seja bem-vinda e parabéns pelo conteúdo, muito interessante.

    Abraço

  3. Paulinha Winter
    12:05 em 22 de junho de 2009
    3

    Jackson,

    Obrigada. Espero poder contribuir com mais conteúdos.

    Abraços

  4. Klaus Peter Laube
    12:08 em 22 de junho de 2009
    4

    Belo post! Sempre é bom saber mais sobre SQL…

    Parabéns.

  5. Paulinha Winter
    12:16 em 22 de junho de 2009
    5

    Oi Klaus!!!

    Obrigada. Em breve falarei mais sobre o assunto.

    Abraços

  6. Guilherme Pinter
    18:19 em 22 de junho de 2009
    6

    Parabéns pelo post. Trabalho com desenvolvimento PHP utilizando o framework Cake. Muitas vezes acabamos não prestando a devida atenção nesses detalhes de banco que interferem diretamente no desempenho da aplicação.

    Parabéns!

  7. Paulinha Winter
    19:05 em 22 de junho de 2009
    7

    Oi Pinter…

    Obrigada!!!
    É verdade, é que uma vez que você passa usar Frameworks acaba na maioria das vezes não precisando gerar consultas na mão.

    Abraços

  8. Entendendo Outer Joins SQL – Parte 2 (Conexões Internas – INNER JOIN) | Profissionais TI
    7:07 em 23 de junho de 2009
    8

    [...] conexão interna inicialmente faz a mesma coisa que a conexão cruzada, porém aplica restrições que podem ser de igualdade ou desigualdade, isso faz com que algumas [...]

  9. Entendendo Outer Joins SQL – Parte 3 (Conexões Externas LEFT, RIGHT JOIN) | Profissionais TI
    7:02 em 24 de junho de 2009
    9

    [...] Obs.: O modelo de dados utilizado neste post encontra-se em Entendendo Outer Joins SQL – Parte 1 (Produto Cartesiano) [...]

  10. 8 meses de PTI – Resumão do mês de junho! | Profissionais TI
    10:13 em 6 de julho de 2009
    10

    [...] Entendendo Outer Joins SQL – Parte 1 (Produto Cartesiano) [...]

  11. Celso Fernandes R Junior
    17:24 em 29 de abril de 2010
    11

    Gostaria de saber como o produto cartesiano pode ajudar a tabela verdade.

  12. Paulinha Winter
    19:00 em 29 de abril de 2010
    12

    Boa tarde Celso,

    Eu não sei se entendi muito bem a sua pergunta, pode me explicar melhor sua dúvida?

    É que o produto cartesiano simplesmente é uma forma de relacionar todos os dados entre duas tabelas para obter a relação entre elas.

    Também recomendo que você de uma lida na continuação desse post em: http://www.profissionaisti.com.br/author/paulinha/

    Até mais.
    Paulinha

  13. Harryson
    10:57 em 7 de novembro de 2010
    13

    O que você usa pra fazer o modelo das tabelas?

  14. Daniel Tibúrcio
    10:20 em 29 de novembro de 2011
    14

    Preciso de uma tabela juntando duas outras, sendo que uma é um cadastro de códigos e outra das ocorrências destes códigos em uma determinada condição. Só que preciso de uma tabela contendo todos os códigos, mesmo os códigos que não existam na segunda tabela, sendo que estes virão com valores zerados. O que me sugerem?

  15. Fabricio C. Frontarolli
    18:51 em 29 de novembro de 2011
    15

    SELECT TABELA1.*
    FROM TABELA1
    RIGHT JOIN TABELA_CODIGOS
    ON TABELA1.ID_CODIGO = TABELA_CODIGOS.ID
    – WHERE CONDIÇÃO ESPECIFICADA.

    Daniel Tibúrcio, Passa as estrutras das tabelas e oq vc precisa retornar.

  16. Daniel Tibúrcio
    15:22 em 1 de dezembro de 2011
    16

    Obrigado, Fabrício, acho que estamos no caminho certo.

    Adaptei seu código para:

    SELECT ACUMUL.*
    FROM ACUMUL
    RIGHT JOIN RENDESC
    ON ACUMUL.COD_CODIGO = RENDESC.COD_CODIGO
    WHERE ACUMUL.EMP_CODIGO = ’006′ AND
    ACUMUL.CTE_MESPRO = ’201105′

    Só que não deu certo, pois existem códigos na tabela RENDESC que não foram mostrados no resultado.

    Estrutura de ACUMUL:
    EMP_CODIGO (empresa), CTE_MESPRO (ano/mês), FUN_MATRIC(matrrícula), COD_CODIGO (código),CAL_VALOR
    RENDESC: COD_CODIGO, COD_DESCRI

    O que eu preciso é que para cada matrícula fosse mostrado o valor de cada código, inclusive dos códigos que não tem no ACUMUL. Preciso disto porque preciso comparar valores de meses diferentes. A idéia seria criar uma tabela temporária para cada mês e uma outra query para gerar uma com os dois valores e apresentar no relatório.

    Se precisar de alguma ajuda, coloco-me à disposição.

  17. Fabricio C. Frontarolli
    22:51 em 1 de dezembro de 2011
    17

    Se entendi bem, você precisa trazer todos os registros das duas tabelas, se entendi bem faça o seguinte, troque RIGHT JOIN para FULL JOIN, isso irá trazer os dados das duas tabelas.

    Se sua ideia é usar esse resultado para efetuar mais manipulações nesses dados, use SubQuery ou Common Table Expression (se for SQL Server ñ sei se funciona com outros bancos) http://msdn.microsoft.com/en-us/library/ms190766.aspx

  18. Daniel Tibúrcio
    9:23 em 2 de dezembro de 2011
    18

    Não deu certo, Fabrício, o resultado foi o mesmo.

    Vamos a um exemplo: digamos que em RENDESC eu tenha códigos de 001 a 100, mas que nos registros de ACUMUL só tenho 20 códigos gravados. Preciso de uma tabela que mostre o valor de todos os 100 códigos de RENDESC, sendo zero para os 80 inexistentes em ACUMUL.

    Obrigado pela atenção

  19. Fabricio C. Frontarolli
    19:00 em 2 de dezembro de 2011
    19

    tenta isso aqui.. vamos ver se da certo:
    http://pastebin.com/pa6Ty4F9

  20. Daniel Tibúrcio
    15:40 em 5 de dezembro de 2011
    20

    Não aceitou ISNULL, uso Firebird 2.0

    SELECT ISNULL(RENDESC.ID_CODIGO, ‘Nenhuma Ocorrencia’)
    COD_DESCRI, sum_
    FROM RENDESC
    LEFT JOIN ACUMUL
    ON RENDESC.COD_CODIGO = ACUMUL.COD_CODIGO
    WHERE ACUMUL.CTE_MESPRO = ’201105′ AND ACUMUL.EMP_CODIGO = ’005′

    Na verdade eu não preciso da descrição, isto eu pego de outro jeito, preciso do código (COD_CODIGO) e a soma total do valor (CAL_VALOR em ACUMUL) de cada código existente em RENDESC (os códigos não se repetem)

  21. Fabricio C. Frontarolli
    22:25 em 6 de dezembro de 2011
    21
  22. Daniel Tibúrcio
    10:42 em 7 de dezembro de 2011
    22

    Deu certo se eu não tiver a cláusula where, os 125 registros da tabela RENDESC foram mostrados com valor zero para códigos que não tinham correspondente no ACUMUL.

    No entanto, se eu coloco a cláusula WHERE, como mostrado abaixo, só mostra 24 registros, apenas os que haveriam se verificar os códigos distintos em ACUMUL naquela condição.

    SELECT RENDESC.COD_CODIGO, SUM(ACUMUL.CAL_VALOR) AS CAL_VALOR
    FROM RENDESC
    LEFT JOIN ACUMUL
    ON RENDESC.COD_CODIGO = ACUMUL.COD_CODIGO
    WHERE ACUMUL.EMP_CODIGO = ’027′ AND ACUMUL.CTE_MESPRO = ’201107′
    GROUP BY RENDESC.COD_CODIGO

  23. Fabricio C. Frontarolli
    12:57 em 7 de dezembro de 2011
    23

    tira o LEFT e coloca INNER, talvez da certo..

  24. Fabricio C. Frontarolli
    12:59 em 7 de dezembro de 2011
    24

    ou coloca o WHERE antes do GROUP BY

  25. Daniel Tibúrcio
    14:37 em 7 de dezembro de 2011
    25

    INNER = mesma coisa

    RIGHT = mesma coisa

    WHERE antes dá erro de SQL

    Complicado mesmo

  26. Daniel Tibúrcio
    14:24 em 9 de dezembro de 2011
    26

    vamos desistir?

  27. Daniel Tibúrcio
    15:36 em 21 de dezembro de 2011
    27

    Desistimos mesmo? Não deu para entender ter funcionado sem a cláusula WHERE (mostrou todos os códigos como eu queria) e não funcionar com ela (mostrou só os códigos existentes no arquivo, não todos)

  28. Daniel Tibúrcio
    16:22 em 21 de dezembro de 2011
    28

    Deu certo, bastou trocar o WHERE por AND, assim:

    SELECT RENDESC.COD_CODIGO, SUM(ACUMUL.CAL_VALOR) AS CAL_VALOR
    FROM RENDESC
    LEFT JOIN ACUMUL
    ON RENDESC.COD_CODIGO = ACUMUL.COD_CODIGO
    AND ACUMUL.EMP_CODIGO = ’027? AND ACUMUL.CTE_MESPRO = ’201107?
    GROUP BY RENDESC.COD_CODIGO

  29. Daniel Tibúrcio
    16:35 em 21 de dezembro de 2011
    29

    Deu certo com os totais gerais, mas eu precisava fazer o mesmo por matrícula, algo assim:

    SELECT RENDESC.COD_CODIGO, ACUMUL.FUN_MATRIC, SUM(ACUMUL.CAL_VALOR) AS CAL_VALOR
    FROM RENDESC
    LEFT JOIN ACUMUL
    ON RENDESC.COD_CODIGO = ACUMUL.COD_CODIGO
    AND ACUMUL.EMP_CODIGO = ’027′ AND ACUMUL.CTE_MESPRO = ’201107′
    GROUP BY RENDESC.COD_CODIGO, ACUMUL.FUN_MATRIC

    mas assim volta ao problema anterior

  30. Fabricio C. Frontarolli
    13:29 em 22 de dezembro de 2011
    30

    Daniel, me manda os scripts de criação de suas tabelas e oq vc precisa retornar..
    Eu monto aqui e te passou, fica meio abstrato tentar ajudar só imaginando as tabelas.
    Me passa elas e os resultados que você precisa retornar delas, ai te mando.
    Se preferir mandar no email é : fabricio.frontarolli@gmail.com.

    Valeu!

  31. Daniel Tibúrcio
    10:48 em 26 de dezembro de 2011
    31

    CREATE TABLE RENDESC
    (
    COD_CODIGO CHAR(4) ,
    COD_DESCRI VARCHAR(30)
    );

    CREATE TABLE EMPREGAD
    (
    CLI_CODIGO SMALLINT,
    EMP_CODIGO CHAR(3) ,
    FUN_MATRIC VARCHAR(6) ,
    FUN_NOME VARCHAR(60)
    );

    CREATE TABLE ACUMUL
    (
    EMP_CODIGO CHAR(3) ,
    FUN_MATRIC VARCHAR(6) ,
    CTE_MESPRO VARCHAR(6) ,
    COD_CODIGO CHAR(4) ,
    CAL_VALOR FLOAT
    );

    Preciso, para uma empresa definida (campo EMP_CODIGO de EMPREGAD e de ACUMUL), de todos os códigos existentes (COD_CODIGO em RENDESC e ACUMUL, sendo que neste não existem todos existentes em RENDESC) para todos os funcionários (FUN_MATRIC em EMPREGAD e ACUMUL) em um mês definido (CTE_MESPRO em ACUMUL).

    Preciso da matrícula (FUN_MATRIC), código (COD_CODIGO) e a soma dos valores (CAL_VALOR em ACUMUL, sendo que os códigos que não existem aqui deve vir zerados).

    Como vou fazer comparação entre dois meses, a intenção é executar o mesmo SQL para cada mês informado pelo usuário e compará-los, mas esta parte é tranquila.

    Se não apresentarem todos os códigos a comparação ficará errada, pois um código pode acontecer num mês e não acontecer em outro e o contrário pode ocorrer com ourtro código.

    Um grande abraço e excelente 2012!!!

  32. Uma pequena introdução sobre o inner join em SQL « Tecnologia & Segurança
    23:33 em 26 de dezembro de 2011
    32

    [...] as aulas da faculdade, percebi as dificuldades do pessoal em assimilar o conceito sobre do Inner Join em banco de [...]

  33. Daniel Tibúrcio
    16:52 em 3 de janeiro de 2012
    33

    Conseguiu algo, Fabrício?


Deixe seu comentário!