none
Consulta SQL Usando cláusula HAVING - O q há de errado??? RRS feed

  • Pergunta

  • Olá pessoal,

    Estou tentando executar a consulta abaixo, porém ela só me retorna:

    Invalid column name 'SITUACAO_F1'.

    Como posso resolver isso???

    Code Snippet


    SELECT C.Cand_Codigo, C.Cand_Nome,
    RIGHT(CAST(C.Cand_Ano AS Varchar(10)), 2) +'.'+ CAST(RIGHT((C.Cur_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Ud_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Cand_Codigo * 0.000001), 6) AS Varchar(10)) AS MATRICULA,
    D.Det_Codigo, D.Det_Portugues, D.Det_Matematica, D.Det_Fisica,
    D.Det_Quimica, D.Det_Biologia, D.Det_Presenca1, D.Det_Fecha1,
    (D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) AS PONTOS,
    ((D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) * 1.5) AS MEDIA_F1,
    SITUACAO_F1 =
    CASE when D.Det_Portugues = 0 OR D.Det_Matematica = 0 OR D.Det_Fisica = 0 OR D.Det_Quimica = 0 OR D.Det_Biologia = 0 then 'DES'
    when D.Det_Portugues > 0 OR D.Det_Matematica > 0 OR D.Det_Fisica > 0 OR D.Det_Quimica > 0 OR D.Det_Biologia > 0 then 'CLAS'
    else 'PEN'
    End
    FROM TB_Candidatos C
    LEFT JOIN TB_Detalhes D ON (C.Cand_Codigo = D.Cand_Codigo)
    WHERE C.Ud_Codigo = 2 AND C.Cur_Codigo = 2 AND C.Cand_Pago <> 'N'
    HAVING SITUACAO_F1 = 'CLAS'
    ORDER BY C.Cand_Nome




    Grato,

    Ilano.
    quinta-feira, 4 de dezembro de 2008 16:54

Respostas

  • Olá ilanof,

     

    Segue uma solução:

     

    Code Snippet

    WITH Res AS (

    SELECT C.Cand_Codigo, C.Cand_Nome,

    RIGHT(CAST(C.Cand_Ano AS Varchar(10)), 2) +'.'+ CAST(RIGHT((C.Cur_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Ud_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Cand_Codigo * 0.000001), 6) AS Varchar(10)) AS MATRICULA,

    D.Det_Codigo, D.Det_Portugues, D.Det_Matematica, D.Det_Fisica,

    D.Det_Quimica, D.Det_Biologia, D.Det_Presenca1, D.Det_Fecha1,

    (D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) AS PONTOS,

    ((D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) * 1.5) AS MEDIA_F1,

    SITUACAO_F1 =

    CASE when D.Det_Portugues = 0 OR D.Det_Matematica = 0 OR D.Det_Fisica = 0 OR D.Det_Quimica = 0 OR D.Det_Biologia = 0 then 'DES'

    when D.Det_Portugues > 0 OR D.Det_Matematica > 0 OR D.Det_Fisica > 0 OR D.Det_Quimica > 0 OR D.Det_Biologia > 0 then 'CLAS'

    else 'PEN'

    End

    FROM TB_Candidatos C

    LEFT JOIN TB_Detalhes D ON (C.Cand_Codigo = D.Cand_Codigo)

    WHERE C.Ud_Codigo = 1 AND C.Cur_Codigo = 4 AND C.Cand_Pago <> 'N' AND D.Det_Fecha1 = 'S'

    AND C.NS_Codigo IN (SELECT NS_Codigo FROM TB_NumSalas WHERE Loc_Codigo = 4

    AND NS_Codigo = 31)

    GROUP BY C.Cand_Codigo, C.Cand_Nome, D.Det_Codigo, D.Det_Portugues, D.Det_Matematica, D.Det_Fisica,

    D.Det_Quimica, D.Det_Biologia, D.Det_Presenca1, D.Det_Fecha1)

     

    SELECT * FROM Res WHERE SITUACAO_F1 = 'CLAS'

     

     

    Não estou certo se o GROUP BY é necessário, pois, não há presença de funções de agregação.

     

    [ ]s,

     

    Gustavo

    quinta-feira, 4 de dezembro de 2008 18:37

Todas as Respostas

  • Ilanocf,

     

    Esta coluna existe?

     

    Se você deseja utilizar a clausula Having é necessário utilizar a clausula Group By, não estou encontrando esta clausula no seu código.

    quinta-feira, 4 de dezembro de 2008 17:06
  • Boa Tarde,

     

    Embora sua consulta faça sentido, por uma questão de formação lógica do plano de execução (isso é ANSI não é só para o SQL Server), o GROUP BY é executado antes que a expressão

     

    CASE when D.Det_Portugues = 0 OR D.Det_Matematica = 0 OR D.Det_Fisica = 0 OR D.Det_Quimica = 0 OR D.Det_Biologia = 0 then 'DES'
    when D.Det_Portugues > 0 OR D.Det_Matematica > 0 OR D.Det_Fisica > 0 OR D.Det_Quimica > 0 OR D.Det_Biologia > 0 then 'CLAS'
    else 'PEN'
    End

     

    seja chamada de SITUACAO_F1. Assim sendo, se o GROUP BY é executado antes dessa coluna ganhar esse apelido, o GROUP BY não saberá do que se trata e irá gerar um erro. Há formas de "enganar" o fluxo da formação lógica do plano de execução. Mas antes gostaria de saber se seu SQL Server é 2005.

     

    [ ]s,

     

    Gustavo

     

    quinta-feira, 4 de dezembro de 2008 17:31
  • Olá Júnior Galvão,

    Alterei o código para o abaixo, mas ainda continua o problema:

    Code Snippet

    SELECT C.Cand_Codigo, C.Cand_Nome,
    RIGHT(CAST(C.Cand_Ano AS Varchar(10)), 2) +'.'+ CAST(RIGHT((C.Cur_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Ud_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Cand_Codigo * 0.000001), 6) AS Varchar(10)) AS MATRICULA,
    D.Det_Codigo, D.Det_Portugues, D.Det_Matematica, D.Det_Fisica,
    D.Det_Quimica, D.Det_Biologia, D.Det_Presenca1, D.Det_Fecha1,
    (D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) AS PONTOS,
    ((D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) * 1.5) AS MEDIA_F1,
    SITUACAO_F1 =
    CASE when D.Det_Portugues = 0 OR D.Det_Matematica = 0 OR D.Det_Fisica = 0 OR D.Det_Quimica = 0 OR D.Det_Biologia = 0 then 'DES'
    when D.Det_Portugues > 0 OR D.Det_Matematica > 0 OR D.Det_Fisica > 0 OR D.Det_Quimica > 0 OR D.Det_Biologia > 0 then 'CLAS'
    else 'PEN'
    End
    FROM TB_Candidatos C
    LEFT JOIN TB_Detalhes D ON (C.Cand_Codigo = D.Cand_Codigo)

    WHERE C.Ud_Codigo = 1 AND C.Cur_Codigo = 4 AND C.Cand_Pago <> 'N' AND D.Det_Fecha1 = 'S'
    AND C.NS_Codigo IN (SELECT NS_Codigo FROM TB_NumSalas WHERE Loc_Codigo = 4
    AND NS_Codigo = 31)

    GROUP BY C.Cand_Codigo, C.Cand_Nome, D.Det_Codigo, D.Det_Portugues, D.Det_Matematica, D.Det_Fisica,
    D.Det_Quimica, D.Det_Biologia, D.Det_Presenca1, D.Det_Fecha1


    HAVING SITUACAO_F1 = 'CLAS'

    ORDER BY C.Cand_Nome



    quinta-feira, 4 de dezembro de 2008 18:02
  • Olá ilanof,

     

    Preciso saber qual é a sua versão para lhe passar a solução mais indicada.

     

    [ ]s,

     

    Gustavo

     

    quinta-feira, 4 de dezembro de 2008 18:05
  • Maia,

     

    O group by é lido primeiramente, porque o SQL Server vai fazer a leitura de baixo para cima, visando automatizar o plano de execução.

    quinta-feira, 4 de dezembro de 2008 18:11
  • Olá Jr.

     

    Que estranho. Até onde sei a ordem não é de baixo para cima e nem de cima para baixo, nem esquerda para direita nem algo do tipo, mas sim seguindo uma ordem de passos pré-definida.

     

    Fiquei curioso com sua afirmação de que o plano é executado de baixo para cima. Você poderia me dizer onde encontrou essa afirmação ? Gostaria de ler mais a respeito.

     

    [ ]s,

     

    Gustavo

    quinta-feira, 4 de dezembro de 2008 18:17
  • Gustavo,

    Estou usando SQL Server 2005.
    quinta-feira, 4 de dezembro de 2008 18:25
  • Gustavo,

     

    Eu não encontrei esta informação, na verdade foi em um curso Oficial Microsoft que realizei na Ka-Solution, já faz algum tempo que o instrutor fez esta afirmação.

     

    Não tenho certeza, mas foi justamente essa informação passada.

    quinta-feira, 4 de dezembro de 2008 18:30
  • Olá ilanof,

     

    Segue uma solução:

     

    Code Snippet

    WITH Res AS (

    SELECT C.Cand_Codigo, C.Cand_Nome,

    RIGHT(CAST(C.Cand_Ano AS Varchar(10)), 2) +'.'+ CAST(RIGHT((C.Cur_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Ud_Codigo * 0.01), 2) AS Varchar(10)) +'.'+ CAST(RIGHT((C.Cand_Codigo * 0.000001), 6) AS Varchar(10)) AS MATRICULA,

    D.Det_Codigo, D.Det_Portugues, D.Det_Matematica, D.Det_Fisica,

    D.Det_Quimica, D.Det_Biologia, D.Det_Presenca1, D.Det_Fecha1,

    (D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) AS PONTOS,

    ((D.Det_Portugues + D.Det_Matematica + D.Det_Fisica + D.Det_Quimica + D.Det_Biologia) * 1.5) AS MEDIA_F1,

    SITUACAO_F1 =

    CASE when D.Det_Portugues = 0 OR D.Det_Matematica = 0 OR D.Det_Fisica = 0 OR D.Det_Quimica = 0 OR D.Det_Biologia = 0 then 'DES'

    when D.Det_Portugues > 0 OR D.Det_Matematica > 0 OR D.Det_Fisica > 0 OR D.Det_Quimica > 0 OR D.Det_Biologia > 0 then 'CLAS'

    else 'PEN'

    End

    FROM TB_Candidatos C

    LEFT JOIN TB_Detalhes D ON (C.Cand_Codigo = D.Cand_Codigo)

    WHERE C.Ud_Codigo = 1 AND C.Cur_Codigo = 4 AND C.Cand_Pago <> 'N' AND D.Det_Fecha1 = 'S'

    AND C.NS_Codigo IN (SELECT NS_Codigo FROM TB_NumSalas WHERE Loc_Codigo = 4

    AND NS_Codigo = 31)

    GROUP BY C.Cand_Codigo, C.Cand_Nome, D.Det_Codigo, D.Det_Portugues, D.Det_Matematica, D.Det_Fisica,

    D.Det_Quimica, D.Det_Biologia, D.Det_Presenca1, D.Det_Fecha1)

     

    SELECT * FROM Res WHERE SITUACAO_F1 = 'CLAS'

     

     

    Não estou certo se o GROUP BY é necessário, pois, não há presença de funções de agregação.

     

    [ ]s,

     

    Gustavo

    quinta-feira, 4 de dezembro de 2008 18:37
  • Oi Jr.

     

    Acredito que o seu intrutor equivocou-se, pois, não é assim que funciona. Toda vez que uma instrução SELECT é enviada, os seguintes passos são executados (nessa ordem):

     

    (1) Todas as tabelas no FROM são unidas montando um plano cartesiano
    (2) Os filtros baseados na cláusula ON são executados
    (3) Os tipos de JOINs (LEFT, INNER, RIGHT, etc) são avaliados, para verificar que linhas devem ou não permanecer
    (4) Os filtros da cláusula WHERE são aplicados
    (5) As colunas na cláusula GROUP BY são aplicadas montando os grupos
    (6) A cláusula WITH CUBE ou WITH ROLLUP é aplicada
    (7) A cláusula HAVING é aplicada fazendo os filtros nos grupos
    (8) A cláusula SELECT é montada
    (9) As repetições são filtradas pelo DISTINCT
    (10) A ordenação é feita pelo ORDER BY
    (11) Os N primeiros registros são retornados pelo TOP

     

    É por isso que a cláusula HAVING falhou no exemplo. Como o SELECT é aplicado depois do HAVING, no momento do HAVING não há como saber que existe uma coluna chamada 'SITUACAO_F1' por isso a mensagem de erro. Utilizar um Alias na cláusula WHERE incorre no mesmo problema.

     

    É por isso que é possível utilizar os Alias na cláusula ORDER BY, pois, como os Alias são definidos no SELECT e ele é avaliado antes do ORDER BY, no momento do ORDER BY, a coluna com o ALIAS já é conhecido (assim ORDER BY SITUACAO_F1 funcionaria perfeitamente).

     

    Pode ser que o SQL Server fure a ordem, se fisicamente achar uma ordem mais eficiente e se ela não alterar o resultado. O que é certo é que ele não irá avaliar o plano da esquerda para direita, de cima para baixo, ou qualquer outra ordem de direção pre-estabelecida.

     

    Essa informação pode ser confirmada em Inside SQL Server 2005 - T-SQL Querying e no Training Kit da prova 70-442.

     

    [ ]s,

     

    Gustavo

    quinta-feira, 4 de dezembro de 2008 18:46
  • Maia,

     

    Muito obrigado por estes detalhes, eu já conhecia basicamente esta ordem, obrigado por suas colocações.

     

    Vou procurar mais informações.

     

    quinta-feira, 4 de dezembro de 2008 18:54