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ę.