Manera rápida de descubrir el recuento de filas de una tabla en PostgreSQL
Necesito saber el número de filas de una tabla para calcular un porcentaje. Si el recuento total es mayor que alguna constante predefinida, usaré el valor constante. De lo contrario, usaré el número real de filas.
Puedo usar SELECT count(*) FROM table
. Pero si mi valor constante es 500.000 y tengo 5.000.000.000 de filas en mi tabla, contar todas las filas será una pérdida de tiempo.
¿Es posible dejar de contar tan pronto como se supere mi valor constante?
Necesito el número exacto de filas solo siempre que esté por debajo del límite dado. De lo contrario, si el recuento está por encima del límite, uso el valor límite y quiero la respuesta lo más rápido posible.
Algo como esto:
SELECT text,count(*), percentual_calculus()
FROM token
GROUP BY text
ORDER BY count DESC;
Se sabe que contar filas en tablas grandes es lento en PostgreSQL. El modelo MVCC requiere un recuento completo de filas activas para obtener un número preciso. Existen soluciones para acelerar esto drásticamente si el recuento no tiene que ser exacto como parece ser en su caso.
(Recuerde que incluso un recuento "exacto" está potencialmente muerto al llegar bajo una carga de escritura simultánea).
recuento exacto
Lento para mesas grandes.
Con operaciones de escritura simultáneas, es posible que quede obsoleto en el momento en que lo obtenga.
SELECT count(*) AS exact_count FROM myschema.mytable;
Estimar
Extremadamente rápido :
SELECT reltuples AS estimate FROM pg_class where relname = 'mytable';
Normalmente, la estimación es muy cercana. Qué tan cerca depende de si ANALYZE
se VACUUM
ejecutan lo suficiente, donde "suficiente" se define por el nivel de actividad de escritura en su tabla.
Estimación más segura
Lo anterior ignora la posibilidad de que haya varias tablas con el mismo nombre en una base de datos, en diferentes esquemas. Para dar cuenta de eso:
SELECT c.reltuples::bigint AS estimate
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable'
AND n.nspname = 'myschema';
El elenco bigint
formatea bien el real
número, especialmente para conteos grandes.
Mejor estimación
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
Más rápido, más sencillo, más seguro, más elegante. Consulte el manual sobre tipos de identificadores de objetos .
Reemplace 'myschema.mytable'::regclass
con to_regclass('myschema.mytable')
en Postgres 9.4+ para no obtener nada en lugar de una excepción para nombres de tablas no válidos. Ver:
- Cómo comprobar si existe una tabla en un esquema determinado
Mejor estimación aún (por muy poco costo adicional)
Esto no funciona para tablas particionadas porque relpages
siempre es -1 para la tabla principal (aunque reltuples
contiene una estimación real que cubre todas las particiones), probado en Postgres 14.
En su lugar, debe sumar estimaciones para todas las particiones.
Podemos hacer lo que hace el planificador de Postgres. Citando los ejemplos de estimación de filas en el manual :
Estos números están actualizados hasta el último
VACUUM
oANALYZE
en la tabla. Luego, el planificador recupera el número actual de páginas de la tabla (esta es una operación económica que no requiere un escaneo de la tabla). Si es diferente,relpages
sereltuples
escala en consecuencia para llegar a una estimación actual del número de filas.
Postgres usa estimate_rel_size
definido en src/backend/utils/adt/plancat.c
, que también cubre el caso de esquina sin datos pg_class
porque la relación nunca fue eliminada. Podemos hacer algo similar en SQL:
forma mínima
SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM pg_class
WHERE oid = 'mytable'::regclass; -- your table here
Seguro y explícito
SELECT (CASE WHEN c.reltuples < 0 THEN NULL -- never vacuumed
WHEN c.relpages = 0 THEN float8 '0' -- empty table
ELSE c.reltuples / c.relpages END
* (pg_catalog.pg_relation_size(c.oid)
/ pg_catalog.current_setting('block_size')::int)
)::bigint
FROM pg_catalog.pg_class c
WHERE c.oid = 'myschema.mytable'::regclass; -- schema-qualified table here
No rompe con mesas vacías y mesas que nunca has visto VACUUM
o ANALYZE
. El manual sobrepg_class
:
Si la tabla aún no se ha aspirado ni analizado,
reltuples
contiene-1
una indicación de que se desconoce el recuento de filas.
Si esta consulta devuelve NULL
, ejecute ANALYZE
o VACUUM
para la tabla y repita. (Como alternativa, puede estimar el ancho de fila basándose en los tipos de columnas como lo hace Postgres, pero eso es tedioso y propenso a errores).
Si esta consulta devuelve 0
, la tabla parece estar vacía. Pero me ANALYZE
aseguraría. (Y tal vez verifique su autovacuum
configuración).
Normalmente, block_size
es 8192. current_setting('block_size')::int
Cubre raras excepciones.
Las calificaciones de tablas y esquemas lo hacen inmune a cualquier search_path
alcance.
De cualquier manera, la consulta siempre toma <0,1 ms para mí.
Más recursos web:
- Preguntas frecuentes sobre Postgres Wiki
- Las páginas wiki de Postgres para estimaciones de recuento y rendimiento de recuento (*)
TABLESAMPLE SYSTEM (n)
en Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Como comentó @a_horse , la cláusula agregada para el SELECT
comando puede ser útil si las estadísticas pg_class
no están lo suficientemente actualizadas por algún motivo. Por ejemplo:
- No
autovacuum
correr. - Inmediatamente después de un gran
INSERT
// .UPDATE
DELETE
TEMPORARY
tablas (que no están cubiertas porautovacuum
).
Esto solo analiza una selección aleatoria de bloques del n % ( 1
en el ejemplo) y cuenta las filas en ella. Una muestra más grande aumenta el costo y reduce el error, usted elige. La precisión depende de más factores:
- Distribución del tamaño de fila. Si un bloque determinado contiene filas más anchas de lo habitual, el recuento es menor de lo habitual, etc.
- Tuplas muertas o
FILLFACTOR
ocupan espacio por bloque. Si se distribuye de manera desigual en la tabla, la estimación puede estar equivocada. - Errores generales de redondeo.
Normalmente, la estimación pg_class
será más rápida y precisa.
Respuesta a pregunta real
Primero, necesito saber el número de filas en esa tabla, si el recuento total es mayor que alguna constante predefinida,
Y si...
... es posible en el momento en que el conteo pase mi valor constante, detendrá el conteo (y no esperará a terminar el conteo para informar que el conteo de filas es mayor).
Sí. Puede utilizar una subconsulta conLIMIT
:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres en realidad deja de contar más allá del límite dado, obtienes un recuento exacto y actual de hasta n filas (500000 en el ejemplo), y n en caso contrario. Sin embargo, no es tan rápido como la estimación de pg_class
.
Hice esto una vez en una aplicación de Postgres ejecutando:
EXPLAIN SELECT * FROM foo;
Luego examina la salida con una expresión regular o una lógica similar. Para un SELECT * simple, la primera línea de salida debería verse así:
Seq Scan on uids (cost=0.00..1.21 rows=8 width=75)
Puede usar el rows=(\d+)
valor como una estimación aproximada del número de filas que se devolverían, luego solo haga lo real SELECT COUNT(*)
si la estimación es, por ejemplo, menos de 1,5 veces su umbral (o cualquier número que considere que tiene sentido para su aplicación).
Dependiendo de la complejidad de su consulta, este número puede volverse cada vez menos preciso. De hecho, en mi aplicación, a medida que añadimos uniones y condiciones complejas, se volvió tan inexacto que era completamente inútil, incluso saber dentro de una potencia de 100 cuántas filas habríamos devuelto, por lo que tuvimos que abandonar esa estrategia.
Pero si su consulta es lo suficientemente simple como para que Pg pueda predecir, dentro de un margen de error razonable, cuántas filas devolverá, puede que funcione para usted.
Referencia tomada de este Blog.
Puede utilizar a continuación para consultar y encontrar el recuento de filas.
Usando pg_class:
SELECT reltuples::bigint AS EstimatedCount
FROM pg_class
WHERE oid = 'public.TableName'::regclass;
Usando pg_stat_user_tables:
SELECT
schemaname
,relname
,n_live_tup AS EstimatedCount
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;