SQL列出引用表中特定列的所有表

时间:2011-03-18 01:16:45

标签: sql database postgresql

我正在使用PostgreSQL,我试图将表中具有特定列的所有表列为外键/引用。可以这样做吗?我确定这些信息存储在information_schema的某处,但我不知道如何开始查询它。

9 个答案:

答案 0 :(得分:59)

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'

这使用完整的目录/模式/名称三元组来识别所有3个information_schema视图中的db表。您可以根据需要删除一两个。

查询列出了对表'd'

中列'a'具有外键约束的所有表

答案 1 :(得分:52)

其他解决方案无法保证在postgresql 中有效,因为不能保证constraint_name是唯一的;因此你会得到误报。 PostgreSQL用来命名限制愚蠢的东西,如'$ 1',如果你有一个旧的数据库,你一直在维护升级,你可能仍然有一些这些。

由于这个问题是针对AT PostgreSQL而你正在使用的,所以你可以查询内部的postgres表pg_class和pg_attribute以获得更准确的结果。

注意:FK可以在多个列上,因此引用列(pg_attribute的attnum)是一个ARRAY,这是在答案中使用array_agg的原因。

您唯一需要插入的是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');

如果你想以另一种方式(列出特定表所引用的所有内容),那么只需将最后一行更改为:

where c.conrelid = (select oid from pg_class where relname = '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') and 
      c.confkey @> (select array_agg(attnum) from pg_attribute 
                    where attname = 'TARGET_COLUMN_NAME' and attrelid = c.confrelid);

答案 2 :(得分:9)

就个人而言,我更喜欢根据引用的唯一约束而不是列进行查询。这看起来像这样:

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

这是一个允许按列名查询的版本:

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

答案 3 :(得分:8)

此查询需要引用的表名列名,并生成包含外键两侧的结果集。< / p>

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

示例结果集:

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

归功于Lane and Krogh at the PostgreSQL forum

答案 4 :(得分:4)

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

答案 5 :(得分:3)

恢复外键名称以及表名称的简单请求:

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

答案 6 :(得分:1)

我将 @Tony K answer变成了可重用的函数,该函数接受模式/表/列元组并返回所有具有外键关系的表:{{3 }}

我需要这样的东西才能实现将两个记录合并为一个记录的脚本。

功能

CREATE SCHEMA utils;

-- Return type for the utils.get_referenced_tables function
CREATE TYPE utils.referenced_table_t AS (
  constraint_name name,
  schema_name name,
  table_name name,
  column_name name[],
  foreign_schema_name name,
  foreign_table_name name
);
/*
 A function to get all downstream tables that are referenced to a table via a foreign key relationship
 The function looks at all constraints that contain a reference to the provided schema-qualified table column
 It then generates a list of the schema/table/column tuples that are the target of these references
 Idea based on https://stackoverflow.com/a/21125640/7114675
 Postgres built-in reference:
 - pg_namespace  => schemas
 - pg_class      => tables
 - pg_attribute  => table columns
 - pg_constraint => constraints
*/
CREATE FUNCTION utils.get_referenced_tables (schema_name name, table_name name, column_name name)
RETURNS SETOF utils.referenced_table_t AS $$
  -- Wrap the internal query in a select so that we can order it more easily
  SELECT * FROM (
    -- Get human-readable names for table properties by mapping the OID's stored on the pg_constraint
    -- table to the underlying value on their relevant table.
    SELECT
      -- constraint name - we get this directly from the constraints table
      pg_constraint.conname AS constraint_name,
      -- schema_name
      (
        SELECT pg_namespace.nspname FROM pg_namespace
        WHERE pg_namespace.oid = pg_constraint.connamespace
      ) as schema_name,
      -- table_name
      (
        SELECT pg_class.relname FROM pg_class
        WHERE pg_class.oid = pg_constraint.conrelid
      ) as table_name,
      -- column_name
      (
        SELECT array_agg(attname) FROM pg_attribute
        WHERE attrelid = pg_constraint.conrelid
          AND ARRAY[attnum] <@ pg_constraint.conkey
      ) AS column_name,
      -- foreign_schema_name
      (
        SELECT pg_namespace.nspname FROM pg_namespace
        WHERE pg_namespace.oid = (
          SELECT pg_class.relnamespace FROM pg_class
          WHERE pg_class.oid = pg_constraint.confrelid
        )
      ) AS foreign_schema_name,
      -- foreign_table_name
      (
        SELECT pg_class.relname FROM pg_class
        WHERE pg_class.oid = pg_constraint.confrelid
      ) AS foreign_table_name
    FROM pg_constraint
    -- confrelid = constraint foreign relation id = target schema + table
    WHERE confrelid IN (
        SELECT oid FROM pg_class
        -- relname = target table name
        WHERE relname = get_referenced_tables.table_name
        -- relnamespace = target schema
          AND relnamespace = (
            SELECT oid FROM pg_namespace
            WHERE nspname = get_referenced_tables.schema_name
          )
    )
    -- confkey = constraint foreign key = the column on the foreign table linked to the target column
    AND confkey @> (
      SELECT array_agg(attnum) FROM pg_attribute
      WHERE attname = get_referenced_tables.column_name
      AND attrelid = pg_constraint.confrelid
    )
  ) a
  ORDER BY
    schema_name,
    table_name,
    column_name,
    foreign_table_name,
    foreign_schema_name
 ;
$$ LANGUAGE SQL STABLE;

用法示例:

/*
  Function to merge two people into a single person
  The primary person (referenced by primary_person_id) will be retained, the secondary person
  will have all their records re-referenced to the primary person, and then the secondary person
  will be deleted
  Note that this function may be destructive! For most tables, the records will simply be merged,
  but in cases where merging would violate a UNIQUE or EXCLUSION constraint, the secondary person's
  respective records will be dropped. For example, people cannot have overlapping pledges (on the
  pledges.pledge table). If the secondary person has a pledge that overlaps with a pledge that is
  on record for the primary person, the secondary person's pledge will just be deleted.
*/
CREATE FUNCTION utils.merge_person (primary_person_id BIGINT, secondary_person_id BIGINT)
RETURNS people.person AS $$
DECLARE
  _referenced_table utils.referenced_table_t;
  _col name;
  _exec TEXT;
  _primary_person people.person;
BEGIN
  -- defer all deferrable constraints
  SET CONSTRAINTS ALL DEFERRED;
  -- This loop updates / deletes all referenced tables, setting the person_id (or equivalent)
  -- From secondary_person_id => primary_person_id
  FOR _referenced_table IN (SELECT * FROM utils.get_referenced_tables('people', 'person', 'id')) LOOP
    -- the column_names are stored as an array, so we need to loop through these too
    FOREACH _col IN ARRAY _referenced_table.column_name LOOP
      RAISE NOTICE 'Merging %.%(%)', _referenced_table.schema_name, _referenced_table.table_name, _col;

      -- FORMAT allows us to safely build a dynamic SQL string
      _exec = FORMAT(
        $sql$ UPDATE %s.%s SET %s = $1 WHERE %s = $2 $sql$,
        _referenced_table.schema_name,
        _referenced_table.table_name,
        _col,
        _col
      );

      RAISE NOTICE 'SQL:  %', _exec;

      -- wrap the execution in a block so that we can handle uniqueness violations
      BEGIN
        EXECUTE _exec USING primary_person_id, secondary_person_id;
        RAISE NOTICE 'Merged %.%(%) OK!', _referenced_table.schema_name, _referenced_table.table_name, _col;
      EXCEPTION
        -- Error codes are Postgres built-ins, see https://www.postgresql.org/docs/9.6/errcodes-appendix.html
        WHEN unique_violation OR exclusion_violation THEN
          RAISE NOTICE 'Cannot merge record with % = % on table %.%, falling back to deletion!', _col, secondary_person_id, _referenced_table.schema_name, _referenced_table.table_name;
          _exec = FORMAT(
            $sql$ DELETE FROM %s.%s WHERE %s = $1 $sql$,
            _referenced_table.schema_name,
            _referenced_table.table_name,
            _col
          );
          RAISE NOTICE 'SQL:  %', _exec;
          EXECUTE _exec USING secondary_person_id;
          RAISE WARNING 'Deleted record with % = % on table %.%', _col, secondary_person_id, _referenced_table.schema_name, _referenced_table.table_name;
      END;

    END LOOP;
  END LOOP;

  -- Once we've updated all the tables, we can safely delete the secondary person
  RAISE WARNING 'Deleted person with id = %', secondary_person_id;

  -- Get our primary person so that we can return them
  SELECT * FROM people.person WHERE id = primary_person_id INTO _primary_person;

  RETURN _primary_person;

END
$$ LANGUAGE plpgsql VOLATILE;

请注意在函数中使用SET CONSTRAINTS ALL DEFERRED;,以确保在合并结束时检查外键关系。您可能需要将约束更新为DEFERRABLE INITIALLY DEFERRED

ALTER TABLE settings.contact_preference
  DROP CONSTRAINT contact_preference_person_id_fkey,
  DROP CONSTRAINT person_id_current_address_id_fkey,
  ADD CONSTRAINT contact_preference_person_id_fkey
    FOREIGN KEY (person_id)
    REFERENCES people.person(id)
    ON UPDATE CASCADE ON DELETE CASCADE
    DEFERRABLE INITIALLY IMMEDIATE,
  ADD CONSTRAINT person_id_current_address_id_fkey
    FOREIGN KEY (person_id, current_address_id)
    REFERENCES people.address(person_id, id)
    DEFERRABLE INITIALLY IMMEDIATE
;

答案 7 :(得分:0)

如果使用psql客户端,则只需发出\d table_name命令即可​​查看哪些表引用了给定的表。在链接的文档页面上:

  

\d[S+] [ pattern ]

     

对于与pattern匹配的每个关系(表,视图,实例化视图,索引,序列或外部表)或复合类型,   显示所有列,它们的类型,表空间(如果不是默认值)和   任何特殊属性,例如NOT NULL或默认属性。关联的   还显示索引,约束,规则和触发器。对于外国   表中,还会显示关联的外部服务器。

答案 8 :(得分:0)

表约束可以包含多个列。正确执行此操作的技巧是按其约束顺序位置连接各列。如果您未正确连接,则当一个表在唯一约束中具有多个列时,脚本将弹出重复行?。

表格注释

这可能有助于理解这些表的作用。

information_schema.table_constraints

  • 列出所有表约束
  • 解释约束类型
  • 不包括列信息

information_schema.referential_constraints

  • 将fk约束映射到pk约束。
    • constraint_name = fk约束名称
    • unique_constraint_name = pk约束名称
  • 不包括列信息

information_schema.key_column_usage

  • 列出列级别约束信息
  • 在约束中包括列常规位置。这很重要!

查询

列出所有外键列及其引用。

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!

示例

考虑它们与表之间的关系。

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

如果我们查看information_schema.table_constraints表,我们可以看到pk约束和fk约束的名称。

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     |

当我们查看information_schema.referential_constraints表时,我们可以看到我们的外键约束取决于我们的主键唯一约束。

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               |

最后,我们检查该information_schema.key_column_usage表。我们可以看到FK列的position_in_unique_constraint正确映射到PK列的ordinal_position

请注意,根据上述表定义,d正确映射到ac正确映射到b

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                             |

现在剩下的就是将它们连接在一起。上面的主要查询是一种实现方法。