PostgreSQL: How to query all tables with exact number of lines [duplicate] - postgresql
I'm looking for a way to find the row count for all my tables in Postgres. I know I can do this one table at a time with:
SELECT count(*) FROM table_name;
but I'd like to see the row count for all the tables and then order by that to get an idea of how big all my tables are.
There's three ways to get this sort of count, each with their own tradeoffs.
If you want a true count, you have to execute the SELECT statement like the one you used against each table. This is because PostgreSQL keeps row visibility information in the row itself, not anywhere else, so any accurate count can only be relative to some transaction. You're getting a count of what that transaction sees at the point in time when it executes. You could automate this to run against every table in the database, but you probably don't need that level of accuracy or want to wait that long.
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 rows_n DESC;
The second approach notes that the statistics collector tracks roughly how many rows are "live" (not deleted or obsoleted by later updates) at any time. This value can be off by a bit under heavy activity, but is generally a good estimate:
SELECT schemaname,relname,n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
That can also show you how many rows are dead, which is itself an interesting number to monitor.
The third way is to note that the system ANALYZE command, which is executed by the autovacuum process regularly as of PostgreSQL 8.3 to update table statistics, also computes a row estimate. You can grab that one like this:
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;
Which of these queries is better to use is hard to say. Normally I make that decision based on whether there's more useful information I also want to use inside of pg_class or inside of pg_stat_user_tables. For basic counting purposes just to see how big things are in general, either should be accurate enough.
Here is a solution that does not require functions to get an accurate count for each table:
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 will run the passed SQL query and return an XML with the result (the row count for that table). The outer xpath() will then extract the count information from that xml and convert it to a number
The derived table is not really necessary, but makes the xpath() a bit easier to understand - otherwise the whole query_to_xml() would need to be passed to the xpath() function.
To get estimates, see Greg Smith's answer.
To get exact counts, the other answers so far are plagued with some issues, some of them serious (see below). Here's a version that's hopefully better:
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;
It takes a schema name as parameter, or public if no parameter is given.
To work with a specific list of schemas or a list coming from a query without modifying the function, it can be called from within a query like this:
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;
This produces a 3-columns output with the schema, the table and the rows count.
Now here are some issues in the other answers that this function avoids:
Table and schema names shouldn't be injected into executable SQL without being quoted, either with quote_ident or with the more modern format() function with its %I format string. Otherwise some malicious person may name their table tablename;DROP TABLE other_table which is perfectly valid as a table name.
Even without the SQL injection and funny characters problems, table name may exist in variants differing by case. If a table is named ABCD and another one abcd, the SELECT count(*) FROM... must use a quoted name otherwise it will skip ABCD and count abcd twice. The %I of format does this automatically.
information_schema.tables lists custom composite types in addition to tables, even when table_type is 'BASE TABLE' (!). As a consequence, we can't iterate oninformation_schema.tables, otherwise we risk having select count(*) from name_of_composite_type and that would fail. OTOH pg_class where relkind='r' should always work fine.
The type of COUNT() is bigint, not int. Tables with more than 2.15 billion rows may exist (running a count(*) on them is a bad idea, though).
A permanent type need not to be created for a function to return a resultset with several columns. RETURNS TABLE(definition...) is a better alternative.
The hacky, practical answer for people trying to evaluate which Heroku plan they need and can't wait for heroku's slow row counter to refresh:
Basically you want to run \dt in psql, copy the results to your favorite text editor (it will look like this:
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
), then run a regex search and replace like this:
^[^|]*\|\s+([^|]*?)\s+\| table \|.*$
to:
select '\1', count(*) from \1 union/g
which will yield you something very similar to this:
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
;
(You'll need to remove the last union and add the semicolon at the end manually)
Run it in psql and you're done.
?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
[..]
If you don't mind potentially stale data, you can access the same statistics used by the query optimizer.
Something like:
SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;
Simple Two Steps: (Note : No need to change anything - just copy paste)
1. create function
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. Run this query to get rows count for all the tables
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;
or
To get rows counts tablewise
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;
Not sure if an answer in bash is acceptable to you, but 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
Extracted from my Comment in the answer from GregSmith to make it more readable:
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;
Thanks to #a_horse_with_no_name
I usually don't rely on statistics, especially in 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;
This worked for me
SELECT schemaname,relname,n_live_tup FROM pg_stat_user_tables ORDER BY
n_live_tup DESC;
I don't remember the URL from where I collected this. But hope this should help you:
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;
Executing select count_em_all(); should get you row count of all your tables.
I made a small variation to include all tables, also for non-public tables.
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;
use select count_em_all(); to call it.
Hope you find this usefull.
Paul
You Can use this query to generate all tablenames with their counts
select ' select '''|| tablename ||''', count(*) from ' || tablename ||'
union' from pg_tables where schemaname='public';
the result from the above query will be
select 'dim_date', count(*) from dim_date union
select 'dim_store', count(*) from dim_store union
select 'dim_product', count(*) from dim_product union
select 'dim_employee', count(*) from dim_employee union
You'll need to remove the last union and add the semicolon at the end !!
select 'dim_date', count(*) from dim_date union
select 'dim_store', count(*) from dim_store union
select 'dim_product', count(*) from dim_product union
select 'dim_employee', count(*) from dim_employee **;**
RUN !!!
Here is a much simpler way.
tables="$(echo '\dt' | psql -U "${PGUSER}" | tail -n +4 | head -n-2 | tr -d ' ' | cut -d '|' -f2)"
for table in $tables; do
printf "%s: %s\n" "$table" "$(echo "SELECT COUNT(*) FROM $table;" | psql -U "${PGUSER}" | tail -n +3 | head -n-2 | tr -d ' ')"
done
output should look like this
auth_group: 0
auth_group_permissions: 0
auth_permission: 36
auth_user: 2
auth_user_groups: 0
auth_user_user_permissions: 0
authtoken_token: 2
django_admin_log: 0
django_content_type: 9
django_migrations: 22
django_session: 0
mydata_table1: 9011
mydata_table2: 3499
you can update the psql -U "${PGUSER}" portion as needed to access your database
note that the head -n-2 syntax may not work in macOS, you could probably just use a different implementation there
Tested on psql (PostgreSQL) 11.2 under CentOS 7
if you want it sorted by table, then just wrap it with sort
for table in $tables; do
printf "%s: %s\n" "$table" "$(echo "SELECT COUNT(*) FROM $table;" | psql -U "${PGUSER}" | tail -n +3 | head -n-2 | tr -d ' ')"
done | sort -k 2,2nr
output;
mydata_table1: 9011
mydata_table2: 3499
auth_permission: 36
django_migrations: 22
django_content_type: 9
authtoken_token: 2
auth_user: 2
auth_group: 0
auth_group_permissions: 0
auth_user_groups: 0
auth_user_user_permissions: 0
django_admin_log: 0
django_session: 0
I like Daniel Vérité's answer.
But when you can't use a CREATE statement you can either use a bash solution or, if you're a windows user, a powershell one:
# 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"
}
I wanted the total from all tables + a list of tables with their counts. A little like a performance chart of where most time was spent
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
You could of course put a LIMIT clause on the results in this version too so that you get the largest n offenders as well as a total.
One thing that should be noted about this is that you need to let it sit for a while after bulk imports. I tested this by just adding 5000 rows to a database across several tables using real import data. It showed 1800 records for about a minute (probably a configurable window)
This is based from https://stackoverflow.com/a/2611745/1548557 work, so thank you and recognition to that for the query to use within the CTE
If you're in the psql shell, using \gexec allows you to execute the syntax described in syed's answer and Aur's answer without manual edits in an external text editor.
with x (y) as (
select
'select count(*), '''||
tablename||
''' as "tablename" from '||
tablename||' '
from pg_tables
where schemaname='public'
)
select
string_agg(y,' union all '||chr(10)) || ' order by tablename'
from x \gexec
Note, string_agg() is used both to delimit union all between statements and to smush the separated datarows into a single unit to be passed into the buffer.
\gexec
Sends the current query buffer to the server, then treats each column of each row of the query's output (if any) as a SQL statement to be executed.
below query will give us row count and size for each table
select table_schema, table_name,
pg_relation_size('"'||table_schema||'"."'||table_name||'"')/1024/1024 size_MB,
(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 information_schema.tables
order by size_MB desc;
Related
Execute result of a PostgreSQL query again to get the final result set?
I'm looking for a way to get the list of all json attributes across all my PostgreSQL tables dynamically. I have Query 1 which would generate a list of sql statements, and then run that sql statements to get the final output all in one go (like the dynamic SQL concept in SQL server). Query 1 looks like this : create temporary table test (ordr int, field varchar(1000)); -- Step 1 Create temp table to insert all table/col/json attrbute info insert into test(ordr,field) select 0 ordr,'create temporary table temp_table ( table_schema varchar(200) ,table_name varchar(200) ,column_name varchar(200) ,json_attribute varchar(200) ,data_type varchar(50) );' union -- Non json type columns select 1 ordr, 'insert into temp_table(table_name, column_name,data_type,json_attribute)' union -- Json columns with data like json object select 3 ordr, concat('select distinct ''', t.table_name, ''' tbl, ''', c.column_name, ''' col, ''' , c.data_type,''' data_type, ' ,'jsonb_object_keys(', c.column_name, ') json_attribute', ' from ', t.table_name, ' where jsonb_typeof(' , c.column_name, ') = ''object'' union') AS field from information_schema.tables t join information_schema.columns c on c.table_name = t.table_name where t.table_schema not in ('information_schema', 'pg_catalog') --and table_type = 'BASE TABLE' and c.data_type ='jsonb'; --final sql statements to build temp table --copy all the column "txt" to a separate window and execute it, it will create a temp table "temp_table" which will have all tables/cols/json attributes select ordr ,(case when t.ordr = (select max(t2.ordr) from test t2) then replace(field,'union','') else field end) txt from test t union select 9999, ';select * from temp_table;' order by 1 ; Query 1 output : This is a list of sql statements I'm looking for a way to run the Query 1 & Query 1 output which would get me the final output all in one go. Any lead or guidance will be really appreciated.
Get all sequences with current values
I have the following query that gets all sequences and their schemas: SELECT sequence_schema as schema, sequence_name as sequence FROM information_schema.sequences WHERE sequence_schema NOT IN ('topology', 'tiger') ORDER BY 1, 2 I would like to get the current value of each sequence name with something like select last_value from [sequence];. I have tried the following (and a couple variations), but it doesn't work because the syntax isn't correct: DO $$ BEGIN EXECUTE sequence_schema as schema, sequence_name as sequence, last_value FROM information_schema.sequences LEFT JOIN ( EXECUTE 'SELECT last_value FROM ' || schema || '.' || sequence ) tmp ORDER BY 1, 2; END $$; I've found some solutions that create functions to execute text or piece together a query inside a function and return the result, but I would prefer to have a single query that I can run and modify however I like.
In Postgres 12, you can use pg_sequences: select schemaname as schema, sequencename as sequence, last_value from pg_sequences
You can rely on the function pg_sequence_last_value SELECT nspname as schema, relname AS sequence_name, coalesce(pg_sequence_last_value(s.oid), 0) AS seq_last_value FROM pg_class AS s JOIN pg_depend AS d ON d.objid = s.oid JOIN pg_attribute a ON d.refobjid = a.attrelid AND d.refobjsubid = a.attnum JOIN pg_namespace nsp ON s.relnamespace = nsp.oid WHERE s.relkind = 'S' AND d.refclassid = 'pg_class'::regclass AND d.classid = 'pg_class'::regclass AND nspname NOT IN ('topology', 'tiger') ORDER BY 1,2 DESC;
Here's a solution that doesn't rely on pg_sequences or pg_sequence_last_value: CREATE OR REPLACE FUNCTION get_sequences() RETURNS TABLE ( last_value bigint, sequence_schema text, sequence_name text ) LANGUAGE plpgsql AS $func$ DECLARE s RECORD; BEGIN FOR s IN SELECT t.sequence_schema, t.sequence_name FROM information_schema.sequences t LOOP RETURN QUERY EXECUTE format( 'SELECT last_value, ''%1$s''::text, ''%2$s''::text FROM %1$I.%2$I', s.sequence_schema, s.sequence_name ); END LOOP; END; $func$; SELECT * FROM get_sequences(); That'll output a table like this: last_value | sequence_schema | sequence_name ------------+-----------------+------------------------------------------------------- 1 | public | contact_infos_id_seq 1 | media | photos_id_seq 2006 | company | companies_id_seq 2505 | public | houses_id_seq 1 | public | purchase_numbers_id_seq ... etc The other answers will only work if you are on a modern version of Postgres (I believe 10 or greater).
PostgreSQL - pull column name, data type, and sample value from database table
I am wondering if it is possible to extract the column name, data type, and one sample value from a database table with a single PostgreSQL query. I'm aiming to do this for all columns of one table. Feels like a variable is needed for the column name so you can use it when querying the table for the sample value but Postgres doesn't support this in plain SQL statement (How to declare a variable in a PostgreSQL query). I can achieve this through hard-coding a single value but any suggestions on whether this is possible to do for each column of the table (join each one using its name in a select statement to obtain the single sample value)? column | data_type | sample_val -------------------------------- foo_col1 | text | NULL foo_col2 | text | 'foo_val2' foo_col3 | text | NULL select column_name as column, data_type, sample_val from information_schema.columns t1 join pg_class t2 on (t1.table_name = t2.relname) left outer join pg_description t3 on (t2.oid = t3.objoid and t3.objsubid = t1.ordinal_position) left outer join (select CAST('foo_col2' AS text) as foo_col2, foo_col2 as sample_val from foo_schema.foo_table limit 1) n2 on (n2.foo_col2 = column_name) where table_schema = 'foo_schema' and table_name = 'foo_table' order by ordinal_position
You can use a variation of row count for all tables for this: select c.table_schema, c.table_name, c.column_name, c.data_type, (xpath('/table/row/'||column_name||'/text()', query_to_xml(format('select %I from %I.%I limit 1', c.column_name, c.table_schema, c.table_name), true, false, '')))[1]::text as sample_value from information_schema.columns c where table_schema = 'foo_schema'; query_to_xml() will run the query and format the result as XML. The xpath() function then extracts that column value from the XML. This is quite expensive as the query is run once for each column, not once for each table. Note that the sample values might not be from the same row. You can optimize this by just running one query per table and then joining that result back to the columns: with samples as ( select table_schema, table_name, query_to_xml(format('select * from %I.%I limit 1', table_schema, table_name), true, false, '') as sample_row from information_schema.tables where table_schema = 'foo_schema' ) select c.table_schema, c.table_name, c.column_name, c.data_type, (xpath('/table/row/'||column_name||'/text()', s.sample_row))[1]::text as sample_value from information_schema.columns c join samples s on (s.table_schema, s.table_name) = (c.table_schema, c.table_name); With the above, all sample values are from the same row.
Postgres get the exact row count of all tables without using vacuum [duplicate]
I'm looking for a way to find the row count for all my tables in Postgres. I know I can do this one table at a time with: SELECT count(*) FROM table_name; but I'd like to see the row count for all the tables and then order by that to get an idea of how big all my tables are.
There's three ways to get this sort of count, each with their own tradeoffs. If you want a true count, you have to execute the SELECT statement like the one you used against each table. This is because PostgreSQL keeps row visibility information in the row itself, not anywhere else, so any accurate count can only be relative to some transaction. You're getting a count of what that transaction sees at the point in time when it executes. You could automate this to run against every table in the database, but you probably don't need that level of accuracy or want to wait that long. 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 rows_n DESC; The second approach notes that the statistics collector tracks roughly how many rows are "live" (not deleted or obsoleted by later updates) at any time. This value can be off by a bit under heavy activity, but is generally a good estimate: SELECT schemaname,relname,n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC; That can also show you how many rows are dead, which is itself an interesting number to monitor. The third way is to note that the system ANALYZE command, which is executed by the autovacuum process regularly as of PostgreSQL 8.3 to update table statistics, also computes a row estimate. You can grab that one like this: 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; Which of these queries is better to use is hard to say. Normally I make that decision based on whether there's more useful information I also want to use inside of pg_class or inside of pg_stat_user_tables. For basic counting purposes just to see how big things are in general, either should be accurate enough.
Here is a solution that does not require functions to get an accurate count for each table: 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 will run the passed SQL query and return an XML with the result (the row count for that table). The outer xpath() will then extract the count information from that xml and convert it to a number The derived table is not really necessary, but makes the xpath() a bit easier to understand - otherwise the whole query_to_xml() would need to be passed to the xpath() function.
To get estimates, see Greg Smith's answer. To get exact counts, the other answers so far are plagued with some issues, some of them serious (see below). Here's a version that's hopefully better: 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; It takes a schema name as parameter, or public if no parameter is given. To work with a specific list of schemas or a list coming from a query without modifying the function, it can be called from within a query like this: 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; This produces a 3-columns output with the schema, the table and the rows count. Now here are some issues in the other answers that this function avoids: Table and schema names shouldn't be injected into executable SQL without being quoted, either with quote_ident or with the more modern format() function with its %I format string. Otherwise some malicious person may name their table tablename;DROP TABLE other_table which is perfectly valid as a table name. Even without the SQL injection and funny characters problems, table name may exist in variants differing by case. If a table is named ABCD and another one abcd, the SELECT count(*) FROM... must use a quoted name otherwise it will skip ABCD and count abcd twice. The %I of format does this automatically. information_schema.tables lists custom composite types in addition to tables, even when table_type is 'BASE TABLE' (!). As a consequence, we can't iterate oninformation_schema.tables, otherwise we risk having select count(*) from name_of_composite_type and that would fail. OTOH pg_class where relkind='r' should always work fine. The type of COUNT() is bigint, not int. Tables with more than 2.15 billion rows may exist (running a count(*) on them is a bad idea, though). A permanent type need not to be created for a function to return a resultset with several columns. RETURNS TABLE(definition...) is a better alternative.
The hacky, practical answer for people trying to evaluate which Heroku plan they need and can't wait for heroku's slow row counter to refresh: Basically you want to run \dt in psql, copy the results to your favorite text editor (it will look like this: 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 ), then run a regex search and replace like this: ^[^|]*\|\s+([^|]*?)\s+\| table \|.*$ to: select '\1', count(*) from \1 union/g which will yield you something very similar to this: 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 ; (You'll need to remove the last union and add the semicolon at the end manually) Run it in psql and you're done. ?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 [..]
If you don't mind potentially stale data, you can access the same statistics used by the query optimizer. Something like: SELECT relname, n_tup_ins - n_tup_del as rowcount FROM pg_stat_all_tables;
Simple Two Steps: (Note : No need to change anything - just copy paste) 1. create function 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. Run this query to get rows count for all the tables 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; or To get rows counts tablewise 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;
Not sure if an answer in bash is acceptable to you, but 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
Extracted from my Comment in the answer from GregSmith to make it more readable: 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; Thanks to #a_horse_with_no_name
I usually don't rely on statistics, especially in 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;
This worked for me SELECT schemaname,relname,n_live_tup FROM pg_stat_user_tables ORDER BY n_live_tup DESC;
I don't remember the URL from where I collected this. But hope this should help you: 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; Executing select count_em_all(); should get you row count of all your tables.
I made a small variation to include all tables, also for non-public tables. 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; use select count_em_all(); to call it. Hope you find this usefull. Paul
You Can use this query to generate all tablenames with their counts select ' select '''|| tablename ||''', count(*) from ' || tablename ||' union' from pg_tables where schemaname='public'; the result from the above query will be select 'dim_date', count(*) from dim_date union select 'dim_store', count(*) from dim_store union select 'dim_product', count(*) from dim_product union select 'dim_employee', count(*) from dim_employee union You'll need to remove the last union and add the semicolon at the end !! select 'dim_date', count(*) from dim_date union select 'dim_store', count(*) from dim_store union select 'dim_product', count(*) from dim_product union select 'dim_employee', count(*) from dim_employee **;** RUN !!!
Here is a much simpler way. tables="$(echo '\dt' | psql -U "${PGUSER}" | tail -n +4 | head -n-2 | tr -d ' ' | cut -d '|' -f2)" for table in $tables; do printf "%s: %s\n" "$table" "$(echo "SELECT COUNT(*) FROM $table;" | psql -U "${PGUSER}" | tail -n +3 | head -n-2 | tr -d ' ')" done output should look like this auth_group: 0 auth_group_permissions: 0 auth_permission: 36 auth_user: 2 auth_user_groups: 0 auth_user_user_permissions: 0 authtoken_token: 2 django_admin_log: 0 django_content_type: 9 django_migrations: 22 django_session: 0 mydata_table1: 9011 mydata_table2: 3499 you can update the psql -U "${PGUSER}" portion as needed to access your database note that the head -n-2 syntax may not work in macOS, you could probably just use a different implementation there Tested on psql (PostgreSQL) 11.2 under CentOS 7 if you want it sorted by table, then just wrap it with sort for table in $tables; do printf "%s: %s\n" "$table" "$(echo "SELECT COUNT(*) FROM $table;" | psql -U "${PGUSER}" | tail -n +3 | head -n-2 | tr -d ' ')" done | sort -k 2,2nr output; mydata_table1: 9011 mydata_table2: 3499 auth_permission: 36 django_migrations: 22 django_content_type: 9 authtoken_token: 2 auth_user: 2 auth_group: 0 auth_group_permissions: 0 auth_user_groups: 0 auth_user_user_permissions: 0 django_admin_log: 0 django_session: 0
I like Daniel Vérité's answer. But when you can't use a CREATE statement you can either use a bash solution or, if you're a windows user, a powershell one: # 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" }
I wanted the total from all tables + a list of tables with their counts. A little like a performance chart of where most time was spent 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 You could of course put a LIMIT clause on the results in this version too so that you get the largest n offenders as well as a total. One thing that should be noted about this is that you need to let it sit for a while after bulk imports. I tested this by just adding 5000 rows to a database across several tables using real import data. It showed 1800 records for about a minute (probably a configurable window) This is based from https://stackoverflow.com/a/2611745/1548557 work, so thank you and recognition to that for the query to use within the CTE
If you're in the psql shell, using \gexec allows you to execute the syntax described in syed's answer and Aur's answer without manual edits in an external text editor. with x (y) as ( select 'select count(*), '''|| tablename|| ''' as "tablename" from '|| tablename||' ' from pg_tables where schemaname='public' ) select string_agg(y,' union all '||chr(10)) || ' order by tablename' from x \gexec Note, string_agg() is used both to delimit union all between statements and to smush the separated datarows into a single unit to be passed into the buffer. \gexec Sends the current query buffer to the server, then treats each column of each row of the query's output (if any) as a SQL statement to be executed.
below query will give us row count and size for each table select table_schema, table_name, pg_relation_size('"'||table_schema||'"."'||table_name||'"')/1024/1024 size_MB, (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 information_schema.tables order by size_MB desc;
How can I get a list of all functions stored in the database of a particular schema in PostgreSQL?
I want to be able to connect to a PostgreSQL database and find all of the functions for a particular schema. My thought was that I could make some query to pg_catalog or information_schema and get a list of all functions, but I can't figure out where the names and parameters are stored. I'm looking for a query that will give me the function name and the parameter types it takes (and what order it takes them in). Is there a way to do this?
\df <schema>.* in psql gives the necessary information. To see the query that's used internally connect to a database with psql and supply an extra "-E" (or "--echo-hidden") option and then execute the above command.
After some searching, I was able to find the information_schema.routines table and the information_schema.parameters tables. Using those, one can construct a query for this purpose. LEFT JOIN, instead of JOIN, is necessary to retrieve functions without parameters. SELECT routines.routine_name, parameters.data_type, parameters.ordinal_position FROM information_schema.routines LEFT JOIN information_schema.parameters ON routines.specific_name=parameters.specific_name WHERE routines.specific_schema='my_specified_schema_name' ORDER BY routines.routine_name, parameters.ordinal_position;
If any one is interested here is what query is executed by psql on postgres 9.1: SELECT n.nspname as "Schema", p.proname as "Name", pg_catalog.pg_get_function_result(p.oid) as "Result data type", pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types", CASE WHEN p.proisagg THEN 'agg' WHEN p.proiswindow THEN 'window' WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger' ELSE 'normal' END as "Type" FROM pg_catalog.pg_proc p LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace WHERE pg_catalog.pg_function_is_visible(p.oid) AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' ORDER BY 1, 2, 4; You can get what psql runs for a backslash command by running psql with the -E flag.
There's a handy function, oidvectortypes, that makes this a lot easier. SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE ns.nspname = 'my_namespace'; Credit to Leo Hsu and Regina Obe at Postgres Online for pointing out oidvectortypes. I wrote similar functions before, but used complex nested expressions that this function gets rid of the need for. See related answer. (edit in 2016) Summarizing typical report options: -- Compact: SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) -- With result data type: SELECT format( '%I.%I(%s)=%s', ns.nspname, p.proname, oidvectortypes(p.proargtypes), pg_get_function_result(p.oid) ) -- With complete argument description: SELECT format('%I.%I(%s)', ns.nspname, p.proname, pg_get_function_arguments(p.oid)) -- ... and mixing it. -- All with the same FROM clause: FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE ns.nspname = 'my_namespace'; NOTICE: use p.proname||'_'||p.oid AS specific_name to obtain unique names, or to JOIN with information_schema tables — see routines and parameters at #RuddZwolinski's answer. The function's OID (see pg_catalog.pg_proc) and the function's specific_name (see information_schema.routines) are the main reference options to functions. Below, some useful functions in reporting and other contexts. --- --- --- --- --- --- Useful overloads: CREATE FUNCTION oidvectortypes(p_oid int) RETURNS text AS $$ SELECT oidvectortypes(proargtypes) FROM pg_proc WHERE oid=$1; $$ LANGUAGE SQL IMMUTABLE; CREATE FUNCTION oidvectortypes(p_specific_name text) RETURNS text AS $$ -- Extract OID from specific_name and use it in oidvectortypes(oid). SELECT oidvectortypes(proargtypes) FROM pg_proc WHERE oid=regexp_replace($1, '^.+?([^_]+)$', '\1')::int; $$ LANGUAGE SQL IMMUTABLE; CREATE FUNCTION pg_get_function_arguments(p_specific_name text) RETURNS text AS $$ -- Extract OID from specific_name and use it in pg_get_function_arguments. SELECT pg_get_function_arguments(regexp_replace($1, '^.+?([^_]+)$', '\1')::int) $$ LANGUAGE SQL IMMUTABLE; --- --- --- --- --- --- User customization: CREATE FUNCTION pg_get_function_arguments2(p_specific_name text) RETURNS text AS $$ -- Example of "special layout" version. SELECT trim(array_agg( op||'-'||dt )::text,'{}') FROM ( SELECT data_type::text as dt, ordinal_position as op FROM information_schema.parameters WHERE specific_name = p_specific_name ORDER BY ordinal_position ) t $$ LANGUAGE SQL IMMUTABLE;
Run below SQL query to create a view which will show all functions: CREATE OR REPLACE VIEW show_functions AS SELECT routine_name FROM information_schema.routines WHERE routine_type='FUNCTION' AND specific_schema='public';
Get List of function_schema and function_name... SELECT n.nspname AS function_schema, p.proname AS function_name FROM pg_proc p LEFT JOIN pg_namespace n ON p.pronamespace = n.oid WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') ORDER BY function_schema, function_name;
Is a good idea named the functions with commun alias on the first words for filtre the name with LIKE Example with public schema in Postgresql 9.4, be sure to replace with his scheme SELECT routine_name FROM information_schema.routines WHERE routine_type='FUNCTION' AND specific_schema='public' AND routine_name LIKE 'aliasmyfunctions%';
Example: perfdb-# \df information_schema.*; List of functions Schema | Name | Result data type | Argument data types | Type information_schema | _pg_char_max_length | integer | typid oid, typmod integer | normal information_schema | _pg_char_octet_length | integer | typid oid, typmod integer | normal information_schema | _pg_datetime_precision| integer | typid oid, typmod integer | normal ..... information_schema | _pg_numeric_scale | integer | typid oid, typmod integer | normal information_schema | _pg_truetypid | oid | pg_attribute, pg_type | normal information_schema | _pg_truetypmod | integer | pg_attribute, pg_type | normal (11 rows)
This function returns all user defined routines in current database. SELECT pg_get_functiondef(p.oid) FROM pg_proc p INNER JOIN pg_namespace ns ON p.pronamespace = ns.oid WHERE ns.nspname = 'public';
The joins in abovementioned answers returns not only input parameters, but also outputs. So it is necessary to specify also parameter_mode. This select will return list of functions with its input parametrs (if having some). Postgres 14. select r.routine_name, array_agg(p.data_type::text order by p.ordinal_position) from information_schema.routines r left join information_schema.parameters p on r.specific_name = p.specific_name where r.routine_type = 'FUNCTION' and r.specific_schema = 'schema_name' and (p.parameter_mode = 'IN' or p.parameter_mode is null) group by r.routine_name order by r.routine_name;