Bom dia.
Preciso fazer uma estatística de atendimento de ocorrências e para isso preciso retornar pelo comando select o model e o count(m), ao fazê-lo recebo a mensagem de que não é possível retornar dois tipos de dados, executando o comando no pgAdmin o comando executa perfeitamente da forma que pretendo utilizar. Isso me faz pensar que deva ter outro jeito de realizar o que preciso, alguém saberia a melhor forma de fazer isso?
O comando que funciona no pgAdmin:
SELECT count(m) as quantidade, m.codigoOcorrencia_id FROM MovimentacaoVTR as m GROUP BY m.codigoOcorrencia_id
No repositorio
public List<MovimentacaoVTR> teste() {
TypedQuery<MovimentacaoVTR> query = manager.createQuery("SELECT count(m) as quantidade, m.codigoOcorrencia FROM MovimentacaoVTR as m GROUP BY m.codigoOcorrencia", MovimentacaoVTR.class);
return query.getResultList();
}
A função COUNT é uma função agregadora. Em SQL, uma função agregadora é aquela que permite executar uma operação aritmética nos valores de uma determinada coluna em todos os registros de uma tabela. Como executam sobre todos os registros, trazem um valor único representado pelo resultado dessa operação aritmética. Por esse motivo, são conhecidas como operações agregadoras.
No seu caso, pode usar criar uma união de consultas. Por exemplo:
SELECT COUNT(m) as quantidade FROM MovimentacaoVTR as m GROUP BY m.codigoOcorrencia_id
UNION
SELECT m.codigoOcorrencia_id FROM MovimentacaoVTR as m GROUP BY m.codigoOcorrencia_id;
Ou uma subconsulta:
SELECT COUNT(m) as quantidade,
(SELECT m.codigoOcorrencia_id FROM MovimentacaoVTR as m GROUP BY m.codigoOcorrencia_id)
FROM MovimentacaoVTR as m GROUP BY m.codigoOcorrencia_id;
Mas se eu não me engano, as subconsultas são relativamente mais demoradas.
Achei interessante a primeira solução, o UNION mas pelo que vi ele não tem suporte no jpql mas mesmo assim, como eu faria para que o count(m) ficasse em uma coluna separada?
No caso da subconsulta recebo o seguinte erro no pgAdmin “more than one row returned by a subquery used as an expression”
Eu não sei se no JPQL tem o ‘comando’ LIMIT que permite limitar quantos retornos uma
consulta irá ter. Por exemplo:
SELECT COUNT(m) as quantidade,
(SELECT m.codigoOcorrencia_id FROM MovimentacaoVTR as m LIMIT 1)
FROM MovimentacaoVTR as m GROUP BY m.codigoOcorrencia_id;
Você pode dar uma verificada sobre uma solução disso em JPQL aqui: JPQL limit query.
Sua solução acima funciona no postgreSql mas ao implementá-la na classe repositorio alterando o Limit 1 que não foi reconhecido no JPQL pelo distinct recebo o mesmo erro que eu recebia:
java.lang.IllegalArgumentException: Cannot create TypedQuery for query with more than one return using requested result type [com.sistemas.sigcm.model.MovimentacaoVTR]
Fiz também da seguinte forma e também funcionou apenas no postgreSql, quando implemento na classe do repositorio ainda tenho o erro já relatado acima:
SELECT m.codigoOcorrencia, count(m.codigoOcorrencia) FROM MovimentacaoVTR m GROUP BY m.codigoOcorrencia, codigoOcorrencia_id
Penso que o erro está no retorno onde estou usando uma List mas não tenho certeza:
De acordo com a exceção lançada: “Não é possível criar TypedQuery para consulta com mais de um retorno usando o tipo de resultado solicitado”. Não tem acesso a documentação do JPQL? Eu nunca o usei… então não posso ajudar.
Assumindo que MovimentacaoVTR tem campos codigoOcorrencia e countCodigoOcorrencia:
Na query:
SELECT new MovimentacaoVTR(m.codigoOcorrencia, count(m.codigoOcorrencia))
FROM MovimentacaoVTR m
GROUP BY m.codigoOcorrencia, codigoOcorrencia_id
Na entidade MovimentacaoVTR:
public MovimentacaoVTR(String codigoOcorrencia, Long contagem) {
this.codigoOcorrencia = codigoOcorrencia;
this.countCodigoOcorrencia = contagem; // não tenho certeza se deva ser Long
}
@pmlm achei muito interessante o conceito do uso do SELECT new, eu não conhecia, ele possibilita o retorno de mais de um elemento até então limitado pelo TypedQuery. Agora estou tendo um problema com a Entidade MovimentaçãoVTR que na anotação @Entity tenho o seguinte erro:
The Java class for mapped type "MovimentacaoVTR" must define a non-private zero-argument constructor
Outra coisa, não tenho o campo countCodigoOcorrencia na Entidade MovimentacaoVTR, penso que o valor contido nela seria aleatório e não sei se seria uma boa prática ter esse campo, estou enganado?
Para ficar mais claro o campo m.codigoOcorrencia é um relacionamento @ManyToOne. Será que há como contornar isso?
Todas as classes Java, se não tiverem um construtor definido, definem um por omissão. Ao criar o teu construtor estás a “esconder” esse, pelo que deves declarar também o construtor vazio
public MovimentacaoVTR(){
}
Se queres devolver o valor da contagem, ele tem de ser escrito em algum lado. Nesse caso, podes usar campos transient, que servem para estes propósitos de valores que não são guardados na BD mas podem ser de alguma forma calculados.
@Transient
private Long countCodigoOcorrencia;
Se o codigoOcorrencia é uma entidade e não uma String só tens de alterar o construtor para receber um objecto desse tipo e não uma String