345

Is there a way using SQL to list all foreign keys for a given table? I know the table name / schema and I can plug that in.

2
  • I suggest to use @Magnus' answer. Simplest, cleanest, fastest. Commented Oct 10, 2013 at 15:04
  • @ErwinBrandstetter However, the accepted answer gives columns and table names directly, so we don't need to parse them. It would be nice if Magnus' answer could do that too. Commented Jul 14, 2020 at 21:00

32 Answers 32

585

You can do this via the information_schema tables. For example:

SELECT tc.table_schema, tc.constraint_name, tc.table_name, kcu.column_name, ccu.table_schema AS foreign_table_schema, 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 AND tc.table_schema = kcu.table_schema JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema='myschema' AND tc.table_name='mytable'; 

If you need to go the other way, i.e., find all places a table is used as a foreign table, you can replace the last two conditions with:

 AND ccu.table_schema='myschema' AND ccu.table_name='mytable'; 
Sign up to request clarification or add additional context in comments.

14 Comments

table_name='mytable' should be tc.table_name='mytable' or else it throws an ambiguous error
+1, very helpful. To make the query more robust it should probably join on constraint_schema as well, since it's possible for two schemas to have constraints with the same name. Something like: FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu USING (constraint_schema, constraint_name) JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name)
This breaks when there are several columns in a constraint, doesn't it? There seems to be no proper way to associate pk columns with fk columns using information_schema BTW.
It indeed breaks with more than one column in constraint. For Postgres, there is a way of getting this information from the pg_catalog schema. See my answer below.
The query is wrong. It assumes that constraint names cannot repeat, which is false. Constraints with the same name can exist in different namespaces. You are using constraint_name to make the join. Also joinning on both constraint_name and schema name won't work since you are not sure the two constraints are the same. The only option is going for pg_constraints, pg_class etc. using oids to join. Postgres' ANSI catalog is only there for compliance but it's flawed. pg_catalog is the way to go. The correct answer is here dba.stackexchange.com/questions/36979/retrieving-all-pk-and-fk
|
92

Issue \d+ tablename on PostgreSQL prompt, in addition to showing table column's data types it'll show the indexes and foreign keys.

2 Comments

Sorry didn't notice my comment was cropped. If you could at least try it once, you'd see the foreign key mappings are displayed as well.
At least as of 12.5, \d is sufficient but \d+ will show additional (non-FK information) e.g. column Stats target, Description
89

psql does this, and if you start psql with:

psql -E 

it will show you exactly what query is executed. In the case of finding foreign keys, it's:

SELECT conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r WHERE r.conrelid = '16485' AND r.contype = 'f' ORDER BY 1 

In this case, 16485 is the oid of the table I'm looking at - you can get that one by just casting your tablename to regclass like:

WHERE r.conrelid = 'mytable'::regclass 

Schema-qualify the table name if it's not unique (or the first in your search_path):

WHERE r.conrelid = 'myschema.mytable'::regclass 

7 Comments

This is very handy! Postgres seems to have a million little functions like this that make everything simpler. Now how to remember them?
@Phil: You only need a general idea. Let the manual remember the rest.
to list all foreign keys targeting a table: SELECT conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r WHERE r.confrelid = 'myschema.mytable'::regclass;
@ErwinBrandstetter how do i do to get a foreign table name?
I don't get it, what command should be use ? psql -E -U username -d database ThenWHAT ?
|
57

Ollyc's answer is good as it is not Postgres-specific, however, it breaks down when the foreign key references more than one column. The following query works for arbitrary number of columns but it relies heavily on Postgres extensions:

select att2.attname as "child_column", cl.relname as "parent_table", att.attname as "parent_column", conname from (select unnest(con1.conkey) as "parent", unnest(con1.confkey) as "child", con1.confrelid, con1.conrelid, con1.conname from pg_class cl join pg_namespace ns on cl.relnamespace = ns.oid join pg_constraint con1 on con1.conrelid = cl.oid where cl.relname = 'child_table' and ns.nspname = 'child_schema' and con1.contype = 'f' ) con join pg_attribute att on att.attrelid = con.confrelid and att.attnum = con.child join pg_class cl on cl.oid = con.confrelid join pg_attribute att2 on att2.attrelid = con.conrelid and att2.attnum = con.parent 

9 Comments

before 8.4 the function unnest has to be created at first. wiki.postgresql.org/wiki/Array_Unnest
Where does one insert the table name into this query? Entered verbatim the above returns 0 rows on my PSQL DB that has tens of foreign keys.
You replace 'child_table' and 'child_schema' with the names of the table and its schema
I think that 'child_table' is in fact 'parent_table', because cl.relname is in the cl table which is joined over confrelid that is the referenced (parent) table. If you wanna filter on child table you should add join pg_class cl2 on cl2.oid = con.conrelid and then use cl2.relname = 'child_table'.
Works as far as I can tell.
|
38

Extension to ollyc recipe :

CREATE VIEW foreign_keys_view AS SELECT 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'; 

Then:

SELECT * FROM foreign_keys_view WHERE table_name='YourTableNameHere';

Comments

36

Check the ff post for your solution:

http://errorbank.blogspot.com/2011/03/list-all-foreign-keys-references-for.html

SELECT o.conname AS constraint_name, (SELECT nspname FROM pg_namespace WHERE oid=m.relnamespace) AS source_schema, m.relname AS source_table, (SELECT a.attname FROM pg_attribute a WHERE a.attrelid = m.oid AND a.attnum = o.conkey[1] AND a.attisdropped = false) AS source_column, (SELECT nspname FROM pg_namespace WHERE oid=f.relnamespace) AS target_schema, f.relname AS target_table, (SELECT a.attname FROM pg_attribute a WHERE a.attrelid = f.oid AND a.attnum = o.confkey[1] AND a.attisdropped = false) AS target_column FROM pg_constraint o LEFT JOIN pg_class f ON f.oid = o.confrelid LEFT JOIN pg_class m ON m.oid = o.conrelid WHERE o.contype = 'f' AND o.conrelid IN (SELECT oid FROM pg_class c WHERE c.relkind = 'r'); 

5 Comments

Offers two SQLs that work on PostgreSQL 9.1 (once you correct the wrong escaping put your 'tablename' (without schema-prefix) into the SQL).
+1 : this is the only solution that does not return duplicates.
to this solution, works fine and not return duplicates.
This solution will only show the first column of any multi-column foreign keys... but looks so much simpler than the one I just posted that will do multiples.
this the most sufficient result and fastest running query out of all mentioned here. I recommend it as #1 answer.
28

Just replace 'your table name' in below query with your table name.

select * from information_schema.key_column_usage where constraint_catalog=current_catalog and table_name='your_table_name' and position_in_unique_constraint notnull; 

2 Comments

Worked a charm as I write with PG 12.2
Just update "table_name='your_table_name'" in above query and you are done.
26

This query works correct with composite keys also:

select c.constraint_name , x.table_schema as schema_name , x.table_name , x.column_name , y.table_schema as foreign_schema_name , y.table_name as foreign_table_name , y.column_name as foreign_column_name from information_schema.referential_constraints c join information_schema.key_column_usage x on x.constraint_name = c.constraint_name join information_schema.key_column_usage y on y.ordinal_position = x.position_in_unique_constraint and y.constraint_name = c.unique_constraint_name order by c.constraint_name, x.ordinal_position 

4 Comments

You're joining the columns on "constraint_name", so this will only work if all of your constraint names are unique (across all tables in all schemas). This is not usually a requirement, and thus not enforced by the database.
Thanks. This is the only answer that shows how to use information_schema to properly handle multiple columns.
This solution works. It doesn't produce duplicates and it handles multiple fields in the FK.
Most of the other answers here return duplicates if a foreign key consists of 2 or more columns. This sql can handle this - and is using the information_schema, which makes it actually easy to understand.
10

I think what you were looking for and very close to what @ollyc wrote is this:

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='YourTableNameHere'; 

This will list all the tables that use your specified table as a foreign key

Comments

9

One another way:

WITH foreign_keys AS ( SELECT conname, conrelid, confrelid, unnest(conkey) AS conkey, unnest(confkey) AS confkey FROM pg_constraint WHERE contype = 'f' -- AND confrelid::regclass = 'your_table'::regclass ) -- if conrelid, conname pair shows up more than once then it is multicolumn foreign key SELECT fk.conname as constraint_name, fk.conrelid::regclass as referencing_table, a.attname as fkcol, fk.confrelid::regclass as referenced_table, af.attname as ukcol FROM foreign_keys fk JOIN pg_attribute af ON af.attnum = fk.confkey AND af.attrelid = fk.confrelid JOIN pg_attribute a ON a.attnum = conkey AND a.attrelid = fk.conrelid ORDER BY fk.conrelid, fk.conname; 

1 Comment

This is the nicest answer so far. It is concise, works properly with duplicate constraint names, and JOINs easily against the pg_attribute tables so that attribute properties can be tested
6

None of the existing answers gave me results in the form that I actually wanted them in. So here is my (gargantuan) query for finding information about foreign keys.

A few notes:

  • The expressions used to generate from_cols and to_cols could be vastly simplified on Postgres 9.4 and later using WITH ORDINALITY rather than the window-function-using hackery I'm using.
  • Those same expressions are relying on the query planner not altering the returned order of results from UNNEST. I don't think it will, but I don't have any multiple-column foreign keys in my dataset to test with. Adding the 9.4 niceties eliminates this possibility altogether.
  • The query itself requires Postgres 9.0 or later (8.x didn't allow ORDER BY in aggregate functions)
  • Replace STRING_AGG with ARRAY_AGG if you want an array of columns rather than a comma-separated string.

-

SELECT c.conname AS constraint_name, (SELECT n.nspname FROM pg_namespace AS n WHERE n.oid=c.connamespace) AS constraint_schema, tf.name AS from_table, ( SELECT STRING_AGG(QUOTE_IDENT(a.attname), ', ' ORDER BY t.seq) FROM ( SELECT ROW_NUMBER() OVER (ROWS UNBOUNDED PRECEDING) AS seq, attnum FROM UNNEST(c.conkey) AS t(attnum) ) AS t INNER JOIN pg_attribute AS a ON a.attrelid=c.conrelid AND a.attnum=t.attnum ) AS from_cols, tt.name AS to_table, ( SELECT STRING_AGG(QUOTE_IDENT(a.attname), ', ' ORDER BY t.seq) FROM ( SELECT ROW_NUMBER() OVER (ROWS UNBOUNDED PRECEDING) AS seq, attnum FROM UNNEST(c.confkey) AS t(attnum) ) AS t INNER JOIN pg_attribute AS a ON a.attrelid=c.confrelid AND a.attnum=t.attnum ) AS to_cols, CASE confupdtype WHEN 'r' THEN 'restrict' WHEN 'c' THEN 'cascade' WHEN 'n' THEN 'set null' WHEN 'd' THEN 'set default' WHEN 'a' THEN 'no action' ELSE NULL END AS on_update, CASE confdeltype WHEN 'r' THEN 'restrict' WHEN 'c' THEN 'cascade' WHEN 'n' THEN 'set null' WHEN 'd' THEN 'set default' WHEN 'a' THEN 'no action' ELSE NULL END AS on_delete, CASE confmatchtype::text WHEN 'f' THEN 'full' WHEN 'p' THEN 'partial' WHEN 'u' THEN 'simple' WHEN 's' THEN 'simple' ELSE NULL END AS match_type, -- In earlier postgres docs, simple was 'u'nspecified, but current versions use 's'imple. text cast is required. pg_catalog.pg_get_constraintdef(c.oid, true) as condef FROM pg_catalog.pg_constraint AS c INNER JOIN ( SELECT pg_class.oid, QUOTE_IDENT(pg_namespace.nspname) || '.' || QUOTE_IDENT(pg_class.relname) AS name FROM pg_class INNER JOIN pg_namespace ON pg_class.relnamespace=pg_namespace.oid ) AS tf ON tf.oid=c.conrelid INNER JOIN ( SELECT pg_class.oid, QUOTE_IDENT(pg_namespace.nspname) || '.' || QUOTE_IDENT(pg_class.relname) AS name FROM pg_class INNER JOIN pg_namespace ON pg_class.relnamespace=pg_namespace.oid ) AS tt ON tt.oid=c.confrelid WHERE c.contype = 'f' ORDER BY 1; 

Comments

5

Here is a solution by Andreas Joseph Krogh from the PostgreSQL mailing list: http://www.postgresql.org/message-id/[email protected]

SELECT source_table::regclass, source_attr.attname AS source_column, target_table::regclass, target_attr.attname AS target_column FROM pg_attribute target_attr, pg_attribute source_attr, (SELECT source_table, target_table, source_constraints[i] source_constraints, target_constraints[i] AS target_constraints FROM (SELECT conrelid as source_table, confrelid AS target_table, conkey AS source_constraints, confkey AS target_constraints, generate_series(1, array_upper(conkey, 1)) AS i FROM pg_constraint WHERE contype = 'f' ) query1 ) query2 WHERE target_attr.attnum = target_constraints AND target_attr.attrelid = target_table AND source_attr.attnum = source_constraints AND source_attr.attrelid = source_table; 

This solution handles foreign keys that reference multiple columns, and avoids duplicates (which some of the other answers fail to do). The only thing I changed were the variable names.

Here is an example that returns all employee columns that reference the permission table:

SELECT source_column FROM foreign_keys WHERE source_table = 'employee'::regclass AND target_table = 'permission'::regclass; 

Comments

5

To expand upon Martin's excellent answer here is a query that lets you filter based on the parent table and shows you the name of the child table with each parent table so you can see all of the dependent tables/columns based upon the foreign key constraints in the parent table.

select con.constraint_name, att2.attname as "child_column", cl.relname as "parent_table", att.attname as "parent_column", con.child_table, con.child_schema from (select unnest(con1.conkey) as "parent", unnest(con1.confkey) as "child", con1.conname as constraint_name, con1.confrelid, con1.conrelid, cl.relname as child_table, ns.nspname as child_schema from pg_class cl join pg_namespace ns on cl.relnamespace = ns.oid join pg_constraint con1 on con1.conrelid = cl.oid where con1.contype = 'f' ) con join pg_attribute att on att.attrelid = con.confrelid and att.attnum = con.child join pg_class cl on cl.oid = con.confrelid join pg_attribute att2 on att2.attrelid = con.conrelid and att2.attnum = con.parent where cl.relname like '%parent_table%' 

1 Comment

The query in the accepted answer adds 1.2 secs to a ~ 0.03 query, yours adds only 0.01, thanks!
5

Proper solution to the problem, using information_schema, working with multi column keys, joining columns of different names in both tables correctly and also compatible with ms sqlsever:

select fks.TABLE_NAME as foreign_key_table_name , fks.CONSTRAINT_NAME as foreign_key_constraint_name , kcu_foreign.COLUMN_NAME as foreign_key_column_name , rc.UNIQUE_CONSTRAINT_NAME as primary_key_constraint_name , pks.TABLE_NAME as primary_key_table_name , kcu_primary.COLUMN_NAME as primary_key_column_name from INFORMATION_SCHEMA.TABLE_CONSTRAINTS fks -- foreign keys inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu_foreign -- the columns of the above keys on fks.TABLE_CATALOG = kcu_foreign.TABLE_CATALOG and fks.TABLE_SCHEMA = kcu_foreign.TABLE_SCHEMA and fks.TABLE_NAME = kcu_foreign.TABLE_NAME and fks.CONSTRAINT_NAME = kcu_foreign.CONSTRAINT_NAME inner join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc -- referenced constraints on rc.CONSTRAINT_CATALOG = fks.CONSTRAINT_CATALOG and rc.CONSTRAINT_SCHEMA = fks.CONSTRAINT_SCHEMA and rc.CONSTRAINT_NAME = fks.CONSTRAINT_NAME inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS pks -- primary keys (referenced by fks) on rc.UNIQUE_CONSTRAINT_CATALOG = pks.CONSTRAINT_CATALOG and rc.UNIQUE_CONSTRAINT_SCHEMA = pks.CONSTRAINT_SCHEMA and rc.UNIQUE_CONSTRAINT_NAME = pks.CONSTRAINT_NAME inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu_primary on pks.TABLE_CATALOG = kcu_primary.TABLE_CATALOG and pks.TABLE_SCHEMA = kcu_primary.TABLE_SCHEMA and pks.TABLE_NAME = kcu_primary.TABLE_NAME and pks.CONSTRAINT_NAME = kcu_primary.CONSTRAINT_NAME and kcu_foreign.ORDINAL_POSITION = kcu_primary.ORDINAL_POSITION -- this joins the columns where fks.TABLE_SCHEMA = 'dbo' -- replace with schema name and fks.TABLE_NAME = 'your_table_name' -- replace with table name and fks.CONSTRAINT_TYPE = 'FOREIGN KEY' and pks.CONSTRAINT_TYPE = 'PRIMARY KEY' order by fks.constraint_name, kcu_foreign.ORDINAL_POSITION 

Note: There are some differences between potgresql and sqlserver implementations of information_schema which make the top answer give different results on the two systems - one shows column names for the foreign key table the other for the primary key table. For this reason I decided to use KEY_COLUMN_USAGE view instead.

3 Comments

The information schema seems like the right answer, but really you want the pg_catalog tables: pg_constraint etc. We got bitten hard by this. if your database has large amounts of constraints there can be performance issues...
The above condition on ORDINAL_POSITION can yield incorrect result when the order of columns in foreign key is different from the order of columns in the unique constraint. I believe you should have joined on kcu_foreign.POSITION_IN_UNIQUE_CONSTRAINT = kcu_primary.ORDINAL_POSITION Update: Also, a foreign key may depend on a UNIQUE constraint as well, so I think you should remove the pks.CONSTRAINT_TYPE condition and can just join rc to kcu_primary directly
I have made a similar answer here: stackoverflow.com/a/62260908/9093051
5

You can use the PostgreSQL system catalogs. Maybe you can query pg_constraint to ask for foreign keys. You can also use the Information Schema

Comments

4

Use the name of the Primary Key to which the Keys are referencing and query the information_schema:

select table_name, column_name from information_schema.key_column_usage where constraint_name IN (select constraint_name from information_schema.referential_constraints where unique_constraint_name = 'TABLE_NAME_pkey') 

Here 'TABLE_NAME_pkey' is the name of the Primary Key referenced by the Foreign Keys.

Comments

4
 SELECT conrelid::regclass AS table_from, conname, pg_get_constraintdef(oid) as condef FROM pg_catalog.pg_constraint r 

will also work for all constraints. E.g. with pysql: enter image description here

Comments

3

I created little tool to query and then compare database schema: Dump PostgreSQL db schema to text

There is info about FK, but ollyc response gives more details.

Comments

3
SELECT r.conname ,ct.table_name ,pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r, information_schema.constraint_table_usage ct WHERE r.contype = 'f' AND r.conname = ct.constraint_name ORDER BY 1 

Comments

3

This is what I'm currently using, it will list a table and it's fkey constraints [remove table clause and it will list all tables in current catalog]:

SELECT current_schema() AS "schema", current_catalog AS "database", "pg_constraint".conrelid::regclass::text AS "primary_table_name", "pg_constraint".confrelid::regclass::text AS "foreign_table_name", ( string_to_array( ( string_to_array( pg_get_constraintdef("pg_constraint".oid), '(' ) )[2], ')' ) )[1] AS "foreign_column_name", "pg_constraint".conindid::regclass::text AS "constraint_name", TRIM(( string_to_array( pg_get_constraintdef("pg_constraint".oid), '(' ) )[1]) AS "constraint_type", pg_get_constraintdef("pg_constraint".oid) AS "constraint_definition" FROM pg_constraint AS "pg_constraint" JOIN pg_namespace AS "pg_namespace" ON "pg_namespace".oid = "pg_constraint".connamespace WHERE --fkey and pkey constraints "pg_constraint".contype IN ( 'f', 'p' ) AND "pg_namespace".nspname = current_schema() AND "pg_constraint".conrelid::regclass::text IN ('whatever_table_name') 

1 Comment

Cool, but this "pg_constraint".conindid::regclass::text gives you the PK name of the referenced table, the constraint name is "pg_constraint".conname
2

I wrote a solution that like and use frequently. The code is at http://code.google.com/p/pgutils/. See the pgutils.foreign_keys view.

Unfortunately, the output is too wordy to include here. However, you can try it on a public version of the database here, like this:

$ psql -h unison-db.org -U PUBLIC -d unison -c 'select * from pgutils.foreign_keys; 

This works with 8.3 at least. I anticipate updating it, if needed, in the next few months.

-Reece

2 Comments

Project link is now dead.
@pimlottc: Moved to bitbucket.org/reece/pgutils. Thanks for pointing this out.
2

I upgraded answer of @ollyc which is currently at top. I agree with @fionbio because key_column_usage and constraint_column_usage has no relative information at column level.

If constraint_column_usage has ordinal_positon column like key_column_usage, it can be joined with this column. So I made a ordinal_position to constraint_column_usage as below.

I cannot confirm this manually created ordinal_position is exactly in same order with key_column_usage. But I checked it is exactly same order at least in my case.

SELECT tc.table_schema, tc.constraint_name, tc.table_name, kcu.column_name, ccu.table_schema AS foreign_table_schema, 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 AND tc.table_schema = kcu.table_schema JOIN (select row_number() over (partition by table_schema, table_name, constraint_name order by row_num) ordinal_position, table_schema, table_name, column_name, constraint_name from (select row_number() over (order by 1) row_num, table_schema, table_name, column_name, constraint_name from information_schema.constraint_column_usage ) t ) AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema AND ccu.ordinal_position = kcu.ordinal_position WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = 'mytable' 

Comments

1

The selected answer didn't work for me, so posting my sql that worked.

select con.conname as constraint_name, src_schema.nspname as source_schema, source.relname as source_table, source_col.attname as source_column, trg_schema.nspname as target_schema, target.relname as target_table, target_col.attname as target_column from pg_constraint con inner join pg_class source on source.oid = con.conrelid inner join pg_attribute source_col on source_col.attrelid = con.conrelid and source_col.attnum = con.conkey[1] and source_col.attisdropped = false inner join pg_namespace src_schema on src_schema.oid = source.relnamespace inner join pg_class target on target.oid = con.confrelid inner join pg_attribute target_col on target_col.attrelid = con.confrelid and target_col.attnum = con.confkey[1] and source_col.attisdropped = false inner join pg_namespace trg_schema on trg_schema.oid = target.relnamespace 

Comments

1
SELECT conrelid::regclass AS table_name, conname AS foreign_key, pg_get_constraintdef(oid) FROM pg_constraint WHERE contype = 'f' AND connamespace = 'public'::regnamespace ORDER BY conrelid::regclass::text, contype DESC; 

https://soft-builder.com/how-to-list-all-foreign-keys-in-postgresql-database

Comments

0

Note: Do not forget column's order while reading constraint columns!

SELECT conname, attname FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY (c.conkey) WHERE attrelid = 'schema.table_name'::regclass ORDER BY conname, array_position(c.conkey, a.attnum) 

Comments

0

the fastest to verify straight in bash answer based entirely on this answer

IFS='' read -r -d '' sql_code << EOF_SQL_CODE SELECT o.oid , o.conname AS constraint_name , (SELECT nspname FROM pg_namespace WHERE oid=m.relnamespace) AS source_schema , m.relname AS source_table , (SELECT a.attname FROM pg_attribute a WHERE a.attrelid = m.oid AND a.attnum = o.conkey[1] AND a.attisdropped = false) AS source_column , (SELECT nspname FROM pg_namespace WHERE oid=f.relnamespace) AS target_schema , f.relname AS target_table , (SELECT a.attname FROM pg_attribute a WHERE a.attrelid = f.oid AND a.attnum = o.confkey[1] AND a.attisdropped = false) AS target_column , ROW_NUMBER () OVER (ORDER BY o.oid) as rowid FROM pg_constraint o LEFT JOIN pg_class f ON f.oid = o.confrelid LEFT JOIN pg_class m ON m.oid = o.conrelid WHERE 1=1 AND o.contype = 'f' AND o.conrelid IN (SELECT oid FROM pg_class c WHERE c.relkind = 'r') EOF_SQL_CODE psql -d my_db -c "$sql_code" 

Comments

0

Where $1 ('my_schema') is the schema and $2 ('my_table') is the name of the table:

SELECT ss.conname constraint_name, a.attname column_name, ss.refnamespace fk_table_schema, ss.reflname fk_table_name, af.attname fk_column_name FROM pg_attribute a, pg_attribute af, (SELECT r.oid roid, c.conname, rf.relname reflname, information_schema._pg_expandarray(c.conkey) x, nrf.nspname refnamespace, rf.oid rfoid, information_schema._pg_expandarray(cf.confkey) xf FROM pg_namespace nr, pg_class r, pg_constraint c, pg_namespace nrf, pg_class rf, pg_constraint cf WHERE nr.oid = r.relnamespace AND r.oid = c.conrelid AND rf.oid = cf.confrelid AND c.conname = cf.conname AND nrf.oid = rf.relnamespace AND nr.nspname = $1 AND r.relname = $2) ss WHERE ss.roid = a.attrelid AND a.attnum = (ss.x).x AND NOT a.attisdropped AND ss.rfoid = af.attrelid AND af.attnum = (ss.xf).x AND NOT af.attisdropped ORDER BY ss.conname, a.attname; 

Comments

0

My own contribution. The goal is to backup the definition of all the foreign keys:

SELECT 'ALTER TABLE ' || tc.table_schema || '.' || tc.table_name || E'\n ADD FOREIGN KEY (' || kcu.column_name || ')' || E'\n REFERENCES ' || ccu.table_schema || '.' || ccu.table_name || ' (' || ccu.column_name || ') ' || E'\n ' || CASE WHEN rc.match_option <> 'NONE' THEN E'\n MATCH ' || rc.match_option ELSE '' END || CASE WHEN rc.update_rule <> 'NO ACTION' THEN E'\n ON UPDATE ' || rc.update_rule || ' ' ELSE '' END || CASE WHEN rc.delete_rule <> 'NO ACTION' THEN 'ON DELETE ' || rc.delete_rule ELSE '' END || ';' AS add_constraint FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema JOIN information_schema.referential_constraints AS rc ON tc.constraint_name=rc.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY' \t\a\g\a\ta 

Comments

0

improving the query from the most popular answer

because for postgresql 12+ information_schema is very slow

it helped me:

SELECT sh.nspname AS table_schema, tbl.relname AS table_name, col.attname AS column_name, referenced_sh.nspname AS foreign_table_schema, referenced_tbl.relname AS foreign_table_name, referenced_field.attname AS foreign_column_name FROM pg_constraint c INNER JOIN pg_namespace AS sh ON sh.oid = c.connamespace INNER JOIN (SELECT oid, unnest(conkey) as conkey FROM pg_constraint) con ON c.oid = con.oid INNER JOIN pg_class tbl ON tbl.oid = c.conrelid INNER JOIN pg_attribute col ON (col.attrelid = tbl.oid AND col.attnum = con.conkey) INNER JOIN pg_class referenced_tbl ON c.confrelid = referenced_tbl.oid INNER JOIN pg_namespace AS referenced_sh ON referenced_sh.oid = referenced_tbl.relnamespace INNER JOIN (SELECT oid, unnest(confkey) as confkey FROM pg_constraint) conf ON c.oid = conf.oid INNER JOIN pg_attribute referenced_field ON (referenced_field.attrelid = c.confrelid AND referenced_field.attnum = conf.confkey) WHERE c.contype = 'f' 

Comments

0

IF someone want to use store procedures then here is the way to retreive FKs 💪

--command-1 DROP TYPE IF EXISTS returnFkType CASCADE; --command-2 DROP FUNCTION IF EXISTS returnFks; --command-3 CREATE TYPE returnFkType as ( constraint_name varchar, source_schema varchar, target_schema varchar, source_table varchar, target_table varchar, source_column varchar, target_column varchar ); --command-4 CREATE OR REPLACE FUNCTION returnFks(_tbl varchar) RETURNS SETOF returnFkType LANGUAGE sql as $$ select c.constraint_name, x.table_schema as source_schema, y.table_schema as target_schema, x.table_name as source_table, y.table_name as target_table, x.column_name as source_column, y.column_name as target_column from information_schema.referential_constraints c join information_schema.key_column_usage x on x.constraint_name = c.constraint_name join information_schema.key_column_usage y on y.ordinal_position = x.position_in_unique_constraint and y.constraint_name = c.unique_constraint_name and x.table_name = $1 order by c.constraint_name, x.ordinal_position $$ --command-5 select * from returnFks($_TABLE); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.