PostgreSQL: SQL script to get a list of all tables that has a particular column as foreign key

馋奶兔 提交于 2019-12-20 08:01:19

问题


I'm using PostgreSQL and I'm trying to list all the tables that have a particular column from a table as a foreign-key/reference. Can this be done? I'm sure this information is stored somewhere in information_schema but I have no idea how to start querying it.


回答1:


select R.TABLE_NAME
from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE u
inner join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS FK
    on U.CONSTRAINT_CATALOG = FK.UNIQUE_CONSTRAINT_CATALOG
    and U.CONSTRAINT_SCHEMA = FK.UNIQUE_CONSTRAINT_SCHEMA
    and U.CONSTRAINT_NAME = FK.UNIQUE_CONSTRAINT_NAME
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE R
    ON R.CONSTRAINT_CATALOG = FK.CONSTRAINT_CATALOG
    AND R.CONSTRAINT_SCHEMA = FK.CONSTRAINT_SCHEMA
    AND R.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
WHERE U.COLUMN_NAME = 'a'
  AND U.TABLE_CATALOG = 'b'
  AND U.TABLE_SCHEMA = 'c'
  AND U.TABLE_NAME = 'd'

This uses the full catalog/schema/name triplet to identify a db table from all 3 information_schema views. You can drop one or two as required.

The query lists all tables that have a foreign key constraint against the column 'a' in table 'd'




回答2:


The other solutions are not guaranteed to work in postgresql, as the constraint_name is not guaranteed to be unique; thus you will get false positives. PostgreSQL used to name constraints silly things like '$1', and if you've got an old database you've been maintaining through upgrades, you likely still have some of those around.

Since this question was targeted AT PostgreSQL and that is what you are using, then you can query the internal postgres tables pg_class and pg_attribute to get a more accurate result.

NOTE: FKs can be on multiple columns, thus the referencing column (attnum of pg_attribute) is an ARRAY, which is the reason for using array_agg in the answer.

The only thing you need plug in is the TARGET_TABLE_NAME:

select 
  (select r.relname from pg_class r where r.oid = c.conrelid) as table, 
  (select array_agg(attname) from pg_attribute 
   where attrelid = c.conrelid and ARRAY[attnum] <@ c.conkey) as col, 
  (select r.relname from pg_class r where r.oid = c.confrelid) as ftable 
from pg_constraint c 
where c.confrelid = (select oid from pg_class where relname = 'TARGET_TABLE_NAME');

If you want to go the other way (list all of the things a specific table refers to), then just change the last line to:

where c.conrelid = (select oid from pg_class where relname = 'TARGET_TABLE_NAME');

Oh, and since the actual question was to target a specific column, you can specify the column name with this one:

select (select r.relname from pg_class r where r.oid = c.conrelid) as table, 
       (select array_agg(attname) from pg_attribute 
        where attrelid = c.conrelid and ARRAY[attnum] <@ c.conkey) as col, 
       (select r.relname from pg_class r where r.oid = c.confrelid) as ftable 
from pg_constraint c 
where c.confrelid = (select oid from pg_class where relname = 'TARGET_TABLE_NAME') and 
      c.confkey @> (select array_agg(attnum) from pg_attribute 
                    where attname = 'TARGET_COLUMN_NAME' and attrelid = c.confrelid);



回答3:


Personally, I prefer to query based on the referenced unique constraint rather than the column. That would look something like this:

SELECT rc.constraint_catalog,
       rc.constraint_schema||'.'||tc.table_name AS table_name,
       kcu.column_name,
       match_option,
       update_rule,
       delete_rule
FROM information_schema.referential_constraints AS rc 
    JOIN information_schema.table_constraints AS tc USING(constraint_catalog,constraint_schema,constraint_name)
    JOIN information_schema.key_column_usage AS kcu USING(constraint_catalog,constraint_schema,constraint_name)
WHERE unique_constraint_catalog='catalog'
    AND unique_constraint_schema='schema'
    AND unique_constraint_name='constraint name';

Here is a version that allows querying by column name:

SELECT rc.constraint_catalog,
       rc.constraint_schema||'.'||tc.table_name AS table_name,
       kcu.column_name,
       match_option,
       update_rule,
       delete_rule
FROM information_schema.referential_constraints AS rc
    JOIN information_schema.table_constraints AS tc USING(constraint_catalog,constraint_schema,constraint_name)
    JOIN information_schema.key_column_usage AS kcu USING(constraint_catalog,constraint_schema,constraint_name)
    JOIN information_schema.key_column_usage AS ccu ON(ccu.constraint_catalog=rc.unique_constraint_catalog AND ccu.constraint_schema=rc.unique_constraint_schema AND ccu.constraint_name=rc.unique_constraint_name)
WHERE ccu.table_catalog='catalog'
    AND ccu.table_schema='schema'
    AND ccu.table_name='name'
    AND ccu.column_name='column';



回答4:


This query requires only the referenced table name and column name, and produces a result set containing both sides of the foreign key.

select confrelid::regclass, af.attname as fcol,
       conrelid::regclass, a.attname as col
from pg_attribute af, pg_attribute a,
  (select conrelid,confrelid,conkey[i] as conkey, confkey[i] as confkey
   from (select conrelid,confrelid,conkey,confkey,
                generate_series(1,array_upper(conkey,1)) as i
         from pg_constraint where contype = 'f') ss) ss2
where af.attnum = confkey and af.attrelid = confrelid and
      a.attnum = conkey and a.attrelid = conrelid 
  AND confrelid::regclass = 'my_table'::regclass AND af.attname = 'my_referenced_column';

Example result set:

confrelid |         fcol         |   conrelid    |     col
----------+----------------------+---------------+-------------
 my_table | my_referenced_column | some_relation | source_type
 my_table | my_referenced_column | some_feature  | source_type

All credit to Lane and Krogh at the PostgreSQL forum.




回答5:


SELECT
  main_table.table_name            AS main_table_table_name,
  main_table.column_name           AS main_table_column_name,
  main_table.constraint_name       AS main_table_constraint_name,
  info_other_table.table_name      AS info_other_table_table_name,
  info_other_table.constraint_name AS info_other_table_constraint_name,
  info_other_table.column_name     AS info_other_table_column_name
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE main_table
  INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS other_table
    ON other_table.unique_constraint_name = main_table.constraint_name
  INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE info_other_table
    ON info_other_table.constraint_name = other_table.constraint_name
WHERE main_table.table_name = 'MAIN_TABLE_NAME';



回答6:


A simple request for recovered the names of foreign key as well as the names of the tables:

SELECT CONSTRAINT_NAME, table_name
FROM
   information_schema.table_constraints 
WHERE table_schema='public' and constraint_type='FOREIGN KEY'



回答7:


If you use the psql client, you can simply issue the \d table_name command to see which tables reference the given table. From the linked documentation page:

\d[S+] [ pattern ]

For each relation (table, view, materialized view, index, sequence, or foreign table) or composite type matching the pattern, show all columns, their types, the tablespace (if not the default) and any special attributes such as NOT NULL or defaults. Associated indexes, constraints, rules, and triggers are also shown. For foreign tables, the associated foreign server is shown as well.




回答8:


Table constraints can include multiple columns. The trick to getting this right is to join each column by their constraint ordinal positions. If you don't join correctly your script will blow up with duplicate rows 😥 whenever a table has multiple columns in a unique constraint.

Table Notes

This may be helpful in understanding what these tables do.

information_schema.table_constraints

  • lists all table constraints
  • explains constraint types
  • does not include column information

information_schema.referential_constraints

  • maps fk constraints to pk constraints.
    • constraint_name = fk constraint name
    • unique_constraint_name = pk constraint name
  • does not include column information

information_schema.key_column_usage

  • list column level constraint info
  • includes column ordinal positions in constraints. this is important!

Query

Lists all foreign key columns and their references.

select
       -- unique reference info
       ref.table_catalog    as ref_database,
       ref.table_schema     as ref_schema,
       ref.table_name       as ref_table,
       ref.column_name      as ref_column,
       refd.constraint_type as ref_type, -- e.g. UNIQUE or PRIMARY KEY

       -- foreign key info
       fk.table_catalog as fk_database,
       fk.table_schema  as fk_schema,
       fk.table_name    as fk_table,
       fk.column_name   as fk_column,
       map.update_rule  as fk_on_update,
       map.delete_rule  as fk_on_delete

-- lists fk constraints and maps them to pk constraints
from information_schema.referential_constraints as map

-- join unique constraints (e.g. PKs constraints) to ref columns info
inner join information_schema.key_column_usage as ref
    on  ref.constraint_catalog = map.unique_constraint_catalog
    and ref.constraint_schema = map.unique_constraint_schema
    and ref.constraint_name = map.unique_constraint_name

-- optional: to include reference constraint type
left join information_schema.table_constraints as refd
    on  refd.constraint_catalog = ref.constraint_catalog
    and refd.constraint_schema = ref.constraint_schema
    and refd.constraint_name = ref.constraint_name

-- join fk columns to the correct ref columns using ordinal positions
inner join information_schema.key_column_usage as fk
    on  fk.constraint_catalog = map.constraint_catalog
    and fk.constraint_schema = map.constraint_schema
    and fk.constraint_name = map.constraint_name
    and fk.position_in_unique_constraint = ref.ordinal_position --IMPORTANT!

Example

consider the relationship between these to tables.

create table foo (
    a int,
    b int,
    primary key (a,b)
);

create table bar (
    c int,
    d int,
    foreign key (c,d) references foo (b,a) -- i flipped a,b to make a point later.
);

If we check the information_schema.table_constraints table we can see the names of the pk constraint and the fk constraint.

select * from information_schema.table_constraints where table_name in ('foo','bar');
| constraint_name | table_name | constraint_type |
| --------------- | ---------- | --------------- |
| foo_pkey        | foo        | PRIMARY KEY     |
| bar_c_d_fkey    | bar        | FOREIGN KEY     |

And when we check the information_schema.referential_constraints table we can see that our foreign key constraint depends on our primary keys unique constraint.

select * from information_schema.referential_constraints where constraint_name in ('bar_c_d_fkey');
| constraint_name | unique_constraint_name |
| --------------- | ---------------------- |
| bar_c_d_fkey    | foo_pkey               |

And finally, we check that information_schema.key_column_usage table. we can see the the position_in_unique_constraint of the FK columns correctly map to the ordinal_position of the PK columns.

Notice that d correctly maps to a and c correctly maps to b per the table definitions above.

select * from information_schema.key_column_usage where table_name in ('foo','bar');
| constraint_name | table_name | column_name | ordinal_position | position_in_unique_constraint |
| --------------- | ---------- | ----------- | ---------------- | ----------------------------- |
| foo_pkey        | foo        | a           | 1                | null                          |
| foo_pkey        | foo        | b           | 2                | null                          |
| bar_c_d_fkey    | bar        | c           | 1                | 2                             |
| bar_c_d_fkey    | bar        | d           | 2                | 1                             |

Now all that's left is to join them together. The main query above is one way you could do so.



来源:https://stackoverflow.com/questions/5347050/postgresql-sql-script-to-get-a-list-of-all-tables-that-has-a-particular-column

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!