At tælle rækker i store tabeller er kendt for at være langsom i PostgreSQL. MVCC-modellen kræver en fuld optælling af levende rækker for et præcist antal. Der er løsninger til at fremskynde dette dramatisk hvis optællingen ikke gør det skal være præcis som det ser ud til at være i dit tilfælde.
(Husk, at selv en "nøjagtig" optælling potentielt er død ved ankomst!)
Nøjagtigt antal
Langsom til store borde.
Med samtidige skriveoperationer kan den være forældet i det øjeblik, du får den.
SELECT count(*) AS exact_count FROM myschema.mytable;
Estimat
Ekstremt hurtigt :
SELECT reltuples AS estimate FROM pg_class where relname = 'mytable';
Typisk er estimatet meget tæt på. Hvor tæt, afhænger af om ANALYZE
eller VACUUM
er kørt nok - hvor "nok" er defineret af niveauet af skriveaktivitet til din tabel.
Sikker estimat
Ovenstående ignorerer muligheden for flere tabeller med samme navn i én database - i forskellige skemaer. For at tage højde for det:
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';
Castet til bigint
formaterer den real
tal pænt, især for store tæller.
Bedre skøn
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
Hurtigere, enklere, sikrere, mere elegant. Se manualen om objektidentifikatortyper.
Erstat 'myschema.mytable'::regclass
med to_regclass('myschema.mytable')
i Postgres 9.4+ for ikke at få noget i stedet for en undtagelse for ugyldige tabelnavne. Se:
- Sådan kontrollerer du, om en tabel findes i et givet skema
Bedre skøn endnu (for meget få ekstra omkostninger)
Vi kan gøre, hvad Postgres-planlæggeren gør. Citerer Eksempler på rækkevurdering i manualen:
Disse tal er aktuelle fra den sidste VACUUM
eller ANALYZE
på bordet. Planlæggeren henter derefter det aktuelle aktuelle antal sider i tabellen (dette er en billig operation, der ikke kræver en tabelscanning). Hvis det er forskelligt fra relpages
derefter reltuples
skaleres i overensstemmelse hermed for at nå frem til et aktuelt antal rækker-estimat.
Postgres bruger estimate_rel_size
defineret i src/backend/utils/adt/plancat.c
, som også dækker hjørnet af ingen data i pg_class
fordi forholdet aldrig blev støvsuget. Vi kan gøre noget lignende i SQL:
Minimal form
SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM pg_class
WHERE oid = 'mytable'::regclass; -- your table here
Sikker og eksplicit
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
Bryder ikke med tomme tabeller og tabeller, der aldrig har set VACUUM
eller ANALYZE
. Manualen om pg_class
:
Hvis bordet endnu aldrig er blevet støvsuget eller analyseret, reltuples
indeholder -1
angiver, at rækkeantallet er ukendt.
Hvis denne forespørgsel returnerer NULL
, kør ANALYZE
eller VACUUM
til bordet og gentag. (Alternativt kan du estimere rækkebredden baseret på kolonnetyper, som Postgres gør, men det er kedeligt og udsat for fejl.)
Hvis denne forespørgsel returnerer 0
, synes bordet at være tomt. Men jeg ville ANALYZE
for at være sikker. (Og måske tjekke din autovacuum
indstillinger.)
Typisk block_size
er 8192. current_setting('block_size')::int
dækker sjældne undtagelser.
Tabel- og skemakvalifikationer gør den immun over for enhver search_path
og omfang.
Uanset hvad, tager forespørgslen konsekvent <0,1 ms for mig.
Flere webressourcer:
- Ofte stillede spørgsmål om Postgres Wiki
- Postgres wiki-sider for optællingsestimater og antal(*) ydeevne
TABLESAMPLE SYSTEM (n)
i Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Ligesom @a_horse kommenterede, den tilføjede klausul for SELECT
kommandoen kan være nyttig, hvis statistik i pg_class
er ikke aktuelle nok af en eller anden grund. For eksempel:
- Ingen
autovacuum
kører. - Umiddelbart efter en stor
INSERT
/UPDATE
/DELETE
. TEMPORARY
tabeller (som ikke er dækket afautovacuum
).
Dette ser kun på et tilfældigt n % (1
i eksemplet) valg af blokke og tæller rækker i det. En større prøve øger omkostningerne og reducerer fejlen, dit valg. Nøjagtighed afhænger af flere faktorer:
- Fordeling af rækkestørrelse. Hvis en given blok tilfældigvis har bredere end sædvanlige rækker, er antallet lavere end normalt osv.
- Døde tupler eller en
FILLFACTOR
optage plads pr. blok. Hvis det er ujævnt fordelt over bordet, kan estimatet være deaktiveret. - Generelle afrundingsfejl.
Typisk er estimatet fra pg_class
vil være hurtigere og mere præcis.
Svar på det faktiske spørgsmål
Først skal jeg kende antallet af rækker i den tabel, hvis det samlede antal er større end en foruddefineret konstant,
Og om det ...
... er muligt i det øjeblik tællingen passerer min konstante værdi, vil den stoppe tællingen (og ikke vente med at afslutte tællingen for at informere om at rækkeantallet er større).
Ja. Du kan bruge en underforespørgsel med LIMIT
:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres stopper faktisk med at tælle ud over den givne grænse får du en nøjagtig og aktuel tæller op til n rækker (500.000 i eksemplet) og n Ellers. Ikke nær så hurtigt som estimatet i pg_class
dog.