I'm trying to get indexes, primary keys, and all constraints for a schema in PostgreSQL using standard sql. Most of the posts I'm finding for how to do this use SQL syntax that is specific to PostgreSQL (I'd like to avoid this). This references include the following:
How do I get the primary key(s) of a table from Postgres via plpgsql?
https://wiki.postgresql.org/wiki/Retrieve_primary_key_columns
https://dba.stackexchange.com/questions/214863/how-to-list-all-constraints-of-a-table-in-postgresql
The following is what I am currently using. Do the queries shown below correctly query for the indexes, primary keys, and constraints for a given schema?
-- indexes
select
ns.nspname as schema,
t.relname as table_name,
i.relname as index_name,
a.attname as column_name,
ixs.indexdef as index_definition
from
pg_index ix
join pg_class i on ix.indexrelid = i.oid
join pg_class t on ix.indrelid = t.oid and t.relkind = 'r'
join pg_namespace ns on t.relnamespace = ns.oid
join pg_indexes ixs on 1=1
and ns.nspname = ixs.schemaname
and t.relname = ixs.tablename
and i.relname = ixs.indexname
join pg_attribute a on 1=1
and a.attrelid = t.oid
and a.attnum = any(ix.indkey)
where 1=1
and ns.nspname = 'webapi'
order by
1,2,3,4
;
-- primary keys
select
ns.nspname as schema,
t.relname as table_name,
i.relname as index_name,
a.attname as column_name,
ixs.indexdef as index_definition
from
pg_index ix
join pg_class i on ix.indexrelid = i.oid
join pg_class t on ix.indrelid = t.oid and t.relkind = 'r'
join pg_namespace ns on t.relnamespace = ns.oid
join pg_indexes ixs on 1=1
and ns.nspname = ixs.schemaname
and t.relname = ixs.tablename
and i.relname = ixs.indexname
join pg_attribute a on 1=1
and a.attrelid = t.oid
and a.attnum = any(ix.indkey)
where 1=1
and ix.indisprimary = true
and ns.nspname = 'webapi'
order by
1,2,3,4
;
-- constraints
select
ns.nspname as schema,
t.relname as table_name,
con.conname as constraint_name,
a.attname as column_name,
con.contype as constraint_type,
con.*
from
pg_constraint con
join pg_class t on con.conrelid = t.oid
join pg_namespace ns on t.relnamespace = ns.oid
join pg_attribute a on 1=1
and a.attrelid = t.oid
and a.attnum = any(con.conkey)
where 1=1
and ns.nspname = 'webapi'
order by
1,2,3,4
;
I have a partitioned table out of main table using range.
CREATE TABLE public.partition1 PARTITION OF public.maintable
FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2050-01-01 00:00:00')
How can i get the Values range information using a query to postgres.
I have used a query that at least gives me information for the main and partitioned tables, but i cannot seem to find a way to access the value range
FROM ('2017-01-01 00:00:00') TO ('2050-01-01 00:00:00')
assigned to table partition1
Query used to get partition table information
WITH RECURSIVE partition_info
(relid,
relname,
relsize,
relispartition,
relkind) AS
(
(SELECT oid AS relid,
relname,
pg_relation_size(oid) AS relsize,
relispartition,
relkind
FROM pg_catalog.pg_class
WHERE relname = 'completedorders' AND
relkind = 'p')
UNION ALL
(SELECT
c.oid AS relid,
c.relname AS relname,
pg_relation_size(c.oid) AS relsize,
c.relispartition AS relispartition,
c.relkind AS relkind
FROM partition_info AS p,
pg_catalog.pg_inherits AS i,
pg_catalog.pg_class AS c
WHERE p.relid = i.inhparent AND
c.oid = i.inhrelid AND
c.relispartition = true)
)
SELECT * FROM partition_info;
The following query provides the information about partitions as well. From there on its just string manipulation in order to get further information.
Note: you will have to change the name of the table in the query.
with recursive inh as (
select i.inhrelid, null::text as parent
from pg_catalog.pg_inherits i
join pg_catalog.pg_class cl on i.inhparent = cl.oid
join pg_catalog.pg_namespace nsp on cl.relnamespace = nsp.oid
where nsp.nspname = 'public' ---<< change table schema here
and cl.relname = 'tablename' ---<< change table name here
union all
select i.inhrelid, (i.inhparent::regclass)::text
from inh
join pg_catalog.pg_inherits i on (inh.inhrelid = i.inhparent)
)
select c.relname as partition_name,
n.nspname as partition_schema,
pg_get_expr(c.relpartbound, c.oid, true) as partition_expression,
pg_get_expr(p.partexprs, c.oid, true) as sub_partition,
parent,
case p.partstrat
when 'l' then 'LIST'
when 'r' then 'RANGE'
end as sub_partition_strategy
from inh
join pg_catalog.pg_class c on inh.inhrelid = c.oid
join pg_catalog.pg_namespace n on c.relnamespace = n.oid
left join pg_partitioned_table p on p.partrelid = c.oid
order by n.nspname, c.relname
Is there a way to get a list of tables that would also be truncated by a TRUNCATE CASCADE in postgres?
So for example, assuming we have three tables:
a
b (depends on a)
c (depends on b)
TRUNCATE a CASCADE; would also truncate b and c. How could we check this ahead of time?
With the help of this answer you can get the foreign table name by this query
SELECT tc.constraint_name
,tc.table_name
,kcu.column_name
,ccu.table_name AS foreign_table_name
,ccu.column_name AS foreign_column_name
FROM
information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE constraint_type = 'FOREIGN KEY' AND ccu.table_name = 'a'
OR
Create a view with another query
CREATE VIEW vdepend_table
AS
SELECT s1.constraint_name
,s1.table_name
,s1.column_name
,s1.ordinal_position
,s2.table_name_ref
,s2.column_name_ref
,s2.ordinal_position_ref
FROM (
SELECT key_column_usage.constraint_name
,key_column_usage.table_name
,key_column_usage.column_name
,columns.ordinal_position
FROM information_schema.key_column_usage
JOIN information_schema.columns USING (
table_name
,column_name
)
) s1
JOIN (
SELECT constraint_column_usage.constraint_name
,constraint_column_usage.table_name AS table_name_ref
,constraint_column_usage.column_name AS column_name_ref
,cols_ref.ordinal_position AS ordinal_position_ref
FROM information_schema.constraint_column_usage
JOIN information_schema.columns cols_ref ON cols_ref.table_name::TEXT = constraint_column_usage.table_name::TEXT
AND cols_ref.column_name::TEXT = constraint_column_usage.column_name::TEXT
) s2 ON s1.constraint_name::TEXT = s2.constraint_name::TEXT
AND NOT s1.table_name::TEXT = s2.table_name_ref::TEXT;
usage:
select table_name from vdepend_table where table_name_ref='a'
I'm using version 8.2 Postresql
Have been trying to do this for days now, and I keep hitting the same wall. How to Display the following:
Source Table || Source_column || Target View || Target Column
Stg_table1|| customerid || vw_customer || Customer_details_id
The biggest problem I have is that I can't link the Alias names in the view, back to the Source_column. This is because the Ordinal position of the columns in the Source_table, is different to the columns in the Target_view.
I have tried using these scripts, with no luck (this joins the columns back on using information schema, and Ordinal Position)
select distinct
a.attname AS SOURCE
, a.attname::information_schema.sql_identifier AS column_name
--, format_type(a.atttypid, NULL) AS Source_Type
, d.refobjid::regclass AS Source_table
, r.ev_class::regclass AS Target_View
--, pt.Typname AS TYPE, a.*
--, c.Column_name AS Target
--, c.Data_type
from pg_attribute as a
join pg_depend as d on d.refobjid = a.attrelid and d.refobjsubid = a.attnum
join pg_rewrite as r on d.objid = r.oid
--join information_schema.columns c ON a.attnum = c.ordinal_position --and r.ev_class::regclass = c.table_schema||'.'||c.table_name
--join pg_type as pt on a.atttypid = pt.oid
JOIN pg_class
ON pg_class.oid = a.attrelid
AND a.attnum > 0
where
r.ev_class = 'abc.vw_customer'::regclass
and c.table_schema||'.'||c.table_name = 'vw_customer';
And I have also tried it this way:
SELECT distinct dependent.relname, pg_attribute.attname
, pg_attrdef.*
--, pg_attribute.attname::information_schema.sql_identifier AS column_name
--, pg_depend.objid, pg_attribute.*
FROM pg_depend
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
JOIN pg_class as dependee ON pg_rewrite.ev_class = dependee.oid
JOIN pg_class as dependent ON pg_depend.refobjid = dependent.oid
JOIN pg_attribute ON pg_depend.refobjid = pg_attribute.attrelid
--AND pg_depend.refobjsubid = pg_attribute.attnum
LEFT JOIN pg_attrdef ON pg_attribute.attrelid = pg_attrdef.adrelid AND pg_attribute.attnum = pg_attrdef.adnum
WHERE dependee.relname = 'vw_customer'
--and attname in ('Customer_details_id', 'customerid')
AND pg_attribute.attnum > 0
What is the query to get the list all index names, its column name and its table name of a postgresql database?
I have tried to get the list of all indexes in a db by using this query but how to get the list of indexes, its column names and its table names?
SELECT *
FROM pg_class, pg_index
WHERE pg_class.oid = pg_index.indexrelid
AND pg_class.oid IN (
SELECT indexrelid
FROM pg_index, pg_class
WHERE pg_class.oid=pg_index.indrelid
AND indisunique != 't'
AND indisprimary != 't'
AND relname !~ '^pg_');
This will output all indexes with details (extracted from my view definitions):
SELECT i.relname as indname,
i.relowner as indowner,
idx.indrelid::regclass,
am.amname as indam,
idx.indkey,
ARRAY(
SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
FROM generate_subscripts(idx.indkey, 1) as k
ORDER BY k
) as indkey_names,
idx.indexprs IS NOT NULL as indexprs,
idx.indpred IS NOT NULL as indpred
FROM pg_index as idx
JOIN pg_class as i
ON i.oid = idx.indexrelid
JOIN pg_am as am
ON i.relam = am.oid;
Optionally add an extra join to the end so as to trim the namespaces:
SELECT i.relname as indname,
i.relowner as indowner,
idx.indrelid::regclass,
am.amname as indam,
idx.indkey,
ARRAY(
SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
FROM generate_subscripts(idx.indkey, 1) as k
ORDER BY k
) as indkey_names,
idx.indexprs IS NOT NULL as indexprs,
idx.indpred IS NOT NULL as indpred
FROM pg_index as idx
JOIN pg_class as i
ON i.oid = idx.indexrelid
JOIN pg_am as am
ON i.relam = am.oid
JOIN pg_namespace as ns
ON ns.oid = i.relnamespace
AND ns.nspname = ANY(current_schemas(false));
More human friendly version of #Denis solution:
SELECT
U.usename AS user_name,
ns.nspname AS schema_name,
idx.indrelid :: REGCLASS AS table_name,
i.relname AS index_name,
idx.indisunique AS is_unique,
idx.indisprimary AS is_primary,
am.amname AS index_type,
idx.indkey,
ARRAY(
SELECT pg_get_indexdef(idx.indexrelid, k + 1, TRUE)
FROM
generate_subscripts(idx.indkey, 1) AS k
ORDER BY k
) AS index_keys,
(idx.indexprs IS NOT NULL) OR (idx.indkey::int[] #> array[0]) AS is_functional,
idx.indpred IS NOT NULL AS is_partial
FROM pg_index AS idx
JOIN pg_class AS i
ON i.oid = idx.indexrelid
JOIN pg_am AS am
ON i.relam = am.oid
JOIN pg_namespace AS NS ON i.relnamespace = NS.OID
JOIN pg_user AS U ON i.relowner = U.usesysid
WHERE NOT nspname LIKE 'pg%'; -- Excluding system tables
The Query to list all the indexes of a database
SELECT
tablename,
indexes [1],
indexes [2],
indexes [3],
indexes [4],
indexes [5],
indexes [6],
indexes [7],
indexes [8],
indexes [9],
indexes [10]
FROM (SELECT
tablename,
array_agg(indexname) AS indexes
FROM pg_indexes
WHERE schemaname = 'public'
GROUP BY tablename) as sub;
Here's a version that simplifies things compared to other answers by
avoiding nested selects
avoiding built-in functions (maybe hard to remember)
using LATERAL and UNNEST(...) WITH ORDINALITY features available in later PostgreSQL versions (9.4+)
SELECT
tnsp.nspname AS schema_name,
trel.relname AS table_name,
irel.relname AS index_name,
array_agg (
a.attname
|| ' ' || CASE o.option & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END
|| ' ' || CASE o.option & 2 WHEN 2 THEN 'NULLS FIRST' ELSE 'NULLS LAST' END
ORDER BY c.ordinality
) AS columns
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
CROSS JOIN LATERAL unnest (i.indkey) WITH ORDINALITY AS c (colnum, ordinality)
LEFT JOIN LATERAL unnest (i.indoption) WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality
JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
GROUP BY tnsp.nspname, trel.relname, irel.relname
If you are also interested in index size, you may use this query from the PostgreSQL Wiki.
SELECT
t.tablename,
indexname,
c.reltuples AS num_rows,
pg_size_pretty(pg_relation_size(quote_ident(t.tablename)::text)) AS table_size,
pg_size_pretty(pg_relation_size(quote_ident(indexrelname)::text)) AS index_size,
CASE WHEN indisunique THEN 'Y'
ELSE 'N'
END AS UNIQUE,
idx_scan AS number_of_scans,
idx_tup_read AS tuples_read,
idx_tup_fetch AS tuples_fetched
FROM pg_tables t
LEFT OUTER JOIN pg_class c ON t.tablename=c.relname
LEFT OUTER JOIN
( SELECT c.relname AS ctablename, ipg.relname AS indexname, x.indnatts AS number_of_columns, idx_scan, idx_tup_read, idx_tup_fetch, indexrelname, indisunique FROM pg_index x
JOIN pg_class c ON c.oid = x.indrelid
JOIN pg_class ipg ON ipg.oid = x.indexrelid
JOIN pg_stat_all_indexes psai ON x.indexrelid = psai.indexrelid )
AS foo
ON t.tablename = foo.ctablename
WHERE t.schemaname='public'
ORDER BY 1,2;
For Non-Composite Indexes
select t.relname,i.relname ,
STRING_AGG(pga.attname||'', ','order by i.relname,pga.attnum) as columnName
from pg_class t inner join pg_index ix
on t.oid = ix.indrelid
inner join pg_class i
on i.oid = ix.indexrelid
inner join pg_attribute pga
on
pga.attrelid = i.oid
inner join pg_indexes pgidx
on pgidx.indexname=i.relname
where
t.relkind = 'r'
and pgidx.schemaname='asit_cm'
and t.relname ='accessory'
group by t.relname,i.relname having count(*)=1
For Composite Indexes
select t.relname,i.relname ,
STRING_AGG(pga.attname||'', ','order by i.relname,pga.attnum) as columnName
from pg_class t inner join pg_index ix
on t.oid = ix.indrelid
inner join pg_class i
on i.oid = ix.indexrelid
inner join pg_attribute pga
on
pga.attrelid = i.oid
inner join pg_indexes pgidx
on pgidx.indexname=i.relname
where
t.relkind = 'r'
and pgidx.schemaname='asit_cm'
and t.relname ='accessory'
group by t.relname,i.relname having count(*)=1