O problema descrito a seguir se aplica aos bancos de dados Oracle 11g. Também testei as instruções no MySQL e PostgreSQL sem problemas do tipo mencionado nesta pergunta.
Eu quero usar uma função de agregação em uma instrução de seleção que precisa de algum cálculo adicional para produzir o resultado. Meu objetivo principal é manter todo o cálculo em uma declaração, pois o resultado deve ser exibido literalmente no software de back-end.
Que haja uma tabela T
definida por este DDL
create table T(
ID number,
NUM number,
TXT varchar(32)
);
insert into T (ID, NUM, TXT) values (1, 1, 'Text1');
insert into T (ID, NUM, TXT) values (2, 2, 'Text2');
insert into T (ID, NUM, TXT) values (3, 0, 'Text3');
Agora o objetivo é somar todos os valores do atributo num para todas as linhas selecionadas. Eu posso fazer isso usando
select sum(num) from T
Agora, para a parte complicada, suponha que haja uma segunda tabela a partir da qual calculamos um valor adicional a ser combinado com o resultado de nossa primeira seleção. O select envolvido retornará um único número por ser um valor agregado por si só. (Para manter o esquema simples desta demonstração, utilizo a mesma tabela, mas o problema descrito permanece independentemente da segunda tabela utilizada)
select case (select count(*) from T) when 0 then 1 else 0 end from dual;
Quando combinados, deixaremos de fora a select ... from dual
parte que é usada apenas para obter o resultado intermediário exibido. Juntos, a consulta será
select (case (select count(t1.num) from T t1) when 0 then 1 else 0 end)*sum(t2.num) from T t2;
onde atribuímos aliases a cada tabela para tornar o uso claro.
O resultado dessa consulta é uma exceção do Oracle: ORA-00937: not a single-group group function
. Como a expressão case produz um único valor e nenhuma referência é feita a nenhum recurso fora da expressão, eu me pergunto qual princípio básico eu violei com essa instrução select. Além disso, MySQL e PostgreSQL produzem o resultado desejado.
Enquanto procurava uma solução, também tentei esta instrução select:
select (case when (exists (select 1 from T where NUM = 0)) then 0 else 1 end)*sum(NUM) as p from T where NUM > 0;
Aqui descartei a função de agregação interna count(t1.num)
para descartar problemas com agregações aninhadas. Esta instrução deu a mesma mensagem de erro do Oracle, enquanto o cálculo do valor adicional não causou problemas:
select (case when (exists (select 1 from T where NUM = 0)) then 0 else 1 end) as p from dual;
Pergunta : Alguém pode explicar o que está errado? (E principalmente me pergunto se o Oracle não implementa o padrão SQL, ou se o MySQL e o PostgreSQL estendem o padrão).
Tecnicamente, a solução para o meu problema seria uma junção em que ambos, o agregado e o valor adicional são calculados separadamente e, em seguida, combinados na instrução de seleção externa. Meu foco não está em como alguém pode resolver esse problema, mas em estou perdendo alguma coisa no Oracle ou está faltando algo no Oracle? .
Seu valor adicional calculado:
É uma subconsulta escalar, que é uma expressão dinâmica em vez de estática. Como tal, é tratado da mesma forma que uma coluna no que diz respeito ao agregado e precisa ser incluído na cláusula group by para evitar o
ORA-00937: not a single-group group function
erroNo entanto, o oracle não permite subconsultas como parte da cláusula group by e tentando incluir a subconsulta escalar e/ou toda a instrução case:
apenas resulta em um
ORA-22818: subquery expressions not allowed here
erro.As únicas maneiras de contornar isso são converter sua subconsulta escalar em um valor agregado da seguinte forma:
ou
ou reescreva sua consulta para mover a subconsulta escalar não agregada para fora da consulta agregada:
ou pré-compute sua subconsulta escalar para que ela possa ser usada na cláusula group by: