Tenho um banco de dados PostgreSQL (9.2) com uma tabela de relações pai-filho. Eu tenho uma consulta que procura nós com vários pais.
A consulta a seguir funciona e retorna os resultados corretos:
SELECT node,parents FROM
(
SELECT nr.child AS node, COUNT(nr.parent) AS parents
FROM node_relation nr
GROUP BY nr.child
) AS count WHERE parents > 1;
O conjunto de resultados:
node | parents
--------+---------
n21174 | 2
n8635 | 2
(2 rows)
A definição da tabela é:
Table "public.node_relation"
Column | Type | Modifiers
-------------+-----------------------+---------------
child | character varying(50) | not null
parent | character varying(50) | not null
Indexes:
"node_relation_pkey" PRIMARY KEY, btree (child, parent)
Eu reescrevi a consulta para não usar uma sub-seleção:
SELECT child AS node, COUNT(parent) AS parents
FROM node_relation
GROUP BY child
HAVING COUNT(parent) > 1;
A nova consulta funciona, mas me pergunto se a função COUNT está sendo invocada várias vezes.
Atualização: Aqui está o plano de consulta:
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=0.00..1658.81 rows=19970 width=16)
Filter: (count(parent) > 1)
-> Index Only Scan using node_relation_pkey on node_relation (cost=0.00..1259.40 rows=19971 width=16)
Eu preferiria usar o parents
alias, mas o seguinte não funciona:
SELECT child AS node, COUNT(parent) AS parents
FROM node_relation
GROUP BY child
HAVING parents > 1;
ERROR: column "parents" does not exist
LINE 1: ...parents FROM node_relation GROUP BY child HAVING parents > ...
^
O PostgreSQL otimizará as múltiplas invocações de COUNT
?
Se não, existe uma forma alternativa dessa consulta que seria mais eficiente?
Sua segunda consulta (aquela em que você a implementa com a
HAVING
cláusula) provavelmente é mais rápida. Em sua primeira consulta (com a subseleção), o postgres precisa calcular os valores de contagem para toda a tabela. Em sua segunda consulta, ele pode começar a ignorar as linhas a serem contadas assim que atingir um valor de contagem acima de 1 (embora eu não saiba 100% se o postgres é inteligente o suficiente para fazer isso - tenho quase certeza de que sim).Como
COUNT()
é uma função agregada, ela será executada o número de vezes que for executada, independentemente do número de linhas retornadas. Se você tivesse uma função que NÃO fosse uma função agregada, executar seu grupo e cláusula where/haver em uma subseleção provavelmente seria mais rápido.Exemplo do que estou me referindo:
Para responder especificamente à sua pergunta - sim, o postgres acompanhará quais valores agregados calculou e os reutilizará (em vez de recalculá-los) dentro da
HAVING
cláusula. Acredito que também os reutilizará naSELECT
cláusula (se, por algum motivo estranho, você executar exatamente o mesmo agregado mais de uma vez noSELECT
)Para citar a excelente documentação do Postgres (meu em negrito)
Isso não diz especificamente que reutiliza os valores calculados .. mas implica dizer que a
HAVING
cláusula é usada depois que os agregados são calculados.