Reputation: 311
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?
Upvotes: 0
Views: 7181
Reputation: 101
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
Upvotes: 1
Reputation: 121594
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)
Upvotes: 1
Reputation: 5930
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
Upvotes: 4