2010-04-07 8 views
273

Tôi đang tìm cách tìm số hàng cho tất cả các bảng của tôi trong Postgres. Tôi biết tôi có thể làm một bảng này tại một thời điểm với:Làm thế nào để bạn tìm số hàng cho tất cả các bảng của bạn trong Postgres

SELECT count(*) FROM table_name; 

nhưng tôi muốn xem số lượng hàng cho tất cả các bảng và sau đó tự do đó để có được một ý tưởng về lớn như thế nào tất cả các bảng của tôi là .

Trả lời

387

Có ba cách để có được loại này đếm, mỗi cân bằng của mình.

Nếu bạn muốn có số lượng thực, bạn phải thực hiện câu lệnh SELECT giống như câu lệnh bạn đã sử dụng đối với mỗi bảng. Điều này là do PostgreSQL giữ thông tin hiển thị hàng trong chính nó, không phải ở bất kỳ nơi nào khác, vì vậy bất kỳ số đếm chính xác nào cũng chỉ có thể liên quan đến một số giao dịch. Bạn đang nhận được một số của những gì mà giao dịch thấy tại thời điểm khi nó thực hiện. Bạn có thể tự động hóa điều này để chạy với mọi bảng trong cơ sở dữ liệu, nhưng có thể bạn không cần mức độ chính xác đó hoặc muốn đợi lâu.

Cách tiếp cận thứ hai lưu ý rằng trình thu thập số liệu theo dõi khoảng bao nhiêu hàng "sống" (không bị xóa hoặc lỗi thời bởi các cập nhật sau) bất kỳ lúc nào. Giá trị này có thể bị giảm một chút do hoạt động nặng, nhưng thường là ước tính tốt:

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

Điều đó cũng có thể cho bạn biết có bao nhiêu hàng chết, đây là một số thú vị để theo dõi.

Cách thứ ba là lưu ý rằng lệnh ANALYZE hệ thống, được thực thi bởi quy trình tự động ngắt thường xuyên như PostgreSQL 8.3 để cập nhật số liệu thống kê bảng, cũng tính toán ước tính hàng. Bạn có thể lấy cái như thế này:

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; 

Điều nào trong số các truy vấn này là tốt hơn để sử dụng khó nói. Thông thường tôi đưa ra quyết định dựa trên việc liệu có thêm thông tin hữu ích nào tôi cũng muốn sử dụng bên trong pg_class hoặc bên trong pg_stat_user_tables. Đối với mục đích đếm cơ bản chỉ để xem làm thế nào những điều lớn nói chung, hoặc là đủ chính xác.

13

Nếu bạn không nhớ dữ liệu cũ, bạn có thể access the same statistics used by the query optimizer.

Cái gì như:

SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables; 
+0

Tôi đã thử sử dụng điều này, nhưng dữ liệu thực sự khá cũ. – mlissner

+0

@mlissner: Nếu khoảng thời gian autovacuum của bạn quá dài hoặc bạn chưa chạy thủ công 'ANALYZE' trên bảng, các số liệu thống kê có thể bị tắt. Một câu hỏi về tải cơ sở dữ liệu và cách cơ sở dữ liệu được cấu hình (nếu các số liệu thống kê được cập nhật thường xuyên hơn, các số liệu thống kê sẽ chính xác hơn, nhưng nó có thể làm giảm hiệu suất thời gian chạy). Cuối cùng, cách duy nhất để lấy dữ liệu chính xác là chạy 'select count (*) từ bảng' cho tất cả các bảng. – ig0774

+4

chỉ cần thử nó và nó không phải là câu trả lời đúng. –

4

Tôi không nhớ URL từ nơi tôi đã thu thập thông tin này. Nhưng hy vọng điều này sẽ giúp bạn:

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; 

Thực hiện select count_em_all(); sẽ giúp bạn đếm hàng của tất cả các bảng.

+1

Bạn nên báo giá tên cột (như 'quote_ident (t_name.relname)') để đảm bảo hỗ trợ thích hợp cho các tên khác thường (ví dụ: "tên cột") . – gorsky

+0

Để thả nó sau đó: DROP FUNCTION count_em_all(); –

+0

Đã xảy ra lỗi: chọn count_em_all(); L ERI: lỗi cú pháp tại hoặc gần "nhóm" LINE 1: SELECT COUNT() AS "count" FROM nhóm^QUERY: SELECT COUNT() AS "count" TỪ nhóm CONTEXT: PL/pgSQL function count_em_all() dòng 18 tại FOR over Câu lệnh EXECUTE –

8

Không chắc chắn nếu một câu trả lời trong bash là chấp nhận được đối với bạn, nhưng 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 
+6

Tại bản chất của nó, điều này chỉ làm sôi xuống cùng một 'số đếm (*) từ tên_bảng,' trong OP! –

4

Tôi thường không dựa vào thống kê, đặc biệt là trong 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; 
3

tôi đã thực hiện một sự thay đổi nhỏ để bao gồm tất cả các bảng, cũng cho các bảng ngoài công lập.

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; 

sử dụng select count_em_all(); để gọi.

Hy vọng bạn thấy điều này hữu ích. Paul

+0

L ERI: "r.table_schema" không phải là biến số đã biết – slashdottir

14

Để nhận ước tính, hãy xem Greg Smith's answer.

Để có được số lượng chính xác, các câu trả lời khác cho đến nay vẫn gặp phải một số vấn đề, một số vấn đề nghiêm trọng (xem bên dưới). Đây là một phiên bản hy vọng tốt hơn:

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; 

Phải có tên lược đồ làm tham số hoặc public nếu không có thông số nào được cung cấp.

Để làm việc với một danh sách cụ thể của lược đồ hoặc một danh sách đến từ một truy vấn mà không sửa đổi chức năng, nó có thể được gọi từ bên trong một truy vấn như thế này:

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; 

này tạo ra một sản lượng 3 cột với lược đồ, bảng và các hàng đếm.

Bây giờ đây là một số vấn đề trong các câu trả lời khác mà chức năng này tránh:

  • Bảng và schema tên không nên tiêm vào SQL thực thi mà không bị trích dẫn, hoặc với quote_ident hoặc qua các nhân hiện đại hơn format() với chuỗi định dạng %I. Nếu không, một số người độc hại có thể đặt tên cho bàn là tablename;DROP TABLE other_table hoàn toàn hợp lệ dưới dạng tên bảng.

  • Ngay cả khi không có sự cố SQL injection và các ký tự buồn cười, tên bảng có thể tồn tại trong các biến thể khác nhau theo từng trường hợp. Nếu một bảng có tên là ABCD và một bảng khác là abcd, thì SELECT count(*) FROM... phải sử dụng tên được trích dẫn nếu không nó sẽ bỏ qua ABCD và đếm abcd hai lần. Các định dạng %I thực hiện điều này tự động.

  • information_schema.tables liệt kê các loại tổng hợp tùy chỉnh ngoài bảng, ngay cả khi table_type là 'BASE TABLE' (!). Do đó, chúng tôi không thể lặp lại trên information_schema.tables, nếu không chúng tôi có nguy cơ bị select count(*) from name_of_composite_type và điều đó sẽ không thành công. OTOH pg_class where relkind='r' luôn hoạt động tốt.

  • Loại COUNT() là bigint, không phải int. Bảng với hơn 2,15 tỷ hàng có thể tồn tại (tuy nhiên, việc đếm số (*) trên chúng là một ý tưởng tồi).

  • Một loại vĩnh viễn không cần phải được tạo cho hàm để trả về kết quả có nhiều cột. RETURNS TABLE(definition...) là giải pháp thay thế tốt hơn.

1

Tôi thích Daniel Vérité's answer. Nhưng khi bạn không thể sử dụng câu lệnh CREATE bạn có thể sử dụng một bash solution hay, nếu bạn là người dùng cửa sổ, một powershell một:

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

Các hacky, câu trả lời thiết thực cho những người cố gắng để đánh giá mà Heroku kế hoạch mà họ cần và không thể chờ đợi cho Heroku của chậm hàng truy cập để làm mới:

về cơ bản bạn muốn chạy \dt trong psql, sao chép các kết quả để soạn thảo văn bản yêu thích của bạn (nó sẽ giống như thế này:

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 

), sau đó chạy một tìm kiếm và thay thế regex như thế này:

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

tới:

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

mà sẽ mang lại bạn một cái gì đó rất tương tự như sau:

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 
; 

(Bạn sẽ cần phải xóa union và thêm dấu chấm phẩy ở cuối theo cách thủ công)

Ru n nó trong psql và bạn đã hoàn tất.

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

Đây là một giải pháp mà không yêu cầu các chức năng để có được một con số chính xác cho mỗi bảng:

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 sẽ chạy truy vấn SQL thông qua và trả về một XML với kết quả (số lượng hàng cho bảng đó). Bên ngoài xpath() sau đó sẽ trích xuất các thông tin đếm từ xml đó và chuyển nó sang một số

Bảng có nguồn gốc là không thực sự cần thiết, nhưng làm cho xpath() một chút dễ dàng hơn để hiểu - nếu không toàn bộ query_to_xml() sẽ cần phải được truyền cho chức năng xpath().

+0

Rất thông minh. Thật đáng tiếc là không có 'query_to_jsonb()'. – klin