Menghitung baris dalam tabel besar diketahui lambat di PostgreSQL. Model MVCC membutuhkan hitungan penuh baris hidup untuk jumlah yang tepat. Ada solusi untuk mempercepat ini secara dramatis jika hitungannya tidak harus tepat sepertinya ada dalam kasus Anda.
(Ingat bahwa bahkan hitungan "tepat" berpotensi mati pada saat kedatangan!)
Jumlah yang tepat
Lambat untuk tabel besar.
Dengan operasi penulisan bersamaan, mungkin sudah usang saat Anda mendapatkannya.
SELECT count(*) AS exact_count FROM myschema.mytable;
Perkiraan
Sangat cepat :
SELECT reltuples AS estimate FROM pg_class where relname = 'mytable';
Biasanya, perkiraannya sangat dekat. Seberapa dekat, tergantung pada apakah ANALYZE
atau VACUUM
dijalankan cukup - di mana "cukup" ditentukan oleh tingkat aktivitas tulis ke tabel Anda.
Perkiraan lebih aman
Di atas mengabaikan kemungkinan beberapa tabel dengan nama yang sama dalam satu database - dalam skema yang berbeda. Untuk memperhitungkan itu:
SELECT c.reltuples::bigint AS estimate
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'mytable'
AND n.nspname = 'myschema';
Pemeran ke bigint
memformat real
angka dengan baik, terutama untuk jumlah besar.
Perkiraan lebih baik
SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'myschema.mytable'::regclass;
Lebih cepat, lebih sederhana, lebih aman, lebih elegan. Lihat manual tentang Jenis Pengidentifikasi Objek.
Ganti 'myschema.mytable'::regclass
dengan to_regclass('myschema.mytable')
di Postgres 9.4+ untuk tidak mendapatkan apa pun selain pengecualian untuk nama tabel yang tidak valid. Lihat:
- Cara memeriksa apakah ada tabel dalam skema tertentu
Perkiraan yang lebih baik lagi (dengan biaya tambahan yang sangat kecil)
Kita dapat melakukan apa yang dilakukan perencana Postgres. Mengutip Contoh Estimasi Baris dalam manual:
Angka-angka ini berlaku pada
VACUUM
terakhir atauANALYZE
di atas meja. Perencana kemudian mengambil jumlah halaman aktual saat ini dalam tabel (ini adalah operasi yang murah, tidak memerlukan pemindaian tabel). Jika itu berbeda darirelpages
lalureltuples
diskalakan sesuai dengan perkiraan jumlah baris saat ini.
Postgres menggunakan estimate_rel_size
didefinisikan dalam src/backend/utils/adt/plancat.c
, yang juga mencakup kasus sudut tidak ada data di pg_class
karena hubungan itu tidak pernah vakum. Kita dapat melakukan hal serupa di SQL:
Bentuk minimal
SELECT (reltuples / relpages * (pg_relation_size(oid) / 8192))::bigint
FROM pg_class
WHERE oid = 'mytable'::regclass; -- your table here
Aman dan eksplisit
SELECT (CASE WHEN c.reltuples < 0 THEN NULL -- never vacuumed
WHEN c.relpages = 0 THEN float8 '0' -- empty table
ELSE c.reltuples / c.relpages END
* (pg_relation_size(c.oid) / pg_catalog.current_setting('block_size')::int)
)::bigint
FROM pg_class c
WHERE c.oid = 'myschema.mytable'::regclass; -- schema-qualified table here
Tidak pecah dengan tabel kosong dan tabel yang belum pernah melihat VACUUM
atau ANALYZE
. Panduan tentang pg_class
:
Jika tabel belum pernah divakum atau dianalisis,
reltuples
berisi-1
menunjukkan bahwa jumlah baris tidak diketahui.
Jika kueri ini mengembalikan NULL
, jalankan ANALYZE
atau VACUUM
untuk meja dan ulangi. (Atau, Anda bisa memperkirakan lebar baris berdasarkan jenis kolom seperti Postgres, tapi itu membosankan dan rawan kesalahan.)
Jika kueri ini mengembalikan 0
, meja tampaknya kosong. Tapi saya akan ANALYZE
untuk memastikan. (Dan mungkin periksa autovacuum
your Anda pengaturan.)
Biasanya, block_size
adalah 8192. current_setting('block_size')::int
mencakup pengecualian langka.
Kualifikasi tabel dan skema membuatnya kebal terhadap search_path
any dan ruang lingkup.
Either way, kueri secara konsisten membutuhkan <0,1 md untuk saya.
Sumber daya Web lainnya:
- FAQ Wiki Postgres
- Halaman wiki Postgres untuk menghitung perkiraan dan menghitung(*) kinerja
TABLESAMPLE SYSTEM (n)
di Postgres 9.5+
SELECT 100 * count(*) AS estimate FROM mytable TABLESAMPLE SYSTEM (1);
Seperti komentar @a_horse, klausa tambahan untuk SELECT
perintah dapat berguna jika statistik di pg_class
tidak cukup saat ini untuk beberapa alasan. Misalnya:
- Tidak ada
autovacuum
berlari. - Segera setelah
INSERT
besar /UPDATE
/DELETE
. TEMPORARY
tabel (yang tidak dicakup olehautovacuum
).
Ini hanya terlihat secara acak n % (1
dalam contoh) pemilihan blok dan menghitung baris di dalamnya. Sampel yang lebih besar meningkatkan biaya dan mengurangi kesalahan, pilihan Anda. Akurasi bergantung pada lebih banyak faktor:
- Distribusi ukuran baris. Jika blok tertentu memiliki baris yang lebih lebar dari biasanya, hitungannya lebih rendah dari biasanya, dll.
- Tuple mati atau
FILLFACTOR
menempati ruang per blok. Jika terdistribusi tidak merata di seluruh tabel, perkiraannya mungkin salah. - Kesalahan pembulatan umum.
Biasanya, perkiraan dari pg_class
akan lebih cepat dan akurat.
Jawaban untuk pertanyaan sebenarnya
Pertama, saya perlu mengetahui jumlah baris dalam tabel itu, jika jumlah total lebih besar dari beberapa konstanta yang telah ditentukan,
Dan apakah itu ...
... mungkin pada saat hitungan melewati nilai konstan saya, itu akan menghentikan penghitungan (dan tidak menunggu untuk menyelesaikan penghitungan untuk menginformasikan bahwa jumlah baris lebih besar).
Ya. Anda dapat menggunakan subkueri dengan LIMIT
:
SELECT count(*) FROM (SELECT 1 FROM token LIMIT 500000) t;
Postgres benar-benar berhenti menghitung melampaui batas yang diberikan, Anda mendapatkan tepat dan terkini hitung hingga n baris (500.000 dalam contoh), dan n sebaliknya. Tidak secepat perkiraan di pg_class
, meskipun.