2010-04-07 14 views

Respuesta

387

Hay tres maneras de obtener este tipo de recuento, cada uno con sus propias ventajas y desventajas.

Si desea un conteo verdadero, debe ejecutar la instrucción SELECT como la que utilizó contra cada tabla. Esto se debe a que PostgreSQL mantiene la información de visibilidad de fila en la fila misma, no en ningún otro lado, por lo que cualquier recuento exacto 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 que se ejecute en todas las tablas de la base de datos, pero probablemente no necesite ese nivel de precisión o desee esperar tanto tiempo.

El segundo enfoque indica que el recopilador de estadísticas rastrea aproximadamente cuántas filas están "activas" (no eliminadas o obsoletas por actualizaciones posteriores) en cualquier momento. Este valor puede ser apagado por un poco por debajo actividad pesada, 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 le puede mostrar cuántas filas están muertos, que es en sí misma un número interesante de controlar.

La tercera forma es observar que el comando ANALIZAR del sistema, que se ejecuta por el proceso de autovacuum regularmente a partir de PostgreSQL 8.3 para actualizar las estadísticas de la tabla, también calcula una estimación de filas. Puede tomar uno como este:

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; 

Cuál de estas consultas es mejor utilizar es difícil de decir. 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 las dos debería ser lo suficientemente precisa.

13

Si no le molestan los datos obsoletos, puede access the same statistics used by the query optimizer.

Algo así como:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables; 
+0

He intentado usar esto, pero los datos son bastante obsoletos. – mlissner

+0

@mlissner: Si su intervalo de autovacuum es demasiado largo o no ha ejecutado un manual 'ANALYZE' en la tabla, las estadísticas pueden desviarse. Es una cuestión de carga de la base de datos y de cómo se configura la base de datos (si las estadísticas se actualizan con mayor frecuencia, las estadísticas serán más precisas, pero podría reducir el rendimiento del tiempo de ejecución). En última instancia, la única forma de obtener datos precisos es ejecutar 'select count (*) from table' para todas las tablas. – ig0774

+4

lo intenté y no es la respuesta correcta. –

4

No recuerdo la URL de donde recogí esto. Pero espero que esto le ayudará a:

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; 

Ejecución select count_em_all(); debe conseguir que el número de filas de todas las tablas.

+1

Es una buena idea citar nombres de columna (como 'quote_ident (t_name.relname)') para garantizar el soporte adecuado para nombres inusuales ("column-name", por ejemplo) . – gorsky

+0

Para soltarlo después: DROP FUNCTION count_em_all(); –

+0

Obtuvo un error: seleccione count_em_all(); ERROR: error de sintaxis en o cerca de "grupo" LINE 1: SELECT COUNT() AS "count" FROM group^QUERY: SELECT COUNT() AS "count" FROM group CONTEXT: Función PL/pgSQL count_em_all() línea 18 en FOR over EJECUTAR la instrucción –

8

No está seguro de si una respuesta en bash es aceptable para usted, pero ... 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 
+6

En esencia, esto se reduce a la misma 'selección de conteo (*) de table_name;' en el OP! –

4

por lo general no se basan en estadísticas, especialmente en 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; 
3

me hizo una pequeña variación para incluir todas las tablas, también para las tablas que no son públicas.

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; 

usa select count_em_all(); para llamarlo.

Espero que te haya sido útil. Paul

+0

ERROR: "r.table_schema" no es una variable conocida – slashdottir

14

Para obtener estimaciones, vea Greg Smith's answer.

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 es de esperar 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; 

Se necesita un nombre de esquema como parámetro o public si no se da ningún parámetro.

Para trabajar con una lista específica de los esquemas o una lista que viene de una consulta sin modificar la función, se puede llamar desde el interior de 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 las filas cuentan.

Ahora aquí hay algunas cuestiones en las otras respuestas que esta función evita:

  • de tablas y esquemas nombres no deben inyectarse en SQL ejecutable sin ser citado, ya sea con quote_ident o con el más moderno format() funcionar con su cadena de formato %I. De lo contrario, una persona malintencionada puede nombrar su tabla tablename;DROP TABLE other_table que es perfectamente válida como nombre de tabla.

  • Incluso sin la inyección de SQL y los problemas de caracteres divertidos, el nombre de la tabla puede existir en variantes que varían según el caso. Si una tabla se llama ABCD y otra abcd, la SELECT count(*) FROM... debe usar un nombre entre comillas, de lo contrario omitirá ABCD y contará abcd dos veces. El formato %I lo hace automáticamente.

  • information_schema.tables enumera los tipos de compuestos personalizados además de las tablas, incluso cuando table_type es 'BASE TABLE' (!). Como consecuencia, no podemos iterar en information_schema.tables, de lo contrario corremos el riesgo de tener select count(*) from name_of_composite_type y que fallaría. OTOH pg_class where relkind='r' siempre debe funcionar bien.

  • El tipo de COUNT() es bigint, no int. Las tablas con más de 2,15 mil millones de filas pueden existir (sin embargo, ejecutar una cuenta (*) 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.

1

Me gusta Daniel Vérité's answer. Pero cuando no se puede utilizar una instrucción CREATE se puede utilizar un bash solution o, si usted es un usuario de Windows, una PowerShell uno:

# 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" 
} 
6

El, hacky respuesta práctica para las personas que tratan de evaluar qué Heroku planean que necesitan y no pueden esperar a lenta contador de filas de heroku refrescar:

Básicamente desea ejecutar \dt en psql, copiar los resultados a su editor de texto favorito (que 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 

), a continuación, ejecutar una búsqueda de expresiones regulares y reemplazar así:

^[^|]*\|\s+([^|]*?)\s+\| table \|.*$ 

a:

select '\1', count(*) from \1 union/g 

que le dió algo muy parecido 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 
; 

(Vas necesita eliminar el union y agregar el punto y coma al final manualmente)

Ru n en 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 
[..] 
12

Aquí es una solución que no requiere funciones para obtener un recuento exacto 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 se ejecute la consulta SQL pasado y devolver un XML con el resultado (el número de filas para esa mesa). El exterior xpath() A continuación, extraer la información de la cuenta de ese XML y convertirlo en un número

la tabla derivada no es realmente necesario, pero hace que el xpath() un poco más fácil de entender - de lo contrario todo el query_to_xml() tendría que ser pasado a la función xpath().

+0

Muy inteligente. Es una pena que no haya 'query_to_jsonb()'. – klin

Cuestiones relacionadas