Как вы находите количество строк для всех ваших таблиц в Postgres

395

Я ищу способ найти количество строк для всех моих таблиц в Postgres. Я знаю, что могу сделать это по одной таблице за раз с:

SELECT count(*) FROM table_name;

но я хотел бы увидеть количество строк для всех таблиц, а затем упорядочить их, чтобы понять, насколько велики все мои таблицы.

mmrobins
источник

Ответы:

582

Есть три способа получить такой счет, каждый со своим собственным компромиссом.

Если вам нужен истинный счет, вы должны выполнить оператор SELECT, подобный тому, который вы использовали для каждой таблицы. Это связано с тем, что PostgreSQL хранит информацию о видимости строк в самой строке, а не где-либо еще, поэтому любой точный счет может быть только относительно некоторой транзакции. Вы получаете счет того, что видит эта транзакция в тот момент, когда она выполняется. Вы можете автоматизировать это для запуска каждой таблицы в базе данных, но вам, вероятно, не нужен такой уровень точности или вы хотите ждать так долго.

Второй подход отмечает, что сборщик статистики отслеживает, сколько строк «живое» (не удалено или не устарело при последующих обновлениях) в любое время. Это значение может быть немного занижено при высокой активности, но обычно это хорошая оценка:

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

Это также может показать количество мертвых строк, что само по себе является интересным числом для мониторинга.

Третий способ - отметить, что системная команда ANALYZE, которая регулярно выполняется процессом автовакуума с PostgreSQL 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;
estani
1
@ Грег Смит Какая версия представлена n_live_tup? В моей базе данных Redshift отсутствует этот столбец. Это производная от Postgres 8.0.2.
Иэн Сэмюэль Маклин, старейшина
1
Запрос «второго подхода» (использующий pg_stat_user_tables) вернул n_live_tupмне в основном нули , потому что ANALYZEон никогда не выполнялся. Вместо того, чтобы запускать ANALYZEкаждую схему / таблицу и ждать ответа вечно, я сначала проверил результаты, используя «третий подход», и тот (с использованием pg_class) дал очень точные результаты.
Брайан Д
@BrianD, можно выполнить анализ на уровне базы данных с помощью утилиты
analyb, названной
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()функции.

a_horse_with_no_name
источник
3
Очень умный. Жаль нет query_to_jsonb().
Клин
@a_horse_with_no_name, это даст какие-либо проблемы с производительностью на занятых и огромных столах во время выполнения?
Спайк
@Spike: проблемы с производительностью по сравнению с чем? Основным узким местом является производительность select count(*)на каждом столе.
a_horse_with_no_name
@a_horse_with_no_name, выполнив функцию x_path для 100 миллионов записей.
Спайк
@Spike: 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;

Это приводит к выводу трех столбцов со схемой, таблицей и количеством строк.

Теперь в некоторых ответах есть некоторые проблемы, которых эта функция избегает:

  • Имена таблиц и схем не следует вставлять в исполняемый SQL без кавычек, ни с помощью, 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и это потерпит неудачу. OTOH 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;
ig0774
источник
@mlissner: Если ваш интервал автоочистки слишком велик или вы не запустили руководство ANALYZEпо таблице, статистика может оказаться далеко. Это вопрос загрузки базы данных и того, как база данных настроена (если статистика обновляется чаще, статистика будет более точной, но это может снизить производительность во время выполнения). В конечном счете, единственный способ получить точные данные - это запустить select count(*) from tableдля всех таблиц.
ig0774
17

Хакерский, практичный ответ для людей, пытающихся оценить, какой план Heroku им нужен, и не может дождаться обновления медленного счетчика строк Героку:

В основном вы хотите запустить \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
В Atom я должен был найти и заменить регулярное выражение так: select '$1', count(*) from $1 union/g
chuck
Кроме того, в сообщении говорится: «Вам нужно удалить объединение и добавить точку с запятой в конце». Это опечатка. Вам нужно удалить /g(сохранить union) и добавить одну точку с запятой ( ;) в самом конце. Не забудьте убрать последний unionперед точкой с запятой.
Чак
1
«Не забудьте убрать последние unionперед точкой с запятой» - вот что я имел в виду :) Добавил слово «последний», чтобы уточнить
Aur Saraf
10

Не уверен, что ответ в bash приемлем для вас, но 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;
Юрий Левинский
источник
Это хорошо, но первый запрос должен также включать схему для значения rownum. Если в разных схемах есть конфликтующие имена, это не будет работать должным образом. Таким образом, эта часть запроса должна выглядеть больше 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();должно дать вам количество строк всех ваших таблиц.

Gnanam
источник
1
Рекомендуется заключать в кавычки имена столбцов (например quote_ident(t_name.relname)), чтобы обеспечить надлежащую поддержку необычных имен (например, «имя-столбца»).
Горский
Чтобы отбросить его потом: DROP FUNCTION count_em_all ();
Аалекс Габи,
Получил ошибку: выберите count_em_all (); ОШИБКА: синтаксическая ошибка в или около «группы». ЛИНИЯ 1: ВЫБРАТЬ СЧЕТЧИК () КАК «считать» ИЗ группы ^ ЗАПРОС: ВЫБРАТЬ СЧЕТЧИК () КАК «считать» ОТ группы КОНТЕКСТ: PL / pgSQL функция count_em_all () строка 18 при FOR более
ВЫПОЛНЕНИЕ
Большой! Для выбора и сортировки - 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» не известная переменная
slashdottir
2

Это сработало для меня

ВЫБЕРИТЕ имя схемы, relname, n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC;

Прадип Маурья
источник
1

Мне нравится ответ Даниэля Верите . Но когда вы не можете использовать оператор CREATE, вы можете использовать либо решение bash, либо, если вы пользователь Windows, - 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
источник
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

MrMesees
источник