Postgres: nombre (*) vs nombre (id)

11

J'ai vu dans la documentation la différence entre count(*)et count(pk). J'avais utilisé count(pk)(où pkest a SERIAL PRIMARY KEY) sans savoir l'existence de count(*).

Ma question concerne les optimisations internes de Postgres. Est-il suffisamment intelligent pour SERIAL PRIMARY KEYcomprendre qu'un va exister dans chaque ligne et ne jamais être faux et simplement compter les lignes ou fera-t-il des vérifications de prédicat redondantes pour chaque ligne? Je suis d'accord que c'est probablement trop d'une optimisation inutile, mais je suis juste curieux.

J'ai jeté un coup d'œil à la sortie de EXPLAINet EXPLAIN VERBOSEpour count(*), count(id)et count(id > 50)voir s'il était EXPLAINmentionné de vérifier les prédicats dans sa sortie. Ce n'est pas le cas.

ldrg
la source

Réponses:

15

J'ai obtenu des résultats cohérents dans mes tests répétés avec différentes versions au cours des dernières années:
count(*)est légèrement plus rapide que count(pk). Il est également plus court et correspond le plus souvent à ce qui est testé: l'existence d'une ligne.

Concernant:

Postgres est-il assez intelligent pour comprendre qu'un SERIAL PRIMARY KEYva exister dans chaque rangée et ne jamais être faux

La seule chose pertinente est la NOT NULLcontrainte. Le PRIMARY KEYest NOT NULLautomatiquement serialou never falseorthogonal à la question.

Avec count(col), si PostgreSQL essayait d'être intelligent et de vérifier si le catalogue système était une colonne NOT NULLet de revenir à un équivalent count(*), vous auriez toujours une recherche de plus sur une table système qu'avec count(*).

Quant à la EXPLAINsortie, il y a un indice:

EXPLAIN SELECT count(*) FROM ...

Aggregate  (cost=4963.38..4963.43 rows=1 width=0) ...


EXPLAIN SELECT count(pk) FROM ...

Aggregate  (cost=4963.38..4963.43 rows=1 width=4) ...

Ce qui signifie, count(col)n'est pas converti en count(*), même s'il est défini NOT NULL.

Erwin Brandstetter
la source
Est-ce toujours le cas avec les nouvelles versions? Je pense qu'il n'aurait pas vraiment besoin d'une recherche pour chaque requête - il pourrait être mis en cache.
Ondra Žižka
1
Btw, avec une NOT NULLcolonne, la différence est grande si vous avez beaucoup de lignes. Dans notre cas avec des millions de lignes, COUNT(*)c'est 3 fois plus rapide. (Postgres 9.4)
Ondra Žižka