web-dev-qa-db-fra.com

Comment trouvez-vous le nombre de lignes pour toutes vos tables dans Postgres

Je cherche un moyen de trouver le nombre de lignes pour toutes mes tables dans Postgres. Je sais que je peux faire cette table à la fois avec:

SELECT count(*) FROM table_name;

mais j'aimerais voir le nombre de lignes pour toutes les tables, puis passer en ordre pour avoir une idée de la taille de toutes mes tables.

346
mmrobins

Il y a trois façons d'obtenir ce type de compte, chacune avec ses propres compromis.

Si vous voulez un nombre réel, vous devez exécuter l'instruction SELECT comme celle que vous avez utilisée pour chaque table. Cela est dû au fait que PostgreSQL ™ conserve les informations de visibilité des lignes dans la ligne elle-même, pas ailleurs. Par conséquent, tout nombre précis ne peut être que relatif à une transaction. Vous obtenez un décompte de ce que cette transaction voit au moment où elle s'exécute. Vous pouvez automatiser cette opération pour qu'elle s'exécute sur toutes les tables de la base de données, mais vous n'avez probablement pas besoin de ce niveau de précision ou ne voulez pas attendre aussi longtemps.

La deuxième approche indique que le collecteur de statistiques suit à peu près combien de lignes sont "actives" (non supprimées ou obsolètes par les mises à jour ultérieures) à tout moment. Cette valeur peut être un peu faible sous une activité intense, mais est généralement une bonne estimation:

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

Cela peut aussi vous montrer combien de lignes sont mortes, ce qui en soi est un nombre intéressant à surveiller.

La troisième méthode consiste à noter que la commande système ANALYZE, qui est exécutée régulièrement par le processus autovacuum à partir de PostgreSQL 8.3 pour mettre à jour les statistiques de table, calcule également une estimation de ligne. Vous pouvez le prendre comme ceci:

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;

Il est difficile de dire laquelle de ces questions est préférable d'utiliser. Normalement, je décide en fonction des informations plus utiles que je souhaite également utiliser dans pg_class ou dans pg_stat_user_tables. Aux fins de comptage de base, juste pour voir à quel point les choses sont grandes en général, l'une ou l'autre devrait être assez précise.

507
Greg Smith

Voici une solution qui n'exige pas de fonctions pour obtenir un décompte précis pour chaque table:

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 exécutera la requête SQL transmise et renverra un XML avec le résultat (le nombre de lignes pour cette table). La xpath() extérieure extraira ensuite les informations de comptage de ce XML et les convertira en un nombre

La table dérivée n'est pas vraiment nécessaire, mais rend la xpath() un peu plus facile à comprendre - sinon l'ensemble query_to_xml() aurait besoin d'être passé à la fonction xpath().

43

Pour obtenir des estimations, voir réponse de Greg Smith .

Pour obtenir des chiffres exacts, les autres réponses à ce jour sont entachées de problèmes, dont certains graves (voir ci-dessous). Voici une version qui, espérons-le, est meilleure:

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;

Il prend un nom de schéma en paramètre ou public si aucun paramètre n'est fourni.

Pour utiliser une liste spécifique de schémas ou une liste provenant d'une requête sans modifier la fonction, elle peut être appelée depuis une requête comme celle-ci:

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;

Cela produit une sortie à 3 colonnes avec le schéma, la table et le nombre de lignes.

Maintenant, voici quelques problèmes dans les autres réponses que cette fonction évite:

  • Les noms de table et de schéma ne doivent pas être injectés dans l'exécutable SQL sans les guillemets, ni avec quote_ident ni avec la fonction plus moderne format() avec sa chaîne de format %I. Sinon, une personne mal intentionnée peut nommer sa table tablename;DROP TABLE other_table qui est parfaitement valide en tant que nom de table.

  • Même en l'absence d'injection SQL et de problèmes de caractères amusants, le nom de la table peut exister selon des variantes différentes selon les cas. Si une table est nommée ABCD et une autre abcd, la SELECT count(*) FROM... doit utiliser un nom cité, sinon elle sautera ABCD et comptera abcd deux fois. Le %I du format le fait automatiquement.

  • information_schema.tables répertorie les types composites personnalisés en plus des tables, même lorsque type_table est défini sur 'BASE TABLE' (!). En conséquence, nous ne pouvons pas itérer sur information_schema.tables, sinon nous risquons d’avoir select count(*) from name_of_composite_type et cela échouerait. OTOH pg_class where relkind='r' devrait toujours fonctionner correctement.

  • Le type de COUNT () est bigint et non int. Des tables de plus de 2,15 milliards de lignes peuvent exister (exécuter un décompte (*) dessus est cependant une mauvaise idée).

  • Il n'est pas nécessaire de créer un type permanent pour qu'une fonction renvoie un résultat avec plusieurs colonnes. RETURNS TABLE(definition...) est une meilleure alternative.

22
Daniel Vérité

Si les données potentiellement périmées ne vous dérangent pas, vous pouvez accéder aux mêmes statistiques que l’optimiseur de requêtes .

Quelque chose comme:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;
16
ig0774

Le hacky, réponse pratique pour ceux qui tentent d'évaluer le plan Heroku dont ils ont besoin et qui ne peuvent attendre que le compteur de lignes lentes de heroku se rafraîchit:

En gros, vous voulez exécuter \dt dans psql, copiez les résultats dans votre éditeur de texte préféré (cela ressemblera à ceci:

 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

), lancez une recherche regex et remplacez-la comme ceci:

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

à:

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

ce qui vous donnera quelque chose de très similaire à ceci:

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
;

(Vous devrez supprimer le dernier union et ajouter le point-virgule à la fin manuellement)

Exécutez-le dans psql et vous avez terminé.

            ?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
[..]
13
Aur Saraf

Je ne sais pas si une réponse en bash est acceptable pour vous, mais 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
10
Stew-au

En général, je ne me fie pas aux statistiques, en particulier dans 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;
7
Yuri Levinsky

Je ne me souviens pas de l'URL d'où j'ai recueilli cela. Mais espérons que cela devrait vous aider:

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; 

L'exécution de select count_em_all(); devrait vous donner le nombre de lignes de toutes vos tables.

6
Gnanam

J'ai fait une petite variante pour inclure toutes les tables, également pour les tables non publiques.

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; 

utilisez select count_em_all(); pour l'appeler.

J'espère que vous trouverez cela utile. Paul

5
Paul

Deux étapes simples:
(Remarque: nul besoin de changer quoi que ce soit - il suffit de copier/coller)
1. Créer une fonction

create function 
cnt_rows(schema text, tablename text) returns integer
as
$body$
declare
  result integer;
  query varchar;
begin
  query := 'SELECT count(1) FROM ' || schema || '.' || tablename;
  execute query into result;
  return result;
end;
$body$
language plpgsql;

2. Exécutez cette requête pour obtenir le nombre de lignes pour toutes les tables

select sum(cnt_rows) as total_no_of_rows from (select 
  cnt_rows(table_schema, table_name)
from information_schema.tables
where 
  table_schema not in ('pg_catalog', 'information_schema') 
  and table_type='BASE TABLE') as subq;

o

Pour obtenir le nombre de lignes par table

select
  table_schema,
  table_name, 
  cnt_rows(table_schema, table_name)
from information_schema.tables
where 
  table_schema not in ('pg_catalog', 'information_schema') 
  and table_type='BASE TABLE'
order by 3 desc;
4
Raju Sah

Cela a fonctionné pour moi

SELECT nom_fichier, nom_nom, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC;

1
Pradeep Maurya

J'aime Daniel Vérité réponse . Mais lorsque vous ne pouvez pas utiliser une instruction CREATE, vous pouvez utiliser un solution bash ou, si vous êtes un utilisateur Windows, un PowerShell:

# 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"
}
1
CFreitas