Usuário com melhor resposta
Consulta SQL Usando cláusula HAVING - O q há de errado???

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.
Respostas
-
Olá ilanof,
Segue uma solução:
Code SnippetWITH
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 CLEFT
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 = 4AND
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
Todas as Respostas
-
-
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'
Endseja 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
-
Olá Júnior Galvão,
Alterei o código para o abaixo, mas ainda continua o problema:
Code SnippetSELECT 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 -
-
-
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
-
-
-
Olá ilanof,
Segue uma solução:
Code SnippetWITH
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 CLEFT
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 = 4AND
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
-
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
-