Bagaimana Anda menemukan jumlah baris untuk semua tabel Anda di Postgres

395

Saya mencari cara untuk menemukan jumlah baris untuk semua tabel saya di Postgres. Saya tahu saya bisa melakukan ini satu per satu dengan:

SELECT count(*) FROM table_name;

tapi saya ingin melihat jumlah baris untuk semua tabel dan kemudian memesannya untuk mendapatkan gambaran seberapa besar semua tabel saya.

mmrobin
sumber

Jawaban:

582

Ada tiga cara untuk menghitung seperti ini, masing-masing dengan pengorbanan mereka sendiri.

Jika Anda menginginkan hitungan yang benar, Anda harus menjalankan pernyataan SELECT seperti yang Anda gunakan terhadap setiap tabel. Ini karena PostgreSQL menyimpan informasi visibilitas baris di baris itu sendiri, bukan di tempat lain, sehingga penghitungan akurat hanya dapat relatif terhadap beberapa transaksi. Anda mendapatkan hitungan dari apa yang dilihat transaksi itu pada saat transaksi dijalankan. Anda bisa mengotomatiskan ini untuk dijalankan terhadap setiap tabel dalam database, tetapi Anda mungkin tidak membutuhkan tingkat akurasi atau ingin menunggu selama itu.

Pendekatan kedua mencatat bahwa pengumpul statistik melacak kira-kira berapa banyak baris yang "hidup" (tidak dihapus atau usang oleh pembaruan selanjutnya) setiap saat. Nilai ini dapat dimatikan sedikit di bawah aktivitas berat, tetapi umumnya merupakan perkiraan yang baik:

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

Itu juga dapat menunjukkan kepada Anda berapa banyak baris yang mati, yang merupakan nomor yang menarik untuk dipantau.

Cara ketiga adalah mencatat bahwa perintah ANALYZE sistem, yang dieksekusi oleh proses autovacuum secara teratur pada PostgreSQL 8.3 untuk memperbarui statistik tabel, juga menghitung estimasi baris. Anda bisa ambil yang seperti ini:

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;

Yang mana dari pertanyaan ini yang lebih baik digunakan adalah sulit untuk dikatakan. Biasanya saya membuat keputusan berdasarkan apakah ada informasi yang lebih berguna yang juga ingin saya gunakan di dalam pg_class atau di dalam pg_stat_user_tables. Untuk keperluan penghitungan dasar hanya untuk melihat seberapa besar hal-hal besar secara umum, keduanya harus cukup akurat.

Greg Smith
sumber
2
Demi penyelesaian, tambahkan ini untuk opsi pertama (terima kasih ke @a_horse_with_no_name):with tbl as (SELECT table_schema,table_name FROM information_schema.tables where table_name not like 'pg_%' and table_schema in ('public')) select table_schema, table_name, (xpath('/row/c/text()', query_to_xml(format('select count(*) as c from %I.%I', table_schema, table_name), false, true, '')))[1]::text::int as rows_n from tbl ORDER BY 3 DESC;
estani
1
@ Greg Smith Versi mana yang diperkenalkan n_live_tup? Database Redshift saya tidak memiliki kolom itu. Ini adalah turunan dari Postgres 8.0.2.
Iain Samuel McLean Penatua
1
Permintaan 'pendekatan kedua' (menggunakan pg_stat_user_tables) mengembalikan sebagian besar nol n_live_tupuntuk saya karena ANALYZEbelum pernah dijalankan. Daripada berjalan ANALYZEpada setiap skema / tabel dan menunggu selamanya untuk jawaban, saya pertama-tama memeriksa hasilnya menggunakan 'pendekatan ketiga' dan yang satu (menggunakan pg_class) mengembalikan jumlah yang sangat akurat.
Brian D
@BrianD, adalah mungkin untuk menjalankan analisis di tingkat basis data menggunakan utilitas dianalisisb sebagai "
analysisb
69

Berikut ini adalah solusi yang tidak memerlukan fungsi untuk mendapatkan hitungan yang akurat untuk setiap tabel:

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_xmlakan menjalankan kueri SQL yang diteruskan dan mengembalikan XML dengan hasilnya (jumlah baris untuk tabel itu). Bagian luar xpath()kemudian akan mengekstrak informasi jumlah dari xml itu dan mengubahnya menjadi angka

Tabel turunan tidak benar-benar diperlukan, tetapi membuat xpath()sedikit lebih mudah untuk dipahami - jika tidak, keseluruhan query_to_xml()harus diteruskan ke xpath()fungsi.

seekor kuda tanpa nama
sumber
3
Sangat pintar. Sayang sekali tidak ada query_to_jsonb().
klin
@a_horse_with_no_name, apakah akan memberikan masalah kinerja pada tabel sibuk dan besar saat mengeksekusi?
Spike
@ Spike: masalah kinerja dibandingkan dengan apa? Hambatan kinerja utama berjalan select count(*)di setiap meja.
a_horse_with_no_name
@a_horse_with_no_name, dengan menjalankan fungsi x_path terhadap 100 juta catatan.
Spike
@ Spike: xpath()fungsi ini hanya diterapkan pada satu baris - hasil daricount(*)
a_horse_with_no_name
24

Untuk mendapatkan taksiran, lihat jawaban Greg Smith .

Untuk mendapatkan penghitungan yang tepat, jawaban lain sejauh ini terganggu dengan beberapa masalah, beberapa di antaranya serius (lihat di bawah). Inilah versi yang semoga lebih baik:

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;

Dibutuhkan nama skema sebagai parameter, atau publicjika tidak ada parameter yang diberikan.

Untuk bekerja dengan daftar skema tertentu atau daftar yang berasal dari kueri tanpa mengubah fungsinya, ia dapat dipanggil dari dalam kueri seperti ini:

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;

Ini menghasilkan output 3-kolom dengan skema, tabel dan jumlah baris.

Sekarang ada beberapa masalah di jawaban lain yang fungsi ini hindari:

  • Nama tabel dan skema tidak boleh disuntikkan ke dalam SQL yang dapat dieksekusi tanpa dikutip, baik dengan quote_identatau dengan fungsi yang lebih modern format()dengan %Istring formatnya. Kalau tidak, beberapa orang jahat dapat memberi nama meja mereka tablename;DROP TABLE other_tableyang benar-benar valid sebagai nama tabel.

  • Bahkan tanpa masalah injeksi SQL dan karakter lucu, nama tabel mungkin ada dalam varian yang berbeda menurut kasus. Jika sebuah tabel dinamai ABCDdan yang lain abcd, SELECT count(*) FROM...harus menggunakan nama yang dikutip jika tidak akan melewati ABCDdan menghitung abcddua kali. The %Iformat melakukan ini secara otomatis.

  • information_schema.tablesdaftar tipe komposit khusus selain tabel, bahkan ketika table_type adalah 'BASE TABLE'(!). Sebagai konsekuensinya, kita tidak dapat melanjutkan information_schema.tables, jika tidak kita berisiko memiliki select count(*) from name_of_composite_typedan itu akan gagal. OTOH pg_class where relkind='r'harus selalu berfungsi dengan baik.

  • Jenis COUNT () adalah bigint, tidak int. Tabel dengan lebih dari 2,15 miliar baris mungkin ada (menjalankan penghitungan (*) pada mereka adalah ide yang buruk).

  • Tipe permanen tidak perlu dibuat untuk fungsi untuk mengembalikan resultset dengan beberapa kolom. RETURNS TABLE(definition...)adalah alternatif yang lebih baik.

Daniel Vérité
sumber
18

Jika Anda tidak keberatan data yang berpotensi basi, Anda dapat mengakses statistik yang sama dengan yang digunakan oleh pengoptimal kueri .

Sesuatu seperti:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;
ig0774
sumber
@mlissner: Jika interval autovacuum Anda terlalu panjang atau Anda belum menjalankan manual ANALYZEdi atas meja, statistiknya bisa keluar. Ini adalah pertanyaan tentang pemuatan basis data dan bagaimana basis data dikonfigurasikan (jika statistik diperbarui lebih sering, statistik akan lebih akurat, tetapi ini dapat mengurangi kinerja runtime). Pada akhirnya, satu-satunya cara untuk mendapatkan data yang akurat adalah menjalankan select count(*) from tablesemua tabel.
ig0774
17

Jawaban praktis dan mudah bagi orang yang mencoba mengevaluasi rencana Heroku mana yang mereka butuhkan dan tidak bisa menunggu penghitung baris lambat heroku untuk disegarkan:

Pada dasarnya Anda ingin menjalankan \dtdi psql, menyalin hasil untuk editor teks favorit Anda (akan terlihat seperti ini:

 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

), lalu jalankan pencarian regex dan ganti seperti ini:

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

untuk:

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

yang akan memberi Anda sesuatu yang sangat mirip dengan ini:

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
;

(Anda harus menghapus yang terakhir uniondan menambahkan tanda titik koma di bagian akhir secara manual)

Jalankan psqldan Anda selesai.

            ?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
[..]
Aur Saraf
sumber
Saya suka ide ini
GuilPejon
Di Atom, saya harus regex pencarian dan ganti seperti ini: select '$1', count(*) from $1 union/g
chuck
Juga, pos mengatakan: "Anda harus menghapus serikat pekerja dan menambahkan tanda titik koma di akhir." Ini salah ketik. Anda harus menghapus /g(menyimpan union) dan menambahkan satu titik koma ( ;) di bagian paling akhir. Jangan lupa untuk menghapus yang terakhir unionsebelum titik koma.
buang
1
"Jangan lupa untuk menghapus yang terakhir unionsebelum titik koma" adalah apa yang saya maksud :) Menambahkan kata "terakhir" untuk memperjelas
Aur Saraf
10

Tidak yakin apakah jawaban dalam bash dapat Anda terima, tetapi 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
Stew-au
sumber
7
Pada intinya, ini hanya bermuara select count(*) from table_name;di OP!
Noach Magedman
8

Saya biasanya tidak mengandalkan statistik, terutama di 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;
Yuri Levinsky
sumber
Ini bagus tetapi permintaan pertama juga harus mencakup skema untuk nilai rownum. Jika ada nama yang saling bertentangan di berbagai skema, ini tidak akan berfungsi seperti yang diharapkan. Jadi, bagian dari kueri ini akan terlihat lebih seperti dsql2('select count(*) from livescreen.'||table_name)atau lebih baik dapat diubah menjadi fungsi tersendiri.
jakub-olczyk
6

Saya tidak ingat URL tempat saya mengumpulkan ini. Tetapi harap ini akan membantu Anda:

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; 

Eksekusi select count_em_all();akan membuat Anda menghitung baris semua tabel Anda.

Gnanam
sumber
1
Sebaiknya mengutip nama kolom (seperti quote_ident(t_name.relname)) untuk memastikan dukungan yang tepat untuk nama yang tidak biasa ("nama kolom", misalnya).
gorsky
Untuk menjatuhkannya setelah itu: DROP FUNCTION count_em_all ();
Aalex Gabi
Mendapat kesalahan: pilih count_em_all (); GALAT: kesalahan sintaks pada atau di dekat "grup" LINE 1: SELECT COUNT () AS "count" DARI grup ^ QUERY: SELECT COUNT () SEBAGAI "count" DARI grup CONTEXT: fungsi PL / pgSQL count_em_all () baris 18 pada SELAMANYA Pernyataan EXECUTE
Aalex Gabi
Bagus! Untuk memilih dan mengurutkan - SELECT * FROM count_em_all() as r ORDER BY r.num_rows DESC;
Ken4scholars
6

Dua Langkah Sederhana:
(Catatan: Tidak perlu mengubah apa pun - cukup salin tempel)
1. buat fungsi

create function 
cnt_rows(schema text, tablename text) returns integer
as
$body$
declare
  result integer;
  query varchar;
begin
  query := 'SELECT count(1) FROM ' || schema || '.' || tablename;
  execute query into result;
  return result;
end;
$body$
language plpgsql;

2. Jalankan kueri ini untuk mendapatkan jumlah baris untuk semua tabel

select sum(cnt_rows) as total_no_of_rows from (select 
  cnt_rows(table_schema, table_name)
from information_schema.tables
where 
  table_schema not in ('pg_catalog', 'information_schema') 
  and table_type='BASE TABLE') as subq;

atau

Untuk mendapatkan jumlah baris dengan tab

select
  table_schema,
  table_name, 
  cnt_rows(table_schema, table_name)
from information_schema.tables
where 
  table_schema not in ('pg_catalog', 'information_schema') 
  and table_type='BASE TABLE'
order by 3 desc;
Raju Sah
sumber
5

Saya membuat variasi kecil untuk memasukkan semua tabel, juga untuk tabel non-publik.

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; 

gunakan select count_em_all();untuk menyebutnya.

Semoga Anda menemukan ini berguna. Paul

Paul
sumber
GALAT: "r.table_schema" bukan variabel yang dikenal
slashdottir
2

Ini berhasil untuk saya

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

Pradeep Maurya
sumber
1

Saya suka jawaban Daniel Vérité . Tetapi ketika Anda tidak bisa menggunakan pernyataan CREATE, Anda bisa menggunakan solusi bash atau, jika Anda pengguna windows, yang PowerShell:

# 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"
}
CFreitas
sumber
0

Saya ingin total dari semua tabel + daftar tabel dengan jumlah mereka. Sedikit seperti bagan kinerja tempat sebagian besar waktu dihabiskan

WITH results AS ( 
  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'
     GROUP BY schemaname, relname, reltuples
)

SELECT * FROM results
UNION
SELECT 'all' AS schemaname, 'all' AS relname, SUM(reltuples) AS "reltuples" FROM results

ORDER BY reltuples DESC

Anda tentu saja dapat menempatkan LIMITklausa pada hasil dalam versi ini juga sehingga Anda mendapatkan npelanggar terbesar serta total.

Satu hal yang harus diperhatikan tentang ini adalah Anda perlu membiarkannya untuk sementara waktu setelah impor massal. Saya menguji ini dengan hanya menambahkan 5.000 baris ke database di beberapa tabel menggunakan data impor nyata. Ini menunjukkan 1800 catatan selama sekitar satu menit (mungkin jendela yang dapat dikonfigurasi)

Ini didasarkan dari https://stackoverflow.com/a/2611745/1548557 kerja, jadi terima kasih dan pengakuan itu untuk permintaan untuk digunakan dalam CTE

MrMesees
sumber