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.
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.
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()
.
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.
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;
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
[..]
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
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;
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.
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
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;
Cela a fonctionné pour moi
SELECT nom_fichier, nom_nom, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC;
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"
}