PostgreSQL: Liczba rekordów w każdej tabeli

16-mar-2022

Jeden z ostatnich kroków migracji danych, to sprawdzenie, czy migracja „niczego nie zgubiła”. Potencjalnie można się np. spodziewać, że tuż po zakończeniu migracji, jakiś zbłąkany użytkownik dopisał swoje rekordy do źródłowej bazy danych, a my tego rekordu nie przenieśliśmy. Jeden z „topornych” sposobów sprawdzenia, czy żaden rekord nie zostanie utracony, jest po prostu policzenie rekordów w źródłowej i docelowej bazie danych.

Najpierw wypadałoby ustalić, jakie mamy schematy w bazie. Robimy to głównie po to, żeby wykluczyć z raportu schematy systemowe, względnie inne, które mają być z jakiegoś powodu ominięte. psql pozwala na uruchomienie skróconego polecenia \dn, ale ta właśnie pomija schematy systemowe. Dlatego proponuję:

SELECT schema_name FROM information_schema.schemata;

Oto możliwy wynik:

 schema_name 
--------------------
 public
 information_schema
 pg_catalog
 pg_toast_temp_1
 pg_temp_1
 pg_toast

Znając schematy bazy danych, pora na sztuczkę. Poniższe zapytanie automatycznie zbierze ze wszystkich tabel z wybranych schematów liczbę rekordów. Ponieważ zapytanie jest uruchamiane na faktycznych danych (a nie np. ze statystyk), jego wykonanie może zająć chwilę czasu. Zapytanie jest sprytne, bo korzysta z możliwości wykonania XML w tym zapytaniu. Poniekąd więc dla każdego rekordu zwracanego z tabel systemowych uruchomi się jeszcze jedno zapytanie XML, zliczające liczbę rekordów:

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 NOT LIKE 'pg_%' and table_schema != 'information_schema'
) t

U mnie zwrcony wynik wygląda mniej więcej tak:

 table_schema | table_name         | row_count 
--------------+--------------------+-----------
 public       | pg_stat_statements | 0
 public       | towns4             | 1000000
 public       | pg_buffercache     | 262144
 public       | towns5             | 1000000
 public       | towns              | 1000000
 public       | towns2             | 1000000
 public       | towns3             | 1000000
 public       | towns6             | 1000000
 public       | towns7             | 1000000
 public       | towns8             | 1000000
 public       | towns9             | 1000000
 public       | towns0             | 1000000
 public       | large_test         | 20000000
 public       | a_table            | 1013
(14 rows)

Powyższe zapytanie można by uruchomić na obu bazach danych i po prostu porównać uzyskane wyniki – czy to excelu, czy pythonie, czy palcem na ekranie – obojętnie.

Istnieją też inne propozycje rozwiązania tego problemu, ale niekoniecznie zliczające rekordy co do jednego, np. zapytanie:

SELECT relname, 
       n_tup_ins - n_tup_del as rowcount 
FROM pg_stat_all_tables 
WHERE schemaname NOT LIKE 'pg_%' and schemaname != 'information_schema';

zwraca:

 relname    | rowcount 
------------+----------
 towns      | 1044898
 towns5     | 1000000
 towns6     | 1000000
 a_table    | 1016
 towns4     | 1000000
 large_test | 20000000
 towns7     | 1000000
 towns2     | 1000000
 towns0     | 1000000
 towns9     | 1000000
 towns8     | 1000000
 towns3     | 1000000
(12 rows)

ale już dla tabeli a_table, widać różnice… Jeśli więc, chcesz mieć dokładne wyniki, to musisz liczyć wszystko. Z pomocą powyższych zapytań będzie to i tak rozwiązanie automatyczne, tylko może z punktu widzenia PostgreSQL, jakby trochę na piechotę.

Komentarze są wyłączone

Autor: Rafał Kraik