PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

GROUP BY + instrução CASE


Sua consulta já funcionaria - exceto que você está enfrentando conflitos de nomenclatura ou apenas confundindo a coluna de saída (o CASE expressão) com coluna de origem result , que tem conteúdo diferente.
...
GROUP BY model.name, attempt.type, attempt.result
...

Você precisa GROUP BY seu CASE expressão em vez de sua coluna de origem:
...
GROUP BY model.name, attempt.type
       , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END
...

Ou forneça um alias de coluna que é diferente de qualquer nome de coluna no FROM list - ou então essa coluna tem precedência:
SELECT ...
     , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END AS result1
...
GROUP BY model.name, attempt.type, result1
...

O padrão SQL é bastante peculiar a esse respeito. Citando o manual aqui:

O nome de uma coluna de saída pode ser usado para se referir ao valor da coluna em ORDER BY e GROUP BY cláusulas, mas não no WHERE ou HAVING cláusulas; lá você deve escrever a expressão.

E:

Se um ORDER BY expressão é um nome simples que corresponde a um nome de coluna de saída e a um nome de coluna de entrada, ORDER BY irá interpretá-lo como o nome da coluna de saída. Isso é o oposto da escolha que GROUP BY fará na mesma situação. Essa inconsistência é feita para ser compatível com o padrão SQL.

Negrito ênfase minha.

Esses conflitos podem ser evitados usando referências posicionais (números ordinais) em GROUP BY e ORDER BY , referenciando itens no SELECT lista da esquerda para a direita. Veja a solução abaixo.
A desvantagem é que isso pode ser mais difícil de ler e vulnerável a edições no SELECT list (pode-se esquecer de adaptar as referências posicionais de acordo).

Mas você não tem que adicionar a coluna day para o GROUP BY cláusula, desde que mantenha um valor constante (CURRENT_DATE-1 ).

Reescrito e simplificado com a sintaxe JOIN adequada e referências posicionais, poderia ficar assim:
SELECT m.name
     , a.type
     , CASE WHEN a.result = 0 THEN 0 ELSE 1 END AS result
     , CURRENT_DATE - 1 AS day
     , count(*) AS ct
FROM   attempt    a
JOIN   prod_hw_id p USING (hard_id)
JOIN   model      m USING (model_id)
WHERE  ts >= '2013-11-06 00:00:00'  
AND    ts <  '2013-11-07 00:00:00'
GROUP  BY 1,2,3
ORDER  BY 1,2,3;

Observe também que estou evitando o nome da coluna time . Essa é uma palavra reservada e nunca deve ser usada como identificador. Além disso, seu "tempo" obviamente é um timestamp ou date , de modo que é bastante enganador.