2010-04-07 10 views
273

Sto cercando un modo per trovare il numero di righe per tutte le mie tabelle in Postgres. So che posso fare questo una tabella alla volta con:Come si trova il numero di righe per tutte le tabelle in Postgres

SELECT count(*) FROM table_name; 

ma mi piacerebbe vedere il conteggio delle righe per tutti i tavoli e poi ORDER BY che per avere un'idea di quanto grande tutte le mie tabelle sono .

risposta

387

Ci sono tre modi per ottenere questo tipo di conteggio, ognuno con le proprie compromessi.

Se si desidera un conteggio vero, è necessario eseguire l'istruzione SELECT come quella utilizzata per ogni tabella. Questo perché PostgreSQL mantiene le informazioni sulla visibilità delle righe nella riga stessa, non in qualsiasi altro luogo, quindi qualsiasi conteggio accurato può essere relativo solo ad alcune transazioni. Stai ottenendo un conteggio di ciò che la transazione vede nel momento in cui viene eseguita. Potresti automatizzarlo per essere eseguito su ogni tabella del database, ma probabilmente non hai bisogno di quel livello di precisione o vuoi aspettare così a lungo.

Il secondo approccio rileva che il raccoglitore di statistiche tiene traccia approssimativamente quante righe sono "live" (non eliminate o obsolete dagli aggiornamenti successivi) in qualsiasi momento. Questo valore può essere spento da un po 'sotto un'attività pesante, ma è generalmente una buona stima:

SELECT schemaname,relname,n_live_tup 
    FROM pg_stat_user_tables 
    ORDER BY n_live_tup DESC; 

Che può anche mostrare come molte righe sono morti, che è essa stessa un numero interessante monitorare.

Il terzo modo è notare che il comando ANALYZE di sistema, che viene eseguito regolarmente dal processo autovacuum da PostgreSQL 8.3 per aggiornare le statistiche della tabella, calcola anche una stima di riga. È possibile prendere quello come questo:

SELECT 
    nspname AS schemaname,relname,reltuples 
FROM pg_class C 
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) 
WHERE 
    nspname NOT IN ('pg_catalog', 'information_schema') AND 
    relkind='r' 
ORDER BY reltuples DESC; 

Quale di queste query è meglio utilizzare è difficile da dire. Normalmente prendo questa decisione in base al fatto che ci siano altre informazioni utili che voglio usare anche all'interno di pg_class o all'interno di pg_stat_user_tables. Ai fini del conteggio di base solo per vedere come sono le cose grandi in generale, entrambi dovrebbero essere abbastanza precisi.

13

Se non ti dispiace dati potenzialmente obsoleti, puoi access the same statistics used by the query optimizer.

Qualcosa di simile:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables; 
+0

Ho provato ad utilizzare questo, ma i dati di molto stantio davvero. – mlissner

+0

@mlissner: se l'intervallo autovacuum è troppo lungo o non hanno eseguito un manuale 'ANALYZE' sul tavolo, le statistiche possono ottenere fuori strada. La sua una questione di caricamento del database e come il database è configurato (se le statistiche vengono aggiornate con maggiore frequenza, le statistiche saranno più accurate, ma potrebbe ridurre le prestazioni di esecuzione). In definitiva, l'unico modo per ottenere dati accurati è eseguire 'select count (*) dalla tabella' per tutte le tabelle. – ig0774

+4

appena provato e NON è la risposta giusta. –

4

Non ricordo l'URL da dove ho raccolto questo. Ma spero che questo dovrebbe aiutare:

CREATE TYPE table_count AS (table_name TEXT, num_rows INTEGER); 

CREATE OR REPLACE FUNCTION count_em_all() RETURNS SETOF table_count AS ' 
DECLARE 
    the_count RECORD; 
    t_name RECORD; 
    r table_count%ROWTYPE; 

BEGIN 
    FOR t_name IN 
     SELECT 
      c.relname 
     FROM 
      pg_catalog.pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace 
     WHERE 
      c.relkind = ''r'' 
      AND n.nspname = ''public'' 
     ORDER BY 1 
     LOOP 
      FOR the_count IN EXECUTE ''SELECT COUNT(*) AS "count" FROM '' || t_name.relname 
      LOOP 
      END LOOP; 

      r.table_name := t_name.relname; 
      r.num_rows := the_count.count; 
      RETURN NEXT r; 
     END LOOP; 
     RETURN; 
END; 
' LANGUAGE plpgsql; 

Esecuzione select count_em_all(); dovrebbe ottenere si fila conteggio di tutte le tabelle.

+1

È consigliabile citare i nomi delle colonne (come 'quote_ident (t_name.relname)') per garantire il corretto supporto per nomi insoliti ("nome-colonna", ad esempio) . – gorsky

+0

farlo cadere in seguito: DROP FUNCTION count_em_all(); –

+0

ottenuto un errore: select count_em_all(); ERRORE: errore di sintassi in corrispondenza di "group" LINE 1: SELECT COUNT() AS "count" FROM group^QUERY: SELECT COUNT() AS "count" FROM group CONTEXT: PL/pgSQL function count_em_all() riga 18 su FOR over Istruzione EXECUTE –

8

Non sono sicuro se una risposta in bash è accettabile per voi, ma FWIW ...

PGCOMMAND=" psql -h localhost -U fred -d mydb -At -c \" 
      SELECT table_name 
      FROM  information_schema.tables 
      WHERE table_type='BASE TABLE' 
      AND  table_schema='public' 
      \"" 
TABLENAMES=$(export PGPASSWORD=test; eval "$PGCOMMAND") 

for TABLENAME in $TABLENAMES; do 
    PGCOMMAND=" psql -h localhost -U fred -d mydb -At -c \" 
       SELECT '$TABLENAME', 
         count(*) 
       FROM  $TABLENAME 
       \"" 
    eval "$PGCOMMAND" 
done 
+6

Nella sua essenza, si riduce allo stesso 'select count (*) from table_name;' nell'OP! –

4

io di solito non si basano sulle statistiche, soprattutto in PostgreSQL.

SELECT table_name, dsql2('select count(*) from '||table_name) as rownum 
FROM information_schema.tables 
WHERE table_type='BASE TABLE' 
    AND table_schema='livescreen' 
ORDER BY 2 DESC; 
CREATE OR REPLACE FUNCTION dsql2(i_text text) 
    RETURNS int AS 
$BODY$ 
Declare 
    v_val int; 
BEGIN 
    execute i_text into v_val; 
    return v_val; 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 
3

Ho fatto una piccola variazione per includere tutte le tabelle, anche per le tabelle non pubblici.

CREATE TYPE table_count AS (table_schema TEXT,table_name TEXT, num_rows INTEGER); 

CREATE OR REPLACE FUNCTION count_em_all() RETURNS SETOF table_count AS ' 
DECLARE 
    the_count RECORD; 
    t_name RECORD; 
    r table_count%ROWTYPE; 

BEGIN 
    FOR t_name IN 
     SELECT table_schema,table_name 
     FROM information_schema.tables 
     where table_schema !=''pg_catalog'' 
      and table_schema !=''information_schema'' 
     ORDER BY 1,2 
     LOOP 
      FOR the_count IN EXECUTE ''SELECT COUNT(*) AS "count" FROM '' || t_name.table_schema||''.''||t_name.table_name 
      LOOP 
      END LOOP; 

      r.table_schema := t_name.table_schema; 
      r.table_name := t_name.table_name; 
      r.num_rows := the_count.count; 
      RETURN NEXT r; 
     END LOOP; 
     RETURN; 
END; 
' LANGUAGE plpgsql; 

uso select count_em_all(); chiamarlo.

Spero che tu trovi questo utile. Paul

+0

ERRORE: "r.table_schema" non è una variabile nota – slashdottir

14

Per ottenere le stime, vedere Greg Smith's answer.

Per ottenere il conteggio esatto, le altre risposte finora sono afflitte da alcuni problemi, alcuni dei quali gravi (vedere di seguito). Ecco una versione che è si spera migliore:

CREATE FUNCTION rowcount_all(schema_name text default 'public') 
    RETURNS table(table_name text, cnt bigint) as 
$$ 
declare 
table_name text; 
begin 
    for table_name in SELECT c.relname FROM pg_class c 
    JOIN pg_namespace s ON (c.relnamespace=s.oid) 
    WHERE c.relkind = 'r' AND s.nspname=schema_name 
    LOOP 
    RETURN QUERY EXECUTE format('select cast(%L as text),count(*) from %I.%I', 
     table_name, schema_name, table_name); 
    END LOOP; 
end 
$$ language plpgsql; 

Ci vuole un nome di schema come parametro o public se non viene dato alcun parametro.

Per eseguire operazioni su un elenco specifico di schemi o un elenco proveniente da una query senza modificare la funzione, può essere chiamato dall'interno di una query come questa:

WITH rc(schema_name,tbl) AS (
    select s.n,rowcount_all(s.n) from (values ('schema1'),('schema2')) as s(n) 
) 
SELECT schema_name,(tbl).* FROM rc; 

Questo produce un'uscita 3 colonne con lo schema, la tabella e il numero delle righe.

Ora qui sono alcuni problemi nelle altre risposte che questa funzione evita:

  • nomi di tabelle e schema non deve essere iniettato in SQL eseguibile senza essere citata, sia con quote_ident o con l'più moderno format() funziona con la sua stringa di formato %I. In caso contrario, alcuni malintenzionati potrebbero nominare la loro tabella tablename;DROP TABLE other_table che è perfettamente valida come nome di una tabella.

  • Anche senza i problemi di SQL injection e caratteri divertenti, il nome della tabella può esistere nelle varianti a seconda dei casi. Se un tavolo è denominato abcd, un altro abcd, lo SELECT count(*) FROM... deve utilizzare un nome tra virgolettato altrimenti salterà ABCD e conterrà il numero abcd due volte. Il formato %I esegue automaticamente questa operazione.

  • information_schema.tables elenca i tipi di composito personalizzati oltre alle tabelle, anche quando table_type è 'BASE TABLE' (!). Di conseguenza, non possiamo eseguire iterazioni su information_schema.tables, altrimenti rischiamo di avere select count(*) from name_of_composite_type e questo non funzionerebbe. OTOH pg_class where relkind='r' dovrebbe sempre funzionare correttamente.

  • Il tipo di COUNT() è bigint, non int. Possono esistere tabelle con oltre 2,15 miliardi di righe (l'esecuzione di un conteggio (*) su di esse è una cattiva idea, però).

  • Non è necessario creare un tipo permanente per una funzione per restituire un gruppo di risultati con più colonne. RETURNS TABLE(definition...) è un'alternativa migliore.

1

Mi piace Daniel Vérité's answer. Ma quando non è possibile utilizzare un'istruzione CREATE è possibile utilizzare un bash solution o, se sei un utente di Windows, un PowerShell uno:

# You don't need this if you have pgpass.conf 
$env:PGPASSWORD = "userpass" 

# Get table list 
$tables = & 'C:\Program Files\PostgreSQL\9.4\bin\psql.exe' -U user -w -d dbname -At -c "select table_name from information_schema.tables where table_type='BASE TABLE' AND table_schema='schema1'" 

foreach ($table in $tables) { 
    & 'C:\path_to_postresql\bin\psql.exe' -U root -w -d dbname -At -c "select '$table', count(*) from $table" 
} 
6

La hacky, risposta pratica per chi cerca di valutare quali Heroku piano di cui hanno bisogno e non può attendere per il contatore lenta fila di Heroku per rinfrescare:

Fondamentalmente si desidera eseguire \dt in psql, copiare i risultati al vostro editor di testo preferito (che sarà simile a questa:

public | auth_group      | table | axrsosvelhutvw 
public | auth_group_permissions   | table | axrsosvelhutvw 
public | auth_permission    | table | axrsosvelhutvw 
public | auth_user      | table | axrsosvelhutvw 
public | auth_user_groups    | table | axrsosvelhutvw 
public | auth_user_user_permissions  | table | axrsosvelhutvw 
public | background_task    | table | axrsosvelhutvw 
public | django_admin_log    | table | axrsosvelhutvw 
public | django_content_type   | table | axrsosvelhutvw 
public | django_migrations    | table | axrsosvelhutvw 
public | django_session     | table | axrsosvelhutvw 
public | exercises_assignment   | table | axrsosvelhutvw 

), quindi eseguire una ricerca regex e sostituire in questo modo:

^[^|]*\|\s+([^|]*?)\s+\| table \|.*$ 

a:

select '\1', count(*) from \1 union/g 

che produrrà qualcosa di molto simile a questo:

select 'auth_group', count(*) from auth_group union 
select 'auth_group_permissions', count(*) from auth_group_permissions union 
select 'auth_permission', count(*) from auth_permission union 
select 'auth_user', count(*) from auth_user union 
select 'auth_user_groups', count(*) from auth_user_groups union 
select 'auth_user_user_permissions', count(*) from auth_user_user_permissions union 
select 'background_task', count(*) from background_task union 
select 'django_admin_log', count(*) from django_admin_log union 
select 'django_content_type', count(*) from django_content_type union 
select 'django_migrations', count(*) from django_migrations union 
select 'django_session', count(*) from django_session 
; 

(Avrete necessario rimuovere il union e aggiungere la virgola alla fine manualmente)

Ru n it in psql e il gioco è fatto.

  ?column?   | count 
--------------------------------+------- 
auth_group_permissions   |  0 
auth_user_user_permissions  |  0 
django_session     | 1306 
django_content_type   | 17 
auth_user_groups    | 162 
django_admin_log    | 9106 
django_migrations    | 19 
[..] 
12

Ecco una soluzione che non richiede funzioni per ottenere un conteggio accurato per ogni tabella:

select table_schema, 
     table_name, 
     (xpath('/row/cnt/text()', xml_count))[1]::text::int as row_count 
from (
    select table_name, table_schema, 
     query_to_xml(format('select count(*) as cnt from %I.%I', table_schema, table_name), false, true, '') as xml_count 
    from information_schema.tables 
    where table_schema = 'public' --<< change here for the schema you want 
) t 

query_to_xml sarà eseguire la query SQL passato e restituire un XML con il risultato (il conteggio delle righe per quel tavolo). L'esterno xpath() sarà quindi estrarre le informazioni di conteggio da quel xml e convertirlo in un numero

La tabella derivata non è realmente necessario, ma rende il xpath() un po 'più facile da capire - altrimenti l'intero query_to_xml() avrebbe bisogno di essere passato a la funzione xpath().

+0

Molto intelligente. È un peccato che non ci sia 'query_to_jsonb()'. – klin

Problemi correlati