Att räkna rader i stora tabeller är känt för att vara långsamt i PostgreSQL. MVCC-modellen kräver ett fullständigt antal levande rader för ett exakt antal. Det finns lösningar för att påskynda detta dramatiskt om räkningen inte gör det måste vara exakt som det verkar vara i ditt fall.
(Kom ihåg att även en "exakt" räkning är potentiellt död vid ankomst!)
Exakt antal
Långsamt för stora tabeller.
Med samtidiga skrivoperationer kan den vara föråldrad så fort du får den.
SELECT count(*) AS exact_count FROM myschema.mytable;
Uppskattning
Extremt snabb :
SELECT reltuples AS estimate FROM pg_class where relname = 'mytable';
Normalt är uppskattningen mycket nära. Hur nära, beror på om ANALYZE
eller VACUUM
körs tillräckligt - där "tillräckligt" definieras av nivån på skrivaktiviteten till din tabell.
Säkrare uppskattning
Ovanstående ignorerar möjligheten av flera tabeller med samma namn i en databas - i olika scheman. För att ta hänsyn till 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';
Casten till bigint
formaterar den real
siffra bra, särskilt för stora räkningar.
Bättre uppskattning
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
Snabbare, enklare, säkrare, mer elegant. Se manualen om objektidentifieringstyper.
Ersätt 'myschema.mytable'::regclass
med to_regclass('myschema.mytable')
i Postgres 9.4+ för att få ingenting istället för ett undantag för ogiltiga tabellnamn. Se:
- Hur man kontrollerar om en tabell finns i ett givet schema
Ännu bättre uppskattning (för mycket liten extra kostnad)
Vi kan göra det som Postgres-planeraren gör. Citerar exemplen på raduppskattning i manualen:
Dessa nummer är aktuella från och med den senaste
VACUUM
ellerANALYZE
på bordet. Planeraren hämtar sedan det faktiska aktuella antalet sidor i tabellen (detta är en billig operation som inte kräver en tabellskanning). Om det skiljer sig frånrelpages
sedanreltuples
skalas i enlighet med detta för att komma fram till en aktuell uppskattning av antalet rader.
Postgres använder estimate_rel_size
definieras i src/backend/utils/adt/plancat.c
, som också täcker hörnfallet av inga data i pg_class
eftersom relationen aldrig dammsugdes. Vi kan göra något liknande i SQL:
Minimal form
SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM pg_class
WHERE oid = 'mytable'::regclass; -- your table here
Säkert och tydligt
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
Bryter inte med tomma tabeller och tabeller som aldrig har sett VACUUM
eller ANALYZE
. Manualen om pg_class
:
Om tabellen ännu aldrig har dammsugits eller analyserats,
reltuples
innehåller-1
indikerar att radantalet är okänt.
Om denna fråga returnerar NULL
, kör ANALYZE
eller VACUUM
för bordet och upprepa. (Alternativt kan du uppskatta radbredden baserat på kolumntyper som Postgres gör, men det är tråkigt och felbenäget.)
Om denna fråga returnerar 0
, tabellen verkar vara tom. Men jag skulle ANALYZE
att försäkra sig. (Och kanske kontrollera ditt autovacuum
inställningar.)
Vanligtvis block_size
är 8192. current_setting('block_size')::int
täcker sällsynta undantag.
Tabell- och schemakvalifikationer gör den immun mot alla search_path
och omfattning.
Hur som helst tar frågan konsekvent <0,1 ms för mig.
Fler webbresurser:
- Postgres Wiki FAQ
- Postgres wiki-sidor för räkningsuppskattningar och count(*) prestanda
TABLESAMPLE SYSTEM (n)
i Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Som @a_horse kommenterade, den tillagda satsen för SELECT
kommandot kan vara användbart om statistik i pg_class
är inte tillräckligt aktuella av någon anledning. Till exempel:
- Inget
autovacuum
igång. - Omedelbart efter en stor
INSERT
/UPDATE
/DELETE
. TEMPORARY
tabeller (som inte täcks avautovacuum
).
Detta tittar bara på ett slumpmässigt n % (1
i exemplet) urval av block och räknar rader i det. Ett större urval ökar kostnaden och minskar felet, ditt val. Noggrannheten beror på fler faktorer:
- Fördelning av radstorlek. Om ett givet block råkar ha bredare rader än vanligt, är antalet lägre än vanligt osv.
- Döda tupler eller en
FILLFACTOR
tar plats per block. Om den är ojämnt fördelad över tabellen kan uppskattningen vara avstängd. - Allmänna avrundningsfel.
Vanligtvis är uppskattningen från pg_class
blir snabbare och mer exakt.
Svar på faktisk fråga
Först måste jag veta antalet rader i den tabellen, om totalantalet är större än någon fördefinierad konstant,
Och om det ...
... är möjligt i det ögonblick räkningen passerar mitt konstanta värde, kommer det att stoppa räkningen (och inte vänta med att slutföra räkningen för att informera om att antalet rader är större).
Ja. Du kan använda en underfråga med LIMIT
:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres slutar faktiskt att räknas utöver den givna gränsen får du en exakt och aktuell räknas upp till n rader (500 000 i exemplet) och n annat. Inte alls lika snabbt som uppskattningen i pg_class
dock.