关于postgresql:Postgres:SQL列出表外键

Postgres: SQL to list table foreign keys

有没有办法使用SQL列出给定表的所有外键? 我知道表名/模式,我可以将其插入。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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';


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

1
psql -E

它会准确显示执行的查询。在查找外键的情况下,它是:

1
2
3
4
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 - 你可以通过将你的tablename转换为regclass来获得那个:

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

如果表名不是唯一的(或search_path中的第一个),则表格限定表名:

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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


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


扩展到ollyc配方:

1
2
3
4
5
6
7
8
9
10
11
12
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';


检查你的解决方案的ff帖子,当你对此有用时,不要忘记标记这个

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

1
2
3
4
5
6
7
8
9
10
11
12
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');


此查询也适用于复合键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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


我认为你在寻找和@ollyc所写的非常接近的是:

1
2
3
4
5
6
7
8
9
10
11
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';

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


您可以使用PostgreSQL系统目录。也许你可以查询pg_constraint来询问外键。
您还可以使用信息架构


为了扩展Martin的优秀答案,这里有一个查询,它允许您根据父表进行过滤,并显示每个父表的子表的名称,以便您可以根据外键约束查看所有相关表/列。父表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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%'


以下是来自PostgreSQL邮件列表的Andreas Joseph Krogh的解决方案:http://www.postgresql.org/message-id/[email protected]

1
2
3
4
5
6
7
8
9
10
11
12
13
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;

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

下面是一个示例,它返回引用permission表的所有employee列:

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


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

几点说明:

  • 用于生成from_colsto_cols的表达式可以在Postgres 9.4及更高版本上使用WITH ORDINALITY而不是使用的窗口函数使用hackery大大简化。
  • 这些相同的表达式依赖于查询规划器而不是改变UNNEST的返回结果顺序。我不认为它会,但我的数据集中没有任何多列外键来测试。添加9.4细节完全消除了这种可能性。
  • 查询本身需要Postgres 9.0或更高版本(8.x在聚合函数中不允许ORDER BY)
  • 如果要使用列数组而不是逗号分隔的字符串,请将STRING_AGG替换为ARRAY_AGG

-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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;

使用Keys引用的主键的名称并查询information_schema:

1
2
3
4
5
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'是外键引用的主键的名称。


简短而甜蜜

1
SELECT  * FROM information_schema.key_column_usage WHERE constraint_catalog=current_catalog AND TABLE_NAME='your_table_name' AND position_in_unique_constraint notnull;


另一种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
;

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

不幸的是,输出太冗长而不包括在这里。但是,您可以在此处对数据库的公共版本进行尝试,如下所示:

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

这至少适用于8.3。如果需要,我预计在接下来的几个月内更新它。

-Reece


1
2
3
4
5
6
7
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

正确解决问题,使用information_schema,使用多列密钥,正确连接两个表中不同名称的列,并与ms sqlsever兼容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

注意:在information_schema的potgresql和sqlserver实现之间存在一些差异,这使得最佳答案在两个系统上给出不同的结果 - 一个显示外键表的列名,另一个显示主键表的列名。 出于这个原因,我决定使用KEY_COLUMN_USAGE视图。


我创建了一个小工具来查询,然后比较数据库模式:
将PostgreSQL数据库模式转储到文本

有关于FK的信息,但ollyc响应提供了更多细节。


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

1
2
3
4
5
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)

这就是我目前使用的,它将列出一个表,它是fkey约束[remove table子句,它将列出当前目录中的所有表]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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')