आप Postgres में अपने सभी तालिकाओं के लिए पंक्ति गणना कैसे पाते हैं


395

मैं Postgres में मेरे सभी तालिकाओं के लिए पंक्ति गणना खोजने का एक तरीका ढूंढ रहा हूं। मुझे पता है कि मैं इस समय के साथ एक टेबल कर सकता हूं:

SELECT count(*) FROM table_name;

लेकिन मैं सभी तालिकाओं के लिए पंक्ति गणना देखना चाहता हूं और फिर मेरे सभी तालिकाओं को कितना बड़ा होने का अंदाजा लगाना है।

जवाबों:


582

इस तरह की गिनती प्राप्त करने के तीन तरीके हैं, प्रत्येक अपने स्वयं के ट्रेडऑफ़ के साथ।

यदि आप एक सच्ची गणना चाहते हैं, तो आपको SELECT स्टेटमेंट को निष्पादित करना होगा, जैसे कि आपने प्रत्येक टेबल के खिलाफ उपयोग किया था। ऐसा इसलिए है क्योंकि PostgreSQL पंक्ति में पंक्ति दृश्यता की जानकारी स्वयं रखता है, कहीं और नहीं, इसलिए कोई भी सटीक गणना केवल कुछ लेनदेन के सापेक्ष हो सकती है। आपको उस समय इस बात की गणना हो रही है कि लेनदेन उस समय क्या देख रहा है जब वह निष्पादित होता है। आप डेटाबेस में प्रत्येक तालिका के विरुद्ध चलने के लिए इसे स्वचालित कर सकते हैं, लेकिन आपको शायद उस स्तर की सटीकता की आवश्यकता नहीं है या उस लंबे समय तक इंतजार करना चाहते हैं।

दूसरा दृष्टिकोण बताता है कि सांख्यिकी कलेक्टर किसी भी समय "लाइव" (हटाए गए या बाद में अपडेट नहीं किए गए) कितनी पंक्तियों को ट्रैक करता है। यह मूल्य भारी गतिविधि के तहत थोड़ा हटकर हो सकता है, लेकिन आमतौर पर यह एक अच्छा अनुमान है:

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

यह आपको यह भी दिखा सकता है कि कितनी पंक्तियाँ मर चुकी हैं, जो कि मॉनिटर करने के लिए एक दिलचस्प संख्या है।

तीसरा तरीका यह नोट करना है कि सिस्टम ANALYZE कमांड, जो कि ऑटोगाक्यूम प्रक्रिया द्वारा नियमित रूप से पोस्टग्रेसीक्यू 8.3 के रूप में तालिका के आंकड़ों को अपडेट करने के लिए निष्पादित किया जाता है, एक पंक्ति अनुमान भी गणना करता है। आप इसे इस तरह से पकड़ सकते हैं:

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;

इन प्रश्नों में से कौन सा उपयोग करना बेहतर है, कहना मुश्किल है। आम तौर पर मैं यह निर्णय लेता हूं कि क्या अधिक उपयोगी जानकारी है जो मैं pg_class के अंदर या pg_stat_user_tables के अंदर भी उपयोग करना चाहता हूं। बुनियादी गणना के उद्देश्यों के लिए बस यह देखना है कि सामान्य रूप से कितनी बड़ी चीजें हैं, या तो पर्याप्त सटीक होनी चाहिए।


2
पूर्णता के लिए, कृपया इसे पहले विकल्प के लिए जोड़ें (धन्यवाद @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;
एस्टानी

1
@Greg स्मिथ ने कौन सा संस्करण पेश किया n_live_tup? मेरे Redshift डेटाबेस में उस कॉलम का अभाव है। यह पोस्टग्रेज 8.0.2 का व्युत्पन्न है।
इयान सैमुअल मैकलीन एल्डर

1
'सेकंड एप्रोच' क्वेरी (का उपयोग करते हुए pg_stat_user_tables) ने n_live_tupमेरे लिए ज्यादातर शून्य वापस लौटाए क्योंकि ANALYZEकभी भी चलाया नहीं गया था। ANALYZEप्रत्येक स्कीमा / टेबल पर चलने के बजाय और हमेशा के लिए एक उत्तर के लिए प्रतीक्षा करें, मैंने पहली बार 'तीसरे दृष्टिकोण' का उपयोग करके परिणामों की जाँच की और एक (उपयोग करते हुए pg_class) बहुत सटीक गणना लौटा दी।
ब्रायन डी

@BrianD, "विश्लेषण किया -d dbname" के रूप में विश्लेषण उपयोगिता का उपयोग करके डेटाबेस स्तर पर विश्लेषण को निष्पादित करना संभव है
Eralper

69

यहां एक समाधान है, जिसमें प्रत्येक तालिका के लिए सटीक गणना करने के लिए कार्यों की आवश्यकता नहीं है:

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_xmlपारित SQL क्वेरी चलाएगा और परिणाम के साथ एक XML लौटाएगा (उस तालिका के लिए पंक्ति गणना)। बाहरी xpath()तब उस xml से गिनती जानकारी निकालेंगे और इसे एक संख्या में बदल देंगे

व्युत्पन्न तालिका वास्तव में आवश्यक नहीं है, लेकिन यह xpath()समझने में थोड़ा आसान बनाता है - अन्यथा फ़ंक्शन query_to_xml()को पूरा करने की आवश्यकता होगी xpath()


3
बहुत चालाक। यह अफ़सोस की बात है कि वहाँ कोई नहीं है query_to_jsonb()
klin

@a_horse_with_no_name, क्या यह निष्पादन के दौरान व्यस्त और विशाल तालिकाओं पर कोई प्रदर्शन मुद्दा देगा?
स्पाइक

@Spike: प्रदर्शन के मुद्दों की तुलना में क्या? प्रमुख प्रदर्शन अड़चन select count(*)हर मेज पर चल रहा है ।
a_horse_with_no_name

@a_horse_with_no_name, 100 मिलियन रिकॉर्ड के खिलाफ x_path फ़ंक्शन निष्पादित करके।
स्पाइक

@ सामान्य: xpath()फ़ंक्शन केवल एक पंक्ति पर लागू होता है - का परिणामcount(*)
a_horse_with_no_name

24

अनुमान प्राप्त करने के लिए, ग्रेग स्मिथ का उत्तर देखें

सटीक गणना प्राप्त करने के लिए, अब तक के अन्य उत्तर कुछ मुद्दों से ग्रस्त हैं, उनमें से कुछ गंभीर हैं (नीचे देखें)। यहाँ एक संस्करण है जो उम्मीद है कि बेहतर है:

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;

यह पैरामीटर के रूप में एक स्कीमा नाम लेता है, या publicयदि कोई पैरामीटर नहीं दिया जाता है।

किसी विशिष्ट सूची या कार्य को संशोधित किए बिना क्वेरी से आने वाली सूची के साथ काम करने के लिए, इसे इस तरह से क्वेरी के भीतर से बुलाया जा सकता है:

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;

यह स्कीमा, तालिका और पंक्तियों की गणना के साथ 3-कॉलम आउटपुट का उत्पादन करता है।

अब यहां कुछ अन्य मुद्दों के बारे में बताया गया है जो इस फ़ंक्शन से बचते हैं:

  • तालिका और स्कीमा नामों को निष्पादन योग्य एसक्यूएल में उद्धृत नहीं किया जाना चाहिए, या तो इसके प्रारूप स्ट्रिंग के साथ quote_identया अधिक आधुनिक format()फ़ंक्शन के साथ %I। अन्यथा कुछ दुर्भावनापूर्ण व्यक्ति अपनी तालिका का नाम दे सकते हैं tablename;DROP TABLE other_tableजो तालिका नाम के रूप में पूरी तरह से मान्य है।

  • SQL इंजेक्शन और मज़ेदार वर्ण समस्याओं के बिना भी, तालिका का नाम भिन्न रूप में मौजूद हो सकता है। यदि किसी तालिका का नाम ABCDऔर दूसरा है abcd, तो SELECT count(*) FROM...एक उद्धृत नाम का उपयोग करना चाहिए अन्यथा यह दो बार ABCDगणना और गणना करेगा abcd%Iप्रारूप के इस स्वचालित रूप से करता है।

  • information_schema.tablesतालिकाओं के अलावा कस्टम मिश्रित प्रकारों को सूचीबद्ध करता है, तब भी जब table_type है 'BASE TABLE'(!)। एक परिणाम के रूप में, हम उस पर पुनरावृति नहीं कर सकते information_schema.tables, अन्यथा हम होने का जोखिम उठाते हैं select count(*) from name_of_composite_typeऔर वह विफल हो जाएगा। ओटीओएच pg_class where relkind='r'को हमेशा ठीक काम करना चाहिए।

  • COUNT का प्रकार () है bigint, नहीं int। 2.15 बिलियन से अधिक पंक्तियों वाली तालिकाएँ मौजूद हो सकती हैं (उन पर एक गिनती (*) चलाना एक बुरा विचार है, हालाँकि)।

  • कई स्तंभों के साथ एक परिणाम देने के लिए एक फ़ंक्शन के लिए एक स्थायी प्रकार की आवश्यकता नहीं है। RETURNS TABLE(definition...)एक बेहतर विकल्प है।


18

यदि आपको संभावित बासी डेटा से ऐतराज नहीं है, तो आप क्वेरी ऑप्टिमाइज़र द्वारा उपयोग किए गए समान आँकड़ों तक पहुँच सकते हैं ।

कुछ इस तरह:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;

@mlissner: यदि आपका ऑटोवेक्यूम अंतराल बहुत लंबा है या आपने ANALYZEटेबल पर मैनुअल नहीं चलाया है , तो आंकड़े रास्ते से हट सकते हैं। इसका डेटाबेस लोड का एक सवाल है और डेटाबेस कैसे कॉन्फ़िगर किया गया है (यदि आंकड़े अधिक बार अपडेट किए जाते हैं, तो आंकड़े अधिक सटीक होंगे, लेकिन यह रनटाइम प्रदर्शन को कम कर सकता है)। अंत में, सटीक डेटा प्राप्त करने का एकमात्र तरीका select count(*) from tableसभी तालिकाओं के लिए चलना है ।
ig0774

17

यह जानने की कोशिश करने वाले लोगों के लिए हैकरी, व्यावहारिक उत्तर कि उन्हें कौन सी हेरोकू योजना की आवश्यकता है और ताज़ा करने के लिए हरकू की धीमी पंक्ति काउंटर की प्रतीक्षा नहीं कर सकते:

मूल रूप से आप चलाना चाहते हैं \dtमें psql, (यह इस तरह दिखेगा अपने पसंदीदा पाठ संपादक के लिए परिणाम कॉपी:

 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

), फिर एक रेगेक्स खोज चलाएं और इस तरह बदलें:

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

सेवा:

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

इससे आपको बहुत कुछ मिलेगा:

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
;

(आपको अंतिम हटाने unionऔर अर्धविराम को मैन्युअल रूप से जोड़ना होगा)

इसे चलाएं psqlऔर आप कर रहे हैं।

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

मुझे यह विचार अच्छा लगा
GuilPejon

एटम में, मुझे खोज को फिर से बदलना और इस तरह बदलना था: select '$1', count(*) from $1 union/g
चक

इसके अलावा, पोस्ट कहता है: "आपको संघ को हटाने और अर्धविराम को अंत में जोड़ने की आवश्यकता होगी।" यह एक टाइपो है। आपको बहुत अंत में एक अर्धविराम ( ) जोड़ना /g(रखना union) और निकालना ;होगा। unionअर्धविराम से पहले अंतिम हटाना न भूलें ।
चक

1
" unionअर्धविराम से पहले अंतिम को दूर करने के लिए मत भूलना " मेरा मतलब है :) स्पष्ट करने के लिए "अंतिम" शब्द जोड़ा गया
और सरफ

10

यकीन नहीं होता है कि अगर कोई जवाब बैश में है, तो आपको स्वीकार्य है, लेकिन 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
इसके सार पर, यह सिर्फ select count(*) from table_name;ओपी में समान रूप से उबलता है !
नोआच मर्दमैन

8

मैं आमतौर पर आंकड़ों पर भरोसा नहीं करता, खासकर 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;

यह अच्छा है लेकिन पहली क्वेरी में पंक्तिबद्ध मान के लिए स्कीमा भी शामिल होना चाहिए। यदि अलग-अलग स्कीमाओं में परस्पर विरोधी नाम हैं तो यह अपेक्षा के अनुरूप काम नहीं करेगा। तो क्वेरी का यह हिस्सा अधिक dsql2('select count(*) from livescreen.'||table_name)या बेहतर दिखना चाहिए, इसे अपने स्वयं के फ़ंक्शन में बदल दिया जा सकता है।
याकूब-ऑल्केज़क

6

मुझे वह URL याद नहीं है जहाँ से मैंने यह एकत्र किया है। लेकिन उम्मीद है कि यह आपकी मदद करनी चाहिए:

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; 

निष्पादन के select count_em_all();लिए आपको अपनी सभी तालिकाओं की पंक्ति गणना मिलनी चाहिए।


1
कॉलम नामों (जैसे quote_ident(t_name.relname)) को असामान्य नामों (उदाहरण के लिए "कॉलम-नाम") के लिए उचित समर्थन सुनिश्चित करना अच्छा है ।
Gorsky

इसे बाद में छोड़ने के लिए: DROP FUNCTION count_em_all ();
एलेक्सी गाबी

एक त्रुटि मिली: count_em_all () का चयन करें; त्रुटि: "समूह" लाइन 1 पर या उसके निकट वाक्यविन्यास त्रुटि: समूह का चयन करें () के रूप में "गणना" समूह से प्रश्न: चयन समूह () के रूप में समूह से "गणना" गणना: PL / pgSQL count_em_all () लाइन 18 से अधिक के लिए। EXECUTE स्टेटमेंट
Aalex Gabi

महान! चयन करने और क्रमबद्ध करने के लिए - SELECT * FROM count_em_all() as r ORDER BY r.num_rows DESC;
Ken4scholars

6

सरल दो चरण:
(नोट: कुछ भी बदलने की आवश्यकता नहीं है - बस कॉपी पेस्ट करें)
1. फ़ंक्शन बनाएं

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. सभी तालिकाओं के लिए पंक्तियाँ गिनने के लिए इस क्वेरी को चलाएँ

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;

या

पंक्तियों को तालिकाबद्ध करने के लिए

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

मैंने सभी तालिकाओं को शामिल करने के लिए एक छोटा बदलाव किया, गैर-सार्वजनिक तालिकाओं के लिए भी।

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();इसे कॉल करने के लिए उपयोग करें।

आशा है कि आपको यह उपयोगी लगेगा। पॉल


त्रुटि: "r.table_schema" एक ज्ञात चर नहीं है
स्लैशडॉटिर


1

मुझे डैनियल वेरिटा का जवाब पसंद है । लेकिन जब आप एक क्रिएट स्टेटमेंट का उपयोग नहीं कर सकते हैं , तो आप या तो एक बैश समाधान का उपयोग कर सकते हैं या, यदि आप एक विंडोज़ उपयोगकर्ता हैं, तो एक पॉवरशेल:

# 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

मैं सभी सारणियों से कुल चाहता था + उनकी गिनती के साथ तालिकाओं की एक सूची। एक प्रदर्शन चार्ट की तरह थोड़ा जहां ज्यादातर समय बिताया गया था

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

आप निश्चित रूप LIMITसे इस संस्करण में भी परिणामों पर एक खंड लगा सकते हैं ताकि आपको सबसे बड़े nअपराधियों के साथ-साथ कुल भी मिल सके ।

इसके बारे में एक बात जो ध्यान रखनी चाहिए, वह यह है कि थोक आयात के बाद आपको इसे थोड़ी देर के लिए बैठने देना चाहिए। मैंने वास्तविक आयात डेटा का उपयोग करके कई तालिकाओं में डेटाबेस में केवल 5000 पंक्तियों को जोड़कर इसका परीक्षण किया। इसने एक मिनट (शायद एक विन्यास योग्य खिड़की) के लिए 1800 रिकॉर्ड दिखाए

यह https://stackoverflow.com/a/2611745/1548557 कार्य से आधारित है , इसलिए CTE के भीतर उपयोग करने के लिए क्वेरी के लिए धन्यवाद और मान्यता

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.