Het is bekend dat het tellen van rijen in grote tabellen traag is in PostgreSQL. Het MVCC-model vereist een volledige telling van live-rijen voor een nauwkeurig aantal. Er zijn oplossingen om dit drastisch te versnellen als de telling niet doet moet exact zijn zoals het in jouw geval lijkt te zijn.
(Vergeet niet dat zelfs een "exacte" telling mogelijk dood is bij aankomst!)
Exact aantal
Langzaam voor grote tabellen.
Met gelijktijdige schrijfbewerkingen kan het verouderd zijn op het moment dat u het krijgt.
SELECT count(*) AS exact_count FROM myschema.mytable;
Schatting
Extreem snel :
SELECT reltuples AS estimate FROM pg_class where relname = 'mytable';
Meestal is de schatting heel dichtbij. Hoe dichtbij, hangt af van of ANALYZE
of VACUUM
genoeg worden uitgevoerd - waarbij "genoeg" wordt gedefinieerd door het niveau van schrijfactiviteit naar uw tabel.
Veiligere schatting
Het bovenstaande negeert de mogelijkheid van meerdere tabellen met dezelfde naam in één database - in verschillende schema's. Om daar rekening mee te houden:
SELECT c.reltuples::bigint AS estimate
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable'
AND n.nspname = 'myschema';
De cast naar bigint
formatteert de real
nummer mooi, vooral voor grote tellingen.
Betere schatting
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
Sneller, eenvoudiger, veiliger, eleganter. Zie de handleiding over Object Identifier Types.
Vervang 'myschema.mytable'::regclass
met to_regclass('myschema.mytable')
in Postgres 9.4+ om niets te krijgen in plaats van een uitzondering voor ongeldige tabelnamen. Zie:
- Hoe te controleren of een tabel in een bepaald schema bestaat
Nog betere schatting (tegen zeer weinig extra kosten)
Wij kunnen doen wat de Postgres planner doet. Citaat van de Voorbeelden van rijschattingen in de handleiding:
Deze nummers zijn actueel vanaf de laatste VACUUM
of ANALYZE
op de tafel. De planner haalt dan het actuele aantal pagina's in de tabel op (dit is een goedkope handeling, waarvoor geen tabelscan nodig is). Als dat anders is dan relpages
dan reltuples
wordt overeenkomstig geschaald om tot een huidige schatting van het aantal rijen te komen.
Postgres gebruikt estimate_rel_size
gedefinieerd in src/backend/utils/adt/plancat.c
, die ook het hoekgeval van geen gegevens dekt in pg_class
omdat de relatie nooit is gestofzuigd. We kunnen iets soortgelijks doen in SQL:
Minimale vorm
SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM pg_class
WHERE oid = 'mytable'::regclass; -- your table here
Veilig en expliciet
SELECT (CASE WHEN c.reltuples < 0 THEN NULL -- never vacuumed
WHEN c.relpages = 0 THEN float8 '0' -- empty table
ELSE c.reltuples / c.relpages END
* (pg_relation_size(c.oid) / pg_catalog.current_setting('block_size')::int)
)::bigint
FROM pg_class c
WHERE c.oid = 'myschema.mytable'::regclass; -- schema-qualified table here
Breekt niet met lege tabellen en tabellen die nog nooit VACUUM
hebben gezien of ANALYZE
. De handleiding op pg_class
:
Als de tafel nog nooit is gestofzuigd of geanalyseerd, reltuples
bevat -1
wat aangeeft dat het aantal rijen onbekend is.
Als deze zoekopdracht NULL
retourneert , voer ANALYZE
uit of VACUUM
voor de tafel en herhaal. (U kunt ook de rijbreedte schatten op basis van kolomtypen zoals Postgres doet, maar dat is vervelend en foutgevoelig.)
Als deze zoekopdracht 0
oplevert , lijkt de tafel leeg te zijn. Maar ik zou ANALYZE
om er zeker van te zijn. (En misschien controleer je autovacuum
instellingen.)
Meestal block_size
is 8192. current_setting('block_size')::int
dekt zeldzame uitzonderingen.
Tabel- en schemakwalificaties maken het immuun voor elk search_path
en reikwijdte.
Hoe dan ook, de query duurt bij mij consequent <0,1 ms.
Meer internetbronnen:
- Veelgestelde vragen over Postgres Wiki
- De Postgres-wikipagina's voor schattingen van tellingen en prestaties van telling(*)
TABLESAMPLE SYSTEM (n)
in Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Zoals @a_horse opmerkte, de toegevoegde clausule voor de SELECT
commando kan handig zijn als statistieken in pg_class
zijn om de een of andere reden niet actueel genoeg. Bijvoorbeeld:
- Geen
autovacuum
rennen. - Onmiddellijk na een grote
INSERT
/UPDATE
/DELETE
. TEMPORARY
tabellen (die niet worden gedekt doorautovacuum
).
Dit kijkt alleen naar een willekeurige n % (1
in het voorbeeld) selectie van blokken en telt rijen daarin. Een groter monster verhoogt de kosten en vermindert de fout, uw keuze. Nauwkeurigheid hangt van meer factoren af:
- Verdeling van rijgrootte. Als een bepaald blok bredere rijen heeft dan normaal, is de telling lager dan normaal, enz.
- Dode tuples of een
FILLFACTOR
ruimte per blok innemen. Indien ongelijk verdeeld over de tafel, kan de schatting niet kloppen. - Algemene afrondingsfouten.
Meestal is de schatting van pg_class
zal sneller en nauwkeuriger zijn.
Antwoord op de eigenlijke vraag
Eerst moet ik het aantal rijen in die tabel weten, als het totaalaantal groter is dan een vooraf gedefinieerde constante,
En of het ...
... is mogelijk op het moment dat de telling mijn constante waarde overschrijdt, zal het het tellen stoppen (en niet wachten om het tellen te voltooien om te informeren dat het aantal rijen groter is).
Ja. U kunt een subquery gebruiken met LIMIT
:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres stopt eigenlijk met tellen boven de gegeven limiet, krijg je een exacte en actuele tel tot n rijen (500000 in het voorbeeld), en n anders. Lang niet zo snel als de schatting in pg_class
, hoewel.