¿Cómo encuentras el recuento de filas para todas tus tablas en Postgres?
Estoy buscando una manera de encontrar el recuento de filas de todas mis tablas en Postgres. Sé que puedo hacer esto una tabla a la vez con:
SELECT count(*) FROM table_name;
pero me gustaría ver el recuento de filas de todas las tablas y luego ordenarlas para tener una idea del tamaño de todas mis tablas.
Hay tres formas de realizar este tipo de recuento, cada una con sus propias compensaciones.
Si desea un recuento verdadero, debe ejecutar la instrucción SELECT como la que utilizó en cada tabla. Esto se debe a que PostgreSQL mantiene la información de visibilidad de las filas en la fila misma, no en ningún otro lugar, por lo que cualquier recuento preciso solo puede ser relativo a alguna transacción. Obtendrá un recuento de lo que ve esa transacción en el momento en que se ejecuta. Puede automatizar esto para ejecutarlo en todas las tablas de la base de datos, pero probablemente no necesite ese nivel de precisión ni quiera esperar tanto.
WITH tbl AS
(SELECT table_schema,
TABLE_NAME
FROM information_schema.tables
WHERE TABLE_NAME not like 'pg_%'
AND table_schema in ('public'))
SELECT table_schema,
TABLE_NAME,
(xpath('/row/c/text()', query_to_xml(format('select count(*) as c from %I.%I', table_schema, TABLE_NAME), FALSE, TRUE, '')))[1]::text::int AS rows_n
FROM tbl
ORDER BY rows_n DESC;
El segundo enfoque señala que el recopilador de estadísticas rastrea aproximadamente cuántas filas están "activas" (no eliminadas ni obsoletas por actualizaciones posteriores) en cualquier momento. Este valor puede variar un poco en condiciones de actividad intensa, pero en general es una buena estimación:
SELECT schemaname,relname,n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
Eso también puede mostrarle cuántas filas están muertas, lo que en sí mismo es un número interesante de monitorear.
La tercera forma es tener en cuenta que el comando ANALYZE del sistema, que el proceso de autovacuum ejecuta regularmente a partir de PostgreSQL 8.3 para actualizar las estadísticas de la tabla, también calcula una estimación de fila. Puedes agarrarlo así:
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;
Es difícil decir cuál de estas consultas es mejor utilizar. Normalmente tomo esa decisión en función de si hay más información útil que también quiero usar dentro de pg_class o dentro de pg_stat_user_tables. Para propósitos básicos de conteo, solo para ver qué tan grandes son las cosas en general, cualquiera de los dos debería ser lo suficientemente preciso.
Aquí hay una solución que no requiere funciones para obtener un recuento preciso para cada tabla:
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
ejecutará la consulta SQL pasada y devolverá un XML con el resultado (el recuento de filas para esa tabla). Luego, el externo xpath()
extraerá la información del recuento de ese xml y la convertirá en un número.
La tabla derivada no es realmente necesaria, pero hace que sea xpath()
un poco más fácil de entender; de lo contrario, query_to_xml()
sería necesario pasar el conjunto a la xpath()
función.
Para obtener estimaciones, consulte la respuesta de Greg Smith .
Para obtener recuentos exactos, las otras respuestas hasta ahora están plagadas de algunos problemas, algunos de ellos graves (ver más abajo). Aquí hay una versión que, con suerte, será mejor:
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;
Toma un nombre de esquema como parámetro, o public
si no se proporciona ningún parámetro.
Para trabajar con una lista específica de esquemas o una lista proveniente de una consulta sin modificar la función, se puede llamar desde una consulta como esta:
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;
Esto produce una salida de 3 columnas con el esquema, la tabla y el recuento de filas.
Ahora aquí hay algunos problemas en las otras respuestas que esta función evita:
Los nombres de tablas y esquemas no deben insertarse en SQL ejecutable sin estar entre comillas, ya sea con la función
quote_ident
más moderna o con su cadena de formato. De lo contrario, alguna persona malintencionada puede nombrar su tabla , lo cual es perfectamente válido como nombre de tabla.format()
%I
tablename;DROP TABLE other_table
Incluso sin la inyección SQL y los problemas de caracteres divertidos, el nombre de la tabla puede existir en variantes que difieren según el caso. Si una tabla tiene un nombre
ABCD
y otraabcd
,SELECT count(*) FROM...
debe usar un nombre entre comillas, de lo contrario se saltaráABCD
y contaráabcd
dos veces. El%I
formato of hace esto automáticamente.Con versiones anteriores de Postgres,
information_schema.tables
enumera tipos compuestos personalizados además de tablas, incluso cuando table_type es'BASE TABLE'
. Como consecuencia, no podemos iterarinformation_schema.tables
, de lo contrario corremos el riesgo de tenerloselect count(*) from name_of_composite_type
y eso fallaría. OTOHpg_class where relkind='r'
siempre debería funcionar bien.El tipo de COUNT() es
bigint
, noint
. Es posible que existan tablas con más de 2,150 millones de filas (aunque realizar un recuento (*) en ellas es una mala idea).No es necesario crear un tipo permanente para que una función devuelva un conjunto de resultados con varias columnas.
RETURNS TABLE(definition...)
es una mejor alternativa.
La respuesta práctica y hacky para las personas que intentan evaluar qué plan de Heroku necesitan y no pueden esperar a que se actualice el lento contador de filas de Heroku:
Básicamente, desea ejecutar \dt
, psql
copiar los resultados a su editor de texto favorito (se verá así:
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
), luego ejecute una búsqueda de expresiones regulares y reemplace así:
^[^|]*\|\s+([^|]*?)\s+\| table \|.*$
a:
select '\1', count(*) from \1 union/g
lo que te dará algo muy similar a esto:
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
;
(Deberás eliminar el último union
y agregar el punto y coma al final manualmente)
Ejecútelo psql
y listo.
?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
[..]