Há mais nesta pergunta do que parece.
Versão simples
Isso é muito mais rápido e simples:
SELECT property_name
,(count(value_a = value_b OR NULL) * 100) / count(*) AS pct
FROM my_obj
GROUP BY 1;
Resultado:
property_name | pct
--------------+----
prop_1 | 17
prop_2 | 43
Como?
-
Você não precisa de uma função para isso.
-
Em vez de contarvalue_b(que você não precisa para começar) e calcular o total, usecount(*)para o total. Mais rápido, mais simples.
-
Isso pressupõe que você não tenhaNULLvalores. Ou seja ambas as colunas são definidasNOT NULL. As informações estão faltando na sua pergunta.
Caso contrário, sua consulta original provavelmente não está fazendo o que você acha que faz . Se algum dos valores for NULL, sua versão não contará essa linha. Você pode até provocar uma divisão por zero exceção desta forma.
Esta versão também funciona com NULL.count(*)produz a contagem de todas as linhas, independentemente dos valores.
-
Veja como funciona a contagem:
TRUE OR NULL = TRUE FALSE OR NULL = NULL
count()ignora valores NULL. Voilá.
-
A precedência do operador rege que=vincula antes deOR. Você pode adicionar parênteses para deixar mais claro:
count ((value_a = value_b) OR FALSE) -
Você pode fazer o mesmo com
count NULLIF(<expression>, FALSE) -
O tipo de resultado decount()ébigintpor padrão.
Uma divisãobigint / bigint, trunca dígitos fracionários .
Incluir dígitos fracionários
Use
100.0 (com dígito fracionário) para forçar o cálculo a ser numeric e assim preservar dígitos fracionários.Você pode querer usar
round() com isso:SELECT property_name
,round((count(value_a = value_b OR NULL) * 100.0) / count(*), 2) AS pct
FROM my_obj
GROUP BY 1;
Resultado:
property_name | pct
--------------+-------
prop_1 | 17.23
prop_2 | 43.09
Como um aparte:
eu uso
value_a em vez de valueA . Não use identificadores de maiúsculas e minúsculas sem aspas no PostgreSQL. Tenho visto muitas perguntas desesperadas provenientes dessa loucura. Se você quer saber do que estou falando, leia o capítulo Identificadores e Palavras-Chave do manual.