2010-04-07 14 views
273

Poszukuję sposobu na znalezienie liczby wierszy dla wszystkich moich tabel w Postgresie. Wiem, że można to zrobić jedną tabelę w czasie z:Jak znaleźć liczbę wierszy dla wszystkich tabel w Postgresie

SELECT count(*) FROM table_name; 

ale chciałbym zobaczyć liczbę wierszy dla wszystkich tabel, a następnie zamówienie przez to, aby zorientować się, jak duże są wszystkie moje tabele .

Odpowiedz

387

Są trzy sposoby, aby tego rodzaju liczyć, każdy z własnym kompromisów.

Jeśli chcesz mieć prawdziwą liczbę, musisz wykonać instrukcję SELECT, taką jak ta użyta w każdej tabeli. Dzieje się tak dlatego, że PostgreSQL zachowuje informacje o widoczności wiersza w samym wierszu, a nie w żadnym innym miejscu, więc dokładna liczba może być zależna tylko od transakcji. Dostajesz liczbę tego, co ta transakcja widzi w momencie jej wykonania. Możesz zautomatyzować to działanie w stosunku do każdej tabeli w bazie danych, ale prawdopodobnie nie potrzebujesz takiego poziomu dokładności lub chcesz tak długo czekać.

Drugie podejście zauważa, że ​​kolektor statystyk śledzi w przybliżeniu, ile wierszy jest "na żywo" (nie usuniętych lub nieaktualnych w późniejszych aktualizacjach) w dowolnym momencie. Wartość ta może być wyłączona przez trochę pod ciężkim aktywności, ale jest na ogół dobre oszacowanie:

SELECT schemaname,relname,n_live_tup 
    FROM pg_stat_user_tables 
    ORDER BY n_live_tup DESC; 

To może również pokazać, jak wiele wierszy są martwe, który jest sam w sobie ciekawy numer monitorować.

Trzeci sposób polega na tym, że polecenie ANALYZE systemu, które jest wykonywane przez proces autovacuum regularnie od PostgreSQL 8.3 w celu aktualizacji statystyk tabeli, również oblicza oszacowanie rzędu. Możesz go pobrać w następujący sposób:

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; 

Które z tych zapytań jest lepsze w użyciu, trudno powiedzieć. Zwykle podejmuję tę decyzję w oparciu o to, czy jest więcej przydatnych informacji, które również chcę użyć w klasie pg_class lub wewnątrz pg_stat_user_tables. W przypadku podstawowych obliczeń, aby zobaczyć, jak duże rzeczy są w ogóle, albo powinny być wystarczająco dokładne.

13

Jeśli nie masz nic przeciwko potencjalnie nieaktualnym danym, możesz access the same statistics used by the query optimizer.

Coś jak:

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

Próbowałem tego używać, ale dane są dość nieaktualne. – mlissner

+0

@mlissner: Jeśli twój automatyczny interwał jest zbyt długi lub nie uruchomiłeś instrukcji "ANALIZA" na stole, statystyki mogą odejść. Jest to kwestia obciążenia bazy danych i konfiguracji bazy danych (jeśli statystyki są aktualizowane częściej, statystyki będą dokładniejsze, ale może to zmniejszyć wydajność środowiska wykonawczego). Ostatecznie, jedynym sposobem uzyskania dokładnych danych jest uruchomienie 'select count (*) from table' dla wszystkich tabel. – ig0774

+4

właśnie wypróbował to i NIE jest właściwą odpowiedzią. –

4

Nie pamiętam adresu URL skąd go zebrałem. Jednak nadzieję, że to powinno pomóc:

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; 

Wykonywanie select count_em_all(); powinno Ci row liczbę wszystkich stołach.

+1

Dobrze jest podać nazwy kolumn (np. 'Quote_ident (t_name.relname)'), aby zapewnić właściwą obsługę niestandardowych nazw (na przykład "nazwa kolumny") . – gorsky

+0

Aby upuścić go później: DROP FUNCTION count_em_all(); –

+0

Wystąpił błąd: wybierz count_em_all(); BŁĄD: błąd składniowy w lub w pobliżu "grupy" LINIA 1: WYBIERZ LICZBĘ() JAK "liczyć" Z grupy^ZAKOŃCZENIE: WYBIERZ COUNT() AS "zlicz" "Z grupy KONTEKST: Funkcja PL/pgSQL count_em_all() linia 18 w FOR ponad WYKONANIE oświadczenie –

8

Nie wiem, czy odpowiedź w bash jest akceptowalna dla ciebie, ale 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

W swojej istocie, to po prostu sprowadza się do tego samego 'select count (*) from table_name;' w OP! –

4

ja zwykle nie opierają się na statystyce, zwłaszcza w 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

Zrobiłem małą zmienność obejmować wszystkie tabele, również dla tabel niepublicznych.

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; 

użyć select count_em_all();, aby go wywołać.

Mam nadzieję, że okaże się to przydatne. Paul

+0

BŁĄD: "r.table_schema" nie jest znaną zmienną – slashdottir

14

Aby uzyskać prognozy, zobacz Greg Smith's answer.

Aby uzyskać dokładne wyniki, pozostałe odpowiedzi są nękane niektórymi problemami, niektóre z nich są poważne (patrz poniżej). Oto wersja to mam nadzieję, że lepiej:

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; 

Zajmuje nazwę schematu jako parametr, lub public jeśli parametr nie jest podany.

Aby pracować z określonej listy schematów lub listy pochodzące z kwerendy bez modyfikowania funkcji, może być wywołana z ciągu kwerendy tak:

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; 

To powoduje wyjście 3-kolumn Schemat, tabela i wiersze się liczą.

Teraz tutaj są pewne problemy w innych odpowiedzi, że funkcja ta pozwala uniknąć:

  • Nazwy tabel i schematów nie należy wstrzykiwać w wykonywalnego SQL bez notowane, albo z quote_ident lub bardziej nowoczesny format() funkcja ze swoim ciągiem formatu %I. W przeciwnym razie pewna złośliwa osoba może nazwać swoją tabelę, która jest całkowicie poprawna jako nazwa tabeli.

  • Nawet bez problemów z iniekcją SQL i zabawnymi postaciami nazwa tabeli może występować w wariantach różniących się wielkością liter. Jeśli tabela ma nazwę ABCD, a inna abcd, musi używać nazwy cytowanej, w przeciwnym razie pominie numer ABCD i policzy dwa razy abcd. %I formatu robi to automatycznie.

  • information_schema.tables zawiera listę niestandardowych typów złożonych oprócz tabel, nawet jeśli typ tabeli to 'BASE TABLE' (!). W związku z tym nie możemy iterować na information_schema.tables, w przeciwnym razie ryzykujemy, mając select count(*) from name_of_composite_type i to się nie powiedzie. OTOH pg_class where relkind='r' powinien zawsze działać poprawnie.

  • Typ COUNT() to bigint, a nie int. Tabele zawierające więcej niż 2,15 miliarda wierszy mogą istnieć (jednak uruchomienie zliczania (*) to zły pomysł).

  • Typu stałego nie trzeba tworzyć dla funkcji zwracającej zestaw wyników z kilkoma kolumnami. RETURNS TABLE(definition...) jest lepszą alternatywą.

1

Ja lubię Daniel Vérité za answer. Ale kiedy nie można użyć instrukcji CREATE można albo użyć bash solution lub, jeśli jesteś użytkownikiem Windows, PowerShell One:

# 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

hacky, praktyczna odpowiedź dla ludzi, którzy próbują ocenić, które Heroku planują oni potrzebują i nie może czekać na powolny licznik rząd Heroku do odświeżenia:

Zasadniczo chcesz uruchomić \dt w psql, skopiuj wyniki do swojego ulubionego edytora tekstu (będzie wyglądać następująco:

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 następnie uruchomić wyszukiwanie regex i zastąpić tak:

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

do:

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

która przyniesie ci coś bardzo podobnego do tego:

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 
; 

(Będziesz musisz usunąć union i ręcznie dodać średnik na końcu)

Ru n to w psql i gotowe.

  ?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

Oto rozwiązanie, które nie wymaga funkcji, aby uzyskać dokładną liczbę dla każdej tabeli:

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 potrwa przekazany zapytanie SQL i powrócić XML z wynikiem (liczbę wierszy dla tego stołu). Zewnętrzna xpath() następnie wydobyć informacje liczyć z tym xml i przekonwertować go na liczbę

tabela pochodzi tak naprawdę nie jest to konieczne, ale sprawia, że ​​xpath() nieco łatwiejsze do zrozumienia - w przeciwnym razie cała query_to_xml() musiałyby być przekazywane do funkcja xpath().

+0

Bardzo sprytny. Szkoda, że ​​nie ma 'query_to_jsonb()'. – klin

Powiązane problemy