En corto#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| -- Filas exactas de todas las tablas de un esquema, en una sola consulta
select table_schema, table_name,
(xpath('/row/cnt/text()', xml_count))[1]::text::bigint as row_count
from (
select table_schema, table_name,
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' and table_type = 'BASE TABLE'
) t
order by row_count desc;
-- Estimación instantánea (no lee las tablas)
select relname, n_live_tup from pg_stat_user_tables order by n_live_tup desc;
-- Usuario de solo lectura (PostgreSQL 14 o superior)
create user reporting with password '…';
grant pg_read_all_data to reporting;
|
Contar filas#
count(*) exacto sobre cada tabla exige recorrerla entera; en una base de cientos de GB tarda. Lo que casi siempre quieres es la estimación que mantiene el autovacuum, que se actualiza en cada ANALYZE y suele estar a un par de puntos del real:
1
2
3
| select schemaname, relname, n_live_tup, n_dead_tup, last_autoanalyze
from pg_stat_user_tables
order by n_live_tup desc;
|
Cuando necesitas el número exacto de todas las tablas sin escribir un DO en PL/pgSQL, el truco de query_to_xml: ejecuta un count(*) dinámico por tabla, devuelve el resultado como XML y xpath extrae el número. Es SQL plano, funciona desde cualquier cliente, y se puede filtrar por esquema o por nombre. Solo recuerda que corre los conteos en serie: en tablas grandes, paciencia.
Tamaños#
1
2
3
4
5
6
7
8
9
| select pg_size_pretty(pg_database_size(current_database()));
select n.nspname as schema, c.relname as table,
pg_size_pretty(pg_total_relation_size(c.oid)) as total, -- datos + índices + TOAST
pg_size_pretty(pg_relation_size(c.oid)) as data
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where c.relkind = 'r' and n.nspname not in ('pg_catalog', 'information_schema')
order by pg_total_relation_size(c.oid) desc
limit 20;
|
Sesiones#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| -- Quién está conectado y qué hace
select pid, usename, state, client_addr, application_name, left(query, 80) as query
from pg_stat_activity
where datname = current_database();
-- Cancelar una consulta (la sesión sigue)
select pg_cancel_backend(12345);
-- Expulsar a todos menos a ti (antes de un DROP o un restore)
select pg_terminate_backend(pid)
from pg_stat_activity
where datname = 'app_db' and pid <> pg_backend_pid();
-- O, desde PostgreSQL 13, directo:
drop database app_db with (force);
-- Uptime
select date_trunc('second', current_timestamp - pg_postmaster_start_time()) as uptime;
|
Usuario de solo lectura#
En 14 o superior, un rol predefinido lo resuelve para todos los esquemas, incluidas las tablas que se creen mañana:
1
2
| create user reporting with password '…';
grant pg_read_all_data to reporting;
|
En versiones anteriores, o si quieres limitarlo a un esquema, el camino clásico:
1
2
3
4
5
6
7
8
9
| grant connect on database app_db to reporting;
grant usage on schema public to reporting;
grant select on all tables in schema public to reporting;
-- tablas futuras: solo cubre las que cree el rol que ejecuta esto (o FOR ROLE app_owner)
alter default privileges for role app_owner in schema public grant select on tables to reporting;
-- Deshacer
revoke all on all tables in schema public from reporting;
alter default privileges for role app_owner in schema public revoke select on tables from reporting;
|
alter default privileges no toca lo que ya existe y solo aplica a objetos creados por el rol indicado: si otra cuenta crea tablas, reporting no las verá. De ahí el valor de pg_read_all_data.
Contraseñas, timezone y psql#
1
2
3
4
5
| \password app_user -- desde psql, sin dejar la contraseña en el historial
alter user app_user with password '…';
alter database app_db set timezone to 'America/Mexico_City'; -- nuevas conexiones a esa base
alter role reporting set timezone to 'UTC'; -- nuevas sesiones de ese rol
|
1
2
| psql -v ON_ERROR_STOP=1 -f data.sql app_db # un script que se detiene en el primer error
psql -P pager=off app_db # sin paginador (o \pset pager 0 dentro)
|
Y dentro de psql, \x auto (columnas verticales cuando no caben) y \timing.
Mantenimiento mínimo#
1
2
| vacuum (verbose, analyze) orders;
reindex table concurrently orders; -- 12+, sin bloquear escrituras
|
VACUUM FULL reescribe la tabla y la bloquea mientras dura; para recuperar espacio en producción, pg_repack.
Trampas#
n_live_tup es una estimación: recién analizada es fiel; en una tabla con mucho churn puede desviarse hasta el siguiente autoanalyze.pg_read_all_data no salta RLS ni concede DDL; es lectura de tablas, vistas y secuencias, nada más.pg_terminate_backend corta transacciones: la aplicación ve un error y reconecta. Para solo abortar una consulta, pg_cancel_backend.alter role … set timezone aplica a sesiones nuevas; la que ya está abierta sigue igual.quote_ident/format('%I') siempre que construyas SQL con nombres de tablas: un nombre con mayúsculas o guiones rompe la consulta sin ellos.
Ver también#