Postgres:SQL列出表外键


Answers:


373

您可以通过information_schema表执行此操作。例如:

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
      AND ccu.table_schema = tc.table_schema
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name='mytable';

8
table_name ='mytable'应该是tc.table_name ='mytable',否则会引发模棱两可的错误
intrepion

15
+1,非常有帮助。为了使查询更健壮,它可能也应该加入constraint_schema,因为两个模式可能具有相同名称的约束。就像这样: 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)
EMP

8
当约束中有多个列时,这会中断,不是吗?似乎没有正确的方法使用information_schema BTW将pk列与fk列相关联。
fionbio

5
实际上,它的约束超过了一列。对于Postgres,有一种方法可以从pg_catalog模式中获取此信息。请参阅下面的答案。
马丁2012年

9
查询错误。它假定约束名称不能重复,这是错误的。具有相同名称的约束可以存在于不同的命名空间中。您正在使用constraint_name进行联接。另外,因为您不确定两个约束是否相同,所以同时加入constraint_name和模式名称都将无效。唯一的选择是使用oid加入pg_constraints,pg_class等。Postgres的ANSI目录仅用于遵从性,但存在缺陷。pg_catalog是必经之路。正确答案在这里dba.stackexchange.com/questions/36979/retrieving-all-pk-and-fk
Tulains科尔多瓦

69

psql会执行此操作,并且如果使用以下命令启动psql:

psql -E

它会告诉您确切执行什么查询。如果找到外键,则为:

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

在这种情况下,16485是我正在查看的表的oid-您可以通过将表名强制转换为regclass来获得该名称:

WHERE r.conrelid = 'mytable'::regclass

如果表名不是唯一的(或您的中的第一个),则对表名进行模式限定search_path

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

2
这非常方便!Postgres似乎有百万个这样的小功能,可以简化所有操作。现在如何记住他们?
epic_fil 2014年

5
@Phil:您只需要一个一般的想法。让手册记住其余内容。
Erwin Brandstetter 2014年

3
列出针对表的所有外键:SELECT conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r WHERE r.confrelid = 'myschema.mytable'::regclass;
regilero 2015年

1
@ErwinBrandstetter我该如何获取外国表名称?
惠灵顿席尔瓦里贝罗

2
我不明白,应该使用什么命令?psql -E -U username -d database ThenWHAT
Poutrathor

49

\d+ tablename在PostgreSQL提示符下发出,除了显示表列的数据类型外,还将显示索引和外键。


对不起,我没有注意到我的评论。如果您至少可以尝试一次,则会看到外键映射也显示出来。
Gre Hahn 2015年

45

Ollyc的答案很好,因为它不是特定于Postgres的,但是,当外键引用的字段超过一列时,它就会崩溃。以下查询适用于任意数量的列,但在很大程度上依赖于Postgres扩展:

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

在8.4之前,必须首先创建功能嵌套。wiki.postgresql.org/wiki/Array_Unnest
maletin 2012年

在哪里将表名插入此查询中?逐字输入,上面的代码在我的PSQL DB上返回了0行,该行具有数十个外键。
Phrogz

4
您将“ child_table”和“ child_schema”替换为表及其模式的名称
马丁

这不会告诉您fkey的名称。
埃文·卡罗尔

@EvanCarroll我已经更新了答案,以包含密钥的名称。
马丁

31

ollyc配方的扩展:

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

然后:

SELECT * FROM foreign_keys_view WHERE table_name='YourTableNameHere';


谢谢,非常适合重用。
schellingerht

16

请在ff帖子中找到您的解决方案,并且在您对此功能有所帮助时也不要忘了标记

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');

提供两个在PostgreSQL 9.1上运行的SQL(一旦您纠正了错误的转义,请将“表名”(不带模式前缀)放到SQL中)。
alfonx 2012年

2
+1:这是唯一不返回重复项的解决方案。
奥利维尔·马特

此解决方案可以正常工作,并且不返回重复项。
富曼2015年

1
该解决方案将仅显示任何多列外键的第一列……但看上去比我刚刚发布的多列外键要简单得多。
dewin '16

12

此查询对复合键也有效:

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

2
您正在联接“ constraint_name”上的列,因此仅当所有约束名称都是唯一的时(在所有模式中的所有表中),这才起作用。通常这不是必需的,因此数据库不强制执行。
Zilk 2013年

3
谢谢。这是唯一显示如何使用information_schema正确处理多列的答案。
塞缪尔·丹尼尔森

此解决方案有效。它不会产生重复项,并且会处理FK中的多个字段。
伊戈尔

9

我认为您正在寻找的内容与@ollyc所写内容非常接近:

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

这将列出所有使用指定表作为外键的表


9

简短但可口的赞誉(如果它对您有用)。

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

当我用PG 12.2编写时发挥了魅力
Jack Kinsella

5

现有的答案都没有以我实际想要的形式给出结果。这是我的(gargantuan)查询,用于查找有关外键的信息。

一些注意事项:

  • 该表达式用于生成from_colsto_cols可以大大简化对的Postgres 9.4和以后使用WITH ORDINALITY,而不是我使用的是使用窗口功能两轮牛车。
  • 这些相同的表达式依赖于查询计划程序,而不会更改返回的结果顺序 UNNEST。我认为不会,但是我的数据集中没有可用于测试的多列外键。添加9.4个细微之处完全消除了这种可能性。
  • 查询本身需要Postgres 9.0或更高版本(8.x不允许ORDER BY在聚合函数中使用)
  • 如果您想要的是列数组而不是逗号分隔的字符串,请替换STRING_AGGARRAY_AGG

--

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;

5

另一种方式:

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 confrelid, conname pair shows up more than once then it is multicolumn foreign key
SELECT fk.conname as constraint_name,
       fk.confrelid::regclass as referenced_table, af.attname as pkcol,
       fk.conrelid::regclass as referencing_table, a.attname as fkcol
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.confrelid, fk.conname
;


4

使用这些键所引用的主键的名称并查询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')

这里的“ TABLE_NAME_pkey”是外键引用的主键的名称。


4

这是来自PostgreSQL邮件列表的Andreas Joseph Krogh的解决方案:http : //www.postgresql.org/message-id/200811072134.44750.andreak@officenet.no

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;

此解决方案处理引用多个列的外键,并避免重复(其他答案中的某些失败)。我唯一更改的是变量名。

这是一个返回所有employee引用该permission表的列的示例:

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

4

为了扩展Martin的出色答案,这里是一个查询,该查询使您可以基于父表进行过滤,并向您显示每个父表的子表的名称,以便您可以基于中的外键约束查看所有依赖表/列。父表。

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
接受的答案中的查询将1.2秒增加到〜0.03查询,您的查询仅增加0.01秒,谢谢!
AVProgrammer

3

正确的解决问题的方法是,使用information_schema,使用多列键,在两个表中正确连接不同名称的列并与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

注意:potgresql和sqlserver实现之间有一些区别,它们的实现information_schema使最重要的答案在两个系统上给出不同的结果-一个显示外键表的列名,另一个显示主键表。因此,我决定改用KEY_COLUMN_USAGE视图。


信息模式看起来似乎是正确的答案,但实际上您确实想要pg_catalog表:pg_constraint等。如果您的数据库有很多限制,则可能存在性能问题...
hajikelist,

ORDINAL_POSITION当外键中的列顺序与唯一约束中的列顺序不同时,上述条件可能会产生错误的结果。我相信你应该已经加入了kcu_foreign.POSITION_IN_UNIQUE_CONSTRAINT = kcu_primary.ORDINAL_POSITION 更新:另外,外键可能取决于UNIQUE约束一样,所以我觉得你应该删除pks.CONSTRAINT_TYPE条件,可以只加入rckcu_primary直接
欧洲糖尿病研究协会

我在这里做了类似的回答:stackoverflow.com/a/62260908/9093051
easd

2
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

2

我写了一个喜欢并经常使用的解决方案。该代码位于http://code.google.com/p/pgutils/。参见pgutils.foreign_keys视图。

不幸的是,输出太罗to了,无法在此处包含。但是,您可以在此处在数据库的公共版本上尝试它,如下所示:

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

至少适用于8.3。我希望在未来几个月内根据需要对其进行更新。

-里斯


1
项目链接现在已消失。
pimlottc 2014年

@pimlottc:移至bitbucket.org/reece/pgutils。感谢您指出了这一点。
Reece 2014年


0

注意:在阅读约束列时不要忘记列的顺序!

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)

0

这是我当前正在使用的东西,它将列出一个表,并且受fkey约束[删除表子句,并将列出当前目录中的所有表]:

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')

0

最快基于bash答案直接验证bash 答案

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"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.