Postgres'teki tüm tablolarınız için satır sayısını nasıl buluyorsunuz?


395

Postgres tüm tablolarım için satır sayısını bulmak için bir yol arıyorum. Bu tabloyu bir seferde yapabileceğimi biliyorum:

SELECT count(*) FROM table_name;

ancak tüm tablolar için satır sayısını görmek ve daha sonra tüm tablolarımın ne kadar büyük olduğu hakkında bir fikir edinmek için bunu sipariş etmek istiyorum.

Yanıtlar:


582

Bu tür bir sayımı elde etmenin üç yolu vardır, her birinin kendi ödünleşimi vardır.

Gerçek bir sayı istiyorsanız, SELECT deyimini her tablo için kullandığınız gibi yürütmeniz gerekir. Bunun nedeni, PostgreSQL'in satır görünürlük bilgilerini başka hiçbir yerde değil, satırda tutmasıdır, bu nedenle doğru sayım yalnızca bazı işlemlerle ilişkili olabilir. İşlemin yürütüldüğü sırada gördüğü işlemlerin sayısını alırsınız. Veritabanındaki her tabloya karşı çalıştırmak için bunu otomatikleştirebilirsiniz, ancak muhtemelen bu doğruluk düzeyine ihtiyacınız yoktur veya bu kadar beklemek istemezsiniz.

İkinci yaklaşım, istatistik toplayıcının kabaca kaç satırın "canlı" olduğunu (sonraki güncellemelerle silinmediğini veya kullanılmadığını) izlediğini belirtir. Bu değer, ağır aktivite altında biraz düşebilir, ancak genellikle iyi bir tahmindir:

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

Bu, kaç satırın öldüğünü de gösterebilir, bu da izlenecek ilginç bir sayıdır.

Üçüncü yol, tablo istatistiklerini güncellemek için PostgreSQL 8.3'ten itibaren otovakum işlemi tarafından düzenli olarak yürütülen sistem ANALYZE komutunun da bir satır tahmini hesapladığını not etmektir. Bunu böyle alabilirsin:

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;

Bu sorgulardan hangisinin daha iyi kullanıldığını söylemek zor. Normalde bu kararı, pg_class içinde mi yoksa pg_stat_user_tables içinde de kullanmak istediğim daha faydalı bilgilere dayanarak alırım. Temel sayma amacıyla, genel olarak ne kadar büyük şeylerin olduğunu görmek için, her ikisi de yeterince doğru olmalıdır.


2
Tamamlama uğruna, lütfen ilk seçenek için ekleyin (teşekkürler @a_horse_with_no_name'e gider):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 Hangi sürüm tanıtıldı n_live_tup? Redshift veritabanımda bu sütun yok. Postgres 8.0.2'nin bir türevidir.
Iain Samuel McLean Elder

1
'İkinci yaklaşım' sorgusu (kullanma pg_stat_user_tables) n_live_tupbenim için çoğunlukla sıfır verdi çünkü ANALYZEhiç çalıştırılmadı. ANALYZEHer şema / tablo üzerinde çalıştırmak ve sonsuza kadar bir cevap beklemek yerine , önce 'üçüncü yaklaşım' kullanarak sonuçları kontrol ettim ve (kullanarak pg_class) çok doğru sayımlar verdi.
Brian D

@BrianD, "
analyzedb

69

Her tablo için doğru sayım elde etmek için işlev gerektirmeyen bir çözüm:

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_xmliletilen SQL sorgusunu çalıştırır ve sonucu içeren bir XML döndürür (bu tablo için satır sayısı). Dış xpath()daha sonra bu xml'den sayım bilgilerini alır ve bir sayıya dönüştürür

Türetilmiş tablo gerçekten gerekli değildir, ancak xpath()anlaşılmasını biraz kolaylaştırır - aksi takdirde bütünün işleve query_to_xml()geçirilmesi gerekir xpath().


3
Çok zeki. Yazık ki hayır query_to_jsonb().
klin

@a_horse_with_no_name, yürütürken meşgul ve devasa tablolarda herhangi bir performans sorunu verir mi?
Spike

@ Spike: neye kıyasla performans sorunları? Başlıca performans darboğazı select count(*)her tabloda bir çalışıyor .
a_horse_with_no_name

@a_horse_with_no_name, 100 milyon kayda karşı x_path işlevini yürüterek.
Spike

@Spike: xpath()işlev yalnızca tek bir satıra uygulanırcount(*)
a_horse_with_no_name

24

Tahmin almak için Greg Smith'in cevabına bakınız .

Kesin sayım elde etmek için, şimdiye kadarki diğer cevaplar, bazıları ciddi olan bazı sorunlarla uğraşmaktadır (aşağıya bakınız). İşte umarım daha iyi bir sürüm:

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;

Parametre olarak bir şema adı alır veya publicparametre verilmezse.

Belirli bir şema listesiyle veya bir sorgudan işlevi değiştirmeden gelen bir listeyle çalışmak için, şuna benzer bir sorgudan çağrılabilir:

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;

Bu şema, tablo ve satır sayısı ile 3 sütun çıktı üretir.

Şimdi, bu işlevin önlediği diğer yanıtlarda bazı sorunlar var:

  • Tablo ve şema adları , biçim dizesiyle quote_identdaha modern format()işlevle veya daha modern işlevle alıntı yapılmadan yürütülebilir SQL'e enjekte edilmemelidir %I. Aksi takdirde, bazı kötü niyetli kişiler tablolarını tablename;DROP TABLE other_tabletablo adı olarak mükemmel şekilde geçerli olarak adlandırabilirler.

  • SQL enjeksiyonu ve komik karakter problemleri olmasa bile, tablo adı duruma göre farklılıklar gösterebilir. Bir tablo ABCDve başka bir tablo isimlendirilmişse abcd, SELECT count(*) FROM...tırnak içine alınmış bir isim kullanmalıdır, aksi takdirde iki kez atlanır ABCDve sayılır abcd. %IFormatının otomatik olarak yapar.

  • information_schema.tablestable_type 'BASE TABLE'(!) olsa bile tablolara ek olarak özel bileşik türlerini listeler . Sonuç olarak, tekrar edemeyiz information_schema.tables, aksi takdirde sahip olma riski vardır select count(*) from name_of_composite_typeve bu başarısız olur. OTOH pg_class where relkind='r'her zaman iyi çalışmalıdır.

  • COUNT () türü bigintdeğil int. 2,15 milyardan fazla satır içeren tablolar olabilir (üzerinde bir sayı (*) çalıştırmak kötü bir fikirdir).

  • Bir işlevin birkaç sütunlu sonuç kümesi döndürmesi için kalıcı bir türün oluşturulması gerekmez. RETURNS TABLE(definition...)daha iyi bir alternatiftir.


18

Potansiyel olarak eski verileri sakıncası yoksa , sorgu optimize edici tarafından kullanılan istatistiklere erişebilirsiniz .

Gibi bir şey:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;

@mlissner: Otovakum aralığınız çok uzunsa veya ANALYZEmasada bir el kitabı kullanmadıysanız, istatistikler yoldan çıkabilir. Bu, veritabanı yükü ve veritabanının nasıl yapılandırıldığıyla ilgilidir (istatistikler daha sık güncellenirse, istatistikler daha doğru olacaktır, ancak çalışma zamanı performansını düşürebilir). Sonuçta, doğru veri almanın tek yolu select count(*) from tabletüm tablolar için çalıştırmaktır .
ig0774

17

Hangi Heroku planına ihtiyaç duyduklarını ve heroku'nun yavaş sıra sayacının yenilenmesini bekleyemeyenleri değerlendirmeye çalışan çirkin, pratik cevap:

Temelde çalıştırmak istediğiniz \dtyer psql, (bu gibi görünecektir sevdiğiniz metin editörü sonuçlarını kopyalayın:

 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

), ardından bir normal ifade araması çalıştırın ve aşağıdaki gibi değiştirin:

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

için:

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

bu size çok benzer bir şey verecektir:

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
;

(Sonuncuyu kaldırmanız unionve sonuna noktalı virgül eklemeniz gerekir)

İçeri girin psqlve işiniz bitti.

            ?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
[..]

I like this idea
GuilPejon

Atom, ben arama regex ve böyle değiştirmek zorunda kaldı: select '$1', count(*) from $1 union/g
chuck

Ayrıca yazı şöyle diyor: "Birliği kaldırmanız ve sonuna noktalı virgül eklemeniz gerekecek." Bu bir yazım hatası. En sonunda bir noktalı virgül ( ) kaldırmanız /g(tutmanız union) ve eklemeniz gerekir ;. unionNoktalı virgül öncesi son kaldırmayı unutmayın .
chuck

1
" unionNoktalı virgül öncesi son kaldırmayı unutmayın " demek istediğim :) Açıklamak için "son" sözcüğü eklendi
Aur Saraf

10

Bash'daki bir cevabın sizin için kabul edilebilir olup olmadığından emin değilim , ama 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

7
Özünde, bu sadece select count(*) from table_name;OP'de aynı şekilde kaynar !
Noach Magedman

8

Genellikle istatistiklere, özellikle PostgreSQL'e güvenmiyorum.

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;

Bu güzel ama ilk sorgu rownum değeri için şema da içermelidir. Farklı şemalarda çakışan adlar varsa, bu beklendiği gibi çalışmaz. Bu nedenle, sorgunun bu kısmı daha çok dsql2('select count(*) from livescreen.'||table_name)veya daha iyi görünmelidir , kendi işlevine dönüştürülebilir.
jakub-olczyk

6

Bunu topladığım URL'yi hatırlamıyorum. Ama umarım bu sana yardımcı olur:

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; 

Yürütme select count_em_all();, tüm tablolarınızın satır sayısını almanızı sağlayacaktır.


1
quote_ident(t_name.relname)Alışılmadık adlar (örneğin "sütun adı") için uygun desteği sağlamak üzere sütun adlarını (beğenmek ) alıntılamak iyi bir fikirdir .
gorsky

Daha sonra bırakmak için: DROP FUNCTION count_em_all ();
Aalex Gabi

Bir hata oluştu: select count_em_all (); HATA: "grup" veya yakınında "sözdizimi hatası LINE 1: SELECT COUNT () AS" count "FROM grup ^ QUERY: SELECT COUNT () AS" count "FROM grup CONTEXT: PL / pgSQL işlevi count_em_all () satır 18 FOR FOR
YÜRÜTME

Harika! Seçmek ve sıralamak için - SELECT * FROM count_em_all() as r ORDER BY r.num_rows DESC;
Ken4scholars

6

Basit İki Adım:
(Not: Hiçbir şeyi değiştirmeye gerek yok - sadece kopyala yapıştır)
1. fonksiyon yarat

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. Tüm tabloların satır sayısını almak için bu sorguyu çalıştırın

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;

veya

Satır sayısını tablo şeklinde almak için

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;

5

Herkese açık olmayan tablolar için de tüm tabloları içerecek küçük bir değişiklik yaptım.

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; 

select count_em_all();aramak için kullanın .

Umarım bu yararlı bulursun. Paul


HATA: "r.table_schema" bilinen bir değişken değil
slashdottir

2

Bu benim için çalıştı

Şema adı, relname, n_live_tup SELECT pg_stat_user_tables ORDER BY n_live_tup DESC;


1

Daniel Vérité'nin cevabını seviyorum . Ancak bir CREATE ifadesi kullanamadığınızda, bash çözümünü veya Windows kullanıcısıysanız, powershell ifadesini kullanabilirsiniz :

# 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"
}

0

Tüm tablolar + sayıları ile tabloların bir listesini toplam istedim. Biraz en fazla zamanın nerede harcandığına dair performans tablosu

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

Tabii ki LIMITbu versiyondaki sonuçlara da bir madde koyabilirsiniz , böylece en büyük nsuçluları ve toplamı elde edebilirsiniz.

Bu konuda dikkat edilmesi gereken bir şey, toplu ithalattan sonra bir süre beklemeniz gerektiğidir. Bunu gerçek alma verilerini kullanarak birkaç tabloya bir veritabanına 5000 satır ekleyerek test ettim. Yaklaşık bir dakika boyunca 1800 kayıt gösterdi (muhtemelen yapılandırılabilir bir pencere)

Bu, https://stackoverflow.com/a/2611745/1548557 çalışmalarından alınmıştır, bu nedenle, sorgunun CTE içinde kullanılması için teşekkür ederiz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.