none
Left Join não funciona RRS feed

  • Pergunta

  • Prezados, 

    tenho uma consulta no Sql Server 2012 que realiza um join entre duas tabelas. A primeira é a tabela de item com 21 registros, a segunda é a de lançamentos. Como quero retornar todos os itens que tiveram ou não lançamentos em um determinado mês montei a consulta abaixo, porém a consulta retorna somente 12 linhas quando o correto seria 21, pois esta é a quantidade de itens na tabela de itens. O que pode estar acontecendo?

    select
      I.ID_ITEM, sum(IsNull(L.VALOR,0)) as Valor
    from 
      ITEM I LEFT JOIN 
      LANCAMENTO L ON I.ID_ITEM = L.ID_ITEM
    where
      Year(L.DT_PAGAMENTO) = 2015 and
      Month(L.DT_PAGAMENTO) = 1
    group by I.ID_ITEM  

    quinta-feira, 28 de maio de 2015 11:56

Respostas

  • Olá!

    Provavelmente os dados estão sendo restringidos por causa do conteúdo na cláusula where. Por mais que você utilize um left join, quando esta tabela é apontada na cláusula where, o efeito fica semelhante ao de um inner join. Tente o seguinte:

    select
      I.ID_ITEM, sum(IsNull(L.VALOR,0)) as Valor
    from 
      ITEM I LEFT JOIN 
      LANCAMENTO L ON I.ID_ITEM = L.ID_ITEM
       and Year(L.DT_PAGAMENTO) = 2015
       and Month(L.DT_PAGAMENTO) = 1
    group by I.ID_ITEM  

    Bom trabalho!

    • Marcado como Resposta Mauro Magalhães segunda-feira, 1 de junho de 2015 11:58
    quinta-feira, 28 de maio de 2015 12:07

Todas as Respostas

  • Mauro,

    faça a pesquisa somente na tabela lançamento considerando os parametros de year e month e verifique se não são eles que estão filtrando os dados.

    select distinct lancamento.Id_item
    from lancamento
    where year(lancamento.Dt_pagamento) = 2015
    and   month(lancamento.dt_pagamento) = 1
    


    Natan

    quinta-feira, 28 de maio de 2015 12:07
  • Olá!

    Provavelmente os dados estão sendo restringidos por causa do conteúdo na cláusula where. Por mais que você utilize um left join, quando esta tabela é apontada na cláusula where, o efeito fica semelhante ao de um inner join. Tente o seguinte:

    select
      I.ID_ITEM, sum(IsNull(L.VALOR,0)) as Valor
    from 
      ITEM I LEFT JOIN 
      LANCAMENTO L ON I.ID_ITEM = L.ID_ITEM
       and Year(L.DT_PAGAMENTO) = 2015
       and Month(L.DT_PAGAMENTO) = 1
    group by I.ID_ITEM  

    Bom trabalho!

    • Marcado como Resposta Mauro Magalhães segunda-feira, 1 de junho de 2015 11:58
    quinta-feira, 28 de maio de 2015 12:07
  • Obrigado!

    Eu também já tentei deste forma e não funcionou. Com certeza é a cláusula where, porém acredito que isso não deveria ocorrer. Acredito que o otimizador deveria filtrar os lançamentos e depois fazer o left join. Da forma como está o left join não está servindo de nada. 

    quinta-feira, 28 de maio de 2015 12:22
  • Bom dia,

    Acredito que você conseguiria obter o resultado desejado com a sugestão do Rodrigo.

    De qualquer forma, seguem mais 2 sugestões para teste:

    with CTE_Sum as
    (
        select 
            ID_ITEM,
            sum(VALOR) as Valor
        from LANCAMENTO
        where
            Year(DT_PAGAMENTO) = 2015 and
            Month(DT_PAGAMENTO) = 1
    )
    
    select
      I.ID_ITEM, IsNull(L.VALOR, 0) as Valor
    from 
      ITEM I LEFT JOIN 
      CTE_Sum L ON I.ID_ITEM = L.ID_ITEM
      

    select
      I.ID_ITEM, sum(IsNull(L.VALOR,0)) as Valor
    from 
      ITEM I LEFT JOIN 
      LANCAMENTO L ON I.ID_ITEM = L.ID_ITEM
    where
      (Year(L.DT_PAGAMENTO) = 2015 and
       Month(L.DT_PAGAMENTO) = 1) or
      (L.DT_PAGAMENTO IS NULL)
    group by I.ID_ITEM  

    Espero que ajude.


    Assinatura: http://www.imoveisemexposicao.com.br

    quinta-feira, 28 de maio de 2015 12:56
  • Deleted
    quinta-feira, 28 de maio de 2015 12:56
  • Deleted
    quinta-feira, 28 de maio de 2015 13:05
  • Olá novamente, Mauro!

    Curioso não ter funcionado da forma que sugeri. Montei um pequeno cenário, e funcionou perfeitamente, exceto quando forcei uma entrada de data em um formato de cultura diferente da definida no banco de dados (como inserir data "17/01/2015" quando deveria ser "2015/01/17", por exemplo, o que faria os registros com data errada serem ignorados). Será que não é uma situação semelhante a esta que está te passando a perna?

    Bom trabalho!

    quinta-feira, 28 de maio de 2015 13:11
  • Eu consegui fazer de uma forma bem parecida com a sua, através de subselect e funcionou. As datas estão inseridas normalmente na base de dados. Eu esperava que a clasula where não interferisse na junção. Imaginava que o Sql Server resolvesse primeiro o filtro para depois resolver a junção. Em outros SGDB´s funciona normalmente. Mas de qualquer forma o importante aqui é obter o resultado desejado. Agradeço a todos pela força!!!!
    quinta-feira, 28 de maio de 2015 15:23
  • Eu consegui fazer de uma forma bem parecida com a sua, através de subselect e funcionou. As datas estão inseridas normalmente na base de dados. Eu esperava que a clasula where não interferisse na junção. Imaginava que o Sql Server resolvesse primeiro o filtro para depois resolver a junção. Em outros SGDB´s funciona normalmente. Mas de qualquer forma o importante aqui é obter o resultado desejado. Agradeço a todos pela força!!!!

    Beleza, Mauro!

    Que bom que conseguiu. Sugiro que você post a sua solução e a marque como resposta, pois poderá ajudar outras pessoas com problema semelhante.

    quinta-feira, 28 de maio de 2015 15:52
  • Deleted
    quinta-feira, 28 de maio de 2015 17:46
  • Ficou como uma variação a query do José. Mais uma forma de fazer. E quero me desculpar com o Rodrigo pois a query dele funciona sim e é mais simples. Mais uma vez muito obrigado a todos!!!

    Eis o código:  

    SELECT 
         I.NM_ITEM, Isnull(Z.Valor,0)
    FROM 
        ITEM I LEFT JOIN 
        (
        SELECT
    L.ID_ITEM, SUM(IsNull(L.VALOR,0)) as Valor

        FROM 
    LANCAMENTO L 
       WHERE
    Year(L.DT_PAGAMENTO) = @ano and
    Month(L.DT_PAGAMENTO) = @mes
    group by L.ID_ITEM ) Z ON I.ID_ITEM = Z.ID_ITEM

    ORDER BY i.NM_ITEM

    sexta-feira, 29 de maio de 2015 11:09
  • Obrigado!

    Eu também já tentei deste forma e não funcionou. Com certeza é a cláusula where, porém acredito que isso não deveria ocorrer. Acredito que o otimizador deveria filtrar os lançamentos e depois fazer o left join. Da forma como está o left join não está servindo de nada. 

    Mauro,

    Diferente da instrução INNER JOIN, as instruções "OUTER JOIN" (LEFT, RIGHT, FULL e CROSS) possuem um critério onde o otimizador da consulta não tenta reorganizar a ordem de vínculo das tabelas para execução do OUTER JOIN (como ocorre no INNER JOIN).

    A tabela da "esquerda" em LEFT OUTER JOIN é acessada primeiro, seguida pela tabela "direita". Essa ordem de vínculo fixo pode levar à planos de execução de "qualidade inferior".

    O Mecanismo dos Bancos de Dados no SQL Server também possuem uma ordem de processamento lógico que prioriza a execução do "JOIN" (no seu caso o LEFT) e só depois "filtra" através do comando "WHERE".

    Apenas para esclarecimento, a Ordem de Processamento lógico de instrução qualquer consulta T-SQL é a seguinte:

    1. FROM
    2. ON
    3. JOIN
    4. WHERE
    5. GROUP BY
    6. WITH CUBE OU WITH ROLLUP
    7. HAVING
    8. SELECT
    9. DISTINCT
    10. ORDER BY
    11. TOP


    Para maiores informações veja:

    https://technet.microsoft.com/pt-br/library/ms172984%28v=sql.110%29.aspx

    https://msdn.microsoft.com/pt-br/library/ms189499.aspx

    Se ajudou na sua solução, não esqueça de marcar como resposta !

    Abraços,

    Durval Ramos
    Microsoft Partner | MTA | MCSA - SQL Server 2012 | MCSE - Data Platform
    ----------------------------------
    Se foi resolvido clique "Marcar como resposta" e se foi útil "Votar como Útil"

    sexta-feira, 29 de maio de 2015 18:46
    Moderador
  • Durval,

    obrigado!

    Valeu pelo esclarecimento!

    segunda-feira, 1 de junho de 2015 10:53