sql >> Databasteknik >  >> RDS >> PostgreSQL

Snabbt sätt att upptäcka radantalet i en tabell i PostgreSQL

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 eller ANALYZE 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ån relpages sedan reltuples 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 av autovacuum ).

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.



  1. hur man använder dbms_scheduler för att köra jobbet var 30:e minut

  2. GROUP BY / aggregerad funktionsförvirring i SQL

  3. Handledning för SQL-transaktioner

  4. Komma igång med Oracle Application Express-APEX