0

I have this schema:

create table "cat" (
    "name" varchar(64),
    "owner" varchar(64),
    primary key ("name", "owner")
);

create table "comment" (
    "name" varchar(45),
    "owner" varchar(45),
    "id" uuid,
    "comment" text,
    primary key ("id"),
    foreign key ("name", "owner") references "cat"("name", "owner")
);

I want to get a list of foreign keys from table "comment" to "cat", so I use:

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 got almost what I want:

  constraint_name  | table_name | column_name | foreign_table_name | foreign_column_name 
-------------------+------------+-------------+--------------------+---------------------
 comment_name_fkey | comment    | owner       | cat                | name
 comment_name_fkey | comment    | name        | cat                | name
 comment_name_fkey | comment    | owner       | cat                | owner
 comment_name_fkey | comment    | name        | cat                | owner

But there are row 1 and row 4, which I would like to eliminate in the result, because it does not mirror the dependencies of column. How can I do it in Postgresql?

1
  • This should be simple enough with WHERE column_name = foreign_column_name, wrap the whole query above as a subquery, if necessary. Commented May 30, 2022 at 15:38

3 Answers 3

4

I think you can't do that using only information_schema, but you can do it querying directly tables:

SELECT conname AS constraint_name, conrelid::regclass AS table_name, ta.attname AS column_name,
       confrelid::regclass AS foreign_table_name, fa.attname AS foreign_column_name
  FROM (
   SELECT conname, conrelid, confrelid,
          unnest(conkey) AS conkey, unnest(confkey) AS confkey
     FROM pg_constraint
    WHERE conname = 'comment_name_fkey'
      --and contype = 'f'
  ) sub
  JOIN pg_attribute AS ta ON ta.attrelid = conrelid AND ta.attnum = conkey
  JOIN pg_attribute AS fa ON fa.attrelid = confrelid AND fa.attnum = confkey

result

  constraint_name  | table_name | column_name | foreign_table_name | foreign_column_name
-------------------+------------+-------------+--------------------+---------------------
 comment_name_fkey | comment    | name        | cat                | name
 comment_name_fkey | comment    | owner       | cat                | owner
Sign up to request clarification or add additional context in comments.

2 Comments

Use this query since I didn't had access to "constraint_column_usage" table. My final query: SELECT DISTINCT conrelid::regclass AS table_name, confrelid::regclass AS foreign_table_name FROM pg_constraint WHERE contype = 'f' ORDER BY table_name, foreign_table_name;
It's also important to note that information_schema is significantly slower than retrieving (and joining) pg_catalog.
1

I think you should select a list of column names used in a reference as a whole:

SELECT
    tc.constraint_name, 
    tc.table_name, 
    string_agg(distinct kcu.column_name, ', ') AS column_names, 
    ccu.table_name AS foreign_table_name,
    string_agg(distinct ccu.column_name, ', ') AS foreign_column_names 
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 tc.table_name = 'comment'
GROUP BY 1, 2, 4;

  constraint_name  | table_name | column_names | foreign_table_name | foreign_column_names 
-------------------+------------+--------------+--------------------+----------------------
 comment_name_fkey | comment    | name, owner  | cat                | name, owner
(1 row) 

However, it is not sure that the column names will be in proper order, it depends on the way they are listed in information_schema.

The more reliable solution is to query the system catalog pg_constraint. The function get_col_names() is defined here: List all foreign keys PostgresSQL.

select 
    conname as constraint_name,
    conrelid::regclass as table_name,
    get_col_names(conrelid, conkey) as column_names,
    confrelid::regclass as foreign_table_name,
    get_col_names(confrelid, confkey) as foreing_column_names
from pg_constraint
where contype ='f'
and conrelid = 'comment'::regclass;

  constraint_name  | table_name | column_names | foreign_table_name | foreing_column_names 
-------------------+------------+--------------+--------------------+----------------------
 comment_name_fkey | comment    | name, owner  | cat                | name, owner
(1 row) 

1 Comment

It's also important to note that information_schema is significantly slower than retrieving (and joining) pg_catalog.
1

The referential_constraints.unique_constraint_* and key_column_usage.ordinal_position columns can be used to properly join the foreign columns to their referenced columns. See this answer here https://stackoverflow.com/a/48824659/9093051.

Below is my stripped-down version:

SELECT
  rc.constraint_schema,
  rc.constraint_name,
  kcu.table_name,
  kcu.column_name,
  rcu.table_name AS referenced_table,
  rcu.column_name AS referenced_column
FROM information_schema.referential_constraints rc
LEFT JOIN information_schema.key_column_usage kcu
  ON rc.constraint_catalog = kcu.constraint_catalog
  AND rc.constraint_schema = kcu.constraint_schema
  AND rc.constraint_name = kcu.constraint_name
LEFT JOIN information_schema.key_column_usage rcu -- referenced columns
  ON rc.unique_constraint_catalog = rcu.constraint_catalog
  AND rc.unique_constraint_schema = rcu.constraint_schema
  AND rc.unique_constraint_name = rcu.constraint_name
  AND rcu.ordinal_position = kcu.position_in_unique_constraint;

Edit fixed the ordinal_position join condition

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.