Explorar una base: qué tablas hay y cuántas filas tienen
1. Qué es
Cuando te dan acceso a una base que no conoces, la primera pregunta nunca es una consulta de negocio. Es:
¿Qué hay aquí dentro, y dónde está el volumen?
La respuesta ingenua es abrir tabla por tabla y hacer COUNT(*). Funciona, pero lee los datos: en una tabla grande tarda, y en un motor que factura por bytes escaneados, cuesta dinero.
La respuesta buena es otra: no preguntar a los datos, preguntar al catálogo. Toda base lleva unas tablas internas donde apunta qué objetos existe y, con suerte, cuántas filas tiene cada uno. Consultar eso es instantáneo y no toca los datos.
El detalle que hay que entender antes de nada:
Ese número puede ser una estimación o un conteo exacto, y depende del motor. No es un matiz cosmético — cambia cómo interpretas el resultado.
2. PostgreSQL
Postgres mantiene estadísticas de actividad por tabla en la vista pg_stat_user_tables. Ahí vive n_live_tup: el número de filas vivas. Son tres consultas en escalera.
2.1 Qué esquemas hay. Antes de filtrar hay que saber por dónde filtrar. public es solo el esquema por defecto — en bases reales de trabajo casi nunca es el único (ventas, staging, raw, analytics…).
SELECT DISTINCT schemaname
FROM pg_stat_user_tables;
2.2 El resumen completo, sin filtrar y con el esquema como una columna más:
SELECT schemaname AS esquema, relname AS tabla, n_live_tup AS filas
FROM pg_stat_user_tables
ORDER BY schemaname, n_live_tup DESC;
2.3 El resumen de un esquema concreto, que es lo habitual:
SELECT relname AS tabla, n_live_tup AS filas
FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY n_live_tup DESC;
En ese WHERE va el nombre de tu esquema, no literalmente public. Es el error más fácil de cometer al copiar la consulta: se ejecuta sin dar error y devuelve cero filas, que parece "esta base está vacía" cuando en realidad es "estás mirando el esquema equivocado".
Nota: pg_stat_user_tables ya excluye los catálogos del sistema, así que el WHERE solo cambia algo si tienes varios esquemas propios. Como filtro explícito está bien porque deja clara la intención.
3. BigQuery
Aquí el paralelo es directo, pero con un giro: el dataset va en la ruta, no en un WHERE. Es la misma idea que el esquema de Postgres, resuelta de otra forma — y es obligatorio, no puedes omitirlo.
Detalle de vocabulario: BigQuery llama SCHEMATA a sus datasets dentro de INFORMATION_SCHEMA. El concepto es el mismo, solo cambia el nombre comercial.
3.1 Qué datasets hay:
SELECT schema_name AS dataset
FROM `mi-proyecto.INFORMATION_SCHEMA.SCHEMATA`;
3.2 El resumen de un dataset:
SELECT table_id AS tabla, row_count AS filas
FROM `mi-proyecto.mi_dataset.__TABLES__`
WHERE type = 1
ORDER BY row_count DESC;
__TABLES__ es una tabla oculta que existe en todo dataset, sin configurar nada. Devuelve además size_bytes, creation_time y last_modified_time.
El filtro type no es opcional en la práctica: 1 son tablas y 2 son vistas. Sin él, las vistas aparecen en el resumen con filas = 0 y ensucian la lectura — parecen tablas vacías cuando no lo son.
4. Por qué unos estiman y otros cuentan exacto
Esta es la parte que explica todo lo demás.
| PostgreSQL | BigQuery | |
|---|---|---|
| Naturaleza del número | Estimación | Exacto |
| De dónde sale | Estadísticas de ANALYZE / autovacuum |
Metadatos de almacenamiento |
| Cuándo se actualiza | Cuando pasa el autovacuum | Continuamente |
Postgres está diseñado para escrituras concurrentes constantes. Mantener un contador exacto de filas obligaría a serializar todas las transacciones sobre ese contador, y eso mataría el rendimiento. Así que Postgres no lo mantiene: guarda la última estimación conocida y la refresca cuando pasa ANALYZE.
BigQuery no tiene ese problema: no hay transacciones concurrentes fila a fila, y necesita saber cuántas filas y cuántos bytes tiene cada tabla porque de eso depende cómo administra y factura el almacenamiento. El conteo exacto le sale gratis porque ya lo lleva por otro motivo.
La regla general: un motor sabe cuántas filas tiene cuando es dueño de su almacenamiento y ese dato le sirve para otra cosa. Cuando solo le sirve para responderte a ti, se conforma con estimarlo.
5. El mismo mapa en otros motores
La estructura se repite en todos: hay una vista de catálogo, una columna con el recuento, y una respuesta a "¿es exacto?".
| Motor | Vista / catálogo | Columna de filas | ¿Exacto? | Comprobado |
|---|---|---|---|---|
| PostgreSQL | pg_stat_user_tables (o pg_class.reltuples) |
n_live_tup |
Estimado | ✅ |
| BigQuery | <dataset>.__TABLES__ |
row_count |
Exacto | ✅ |
| MySQL / MariaDB | information_schema.tables |
table_rows |
Estimado (InnoDB) | — |
| SQL Server | sys.partitions / sys.dm_db_partition_stats |
rows / row_count |
Casi exacto | — |
| Oracle | user_tables / all_tables |
num_rows |
Estimado (última estadística) | — |
| Snowflake | information_schema.tables |
row_count |
Exacto | — |
| Redshift | svv_table_info |
tbl_rows |
Estimado | — |
| DuckDB | duckdb_tables() |
estimated_size |
Estimado | — |
Las dos filas con ✅ están ejecutadas contra bases reales. Las demás vienen de documentación y no las he verificado — sirven como punto de partida para buscar, no como consulta lista para copiar.
Un aviso al traducir la consulta entre motores: information_schema.tables tiene el recuento de filas en MySQL y Snowflake, pero no en PostgreSQL ni en BigQuery. Es la trampa más común, porque information_schema es el estándar SQL y uno asume que se comporta igual en todas partes.
6. Gotchas
6.1 En Postgres, filas = 0 puede significar "todavía no lo sé". n_live_tup depende de que haya pasado ANALYZE o el autovacuum. En una base recién restaurada o recién cargada puede mostrar 0 en tablas que sí tienen datos, simplemente porque nadie ha recolectado estadísticas aún.
Si el resumen sale sospechosamente vacío:
ANALYZE;
y vuelve a consultar. Es la primera comprobación antes de asumir que la base está vacía.
6.2 En BigQuery, TABLE_STORAGE no está disponible por defecto. La documentación menciona INFORMATION_SCHEMA.TABLE_STORAGE con su columna total_rows, y es tentador usarla porque suena más oficial que __TABLES__. No funciona de fábrica. Es telemetría que hay que activar explícitamente por proyecto y región:
ALTER PROJECT `mi-proyecto`
SET OPTIONS (`region-us.enable_info_schema_storage` = TRUE)
Requiere el permiso bigquery.config.update (incluido en bigquery.admin) y, una vez activada, tarda alrededor de un día en poblarse con el histórico. Para un simple resumen de tablas es desproporcionado: __TABLES__ funciona ya.
8. Referencia oficial
- PostgreSQL — The Statistics Collector:
pg_stat_all_tables - PostgreSQL —
pg_class - BigQuery —
INFORMATION_SCHEMA.SCHEMATA - BigQuery —
INFORMATION_SCHEMA.TABLE_STORAGE