【问题标题】:How can I find tables which reference a particular row via a foreign key?如何找到通过外键引用特定行的表?
【发布时间】:2012-07-31 18:59:26
【问题描述】:

给定这样的结构:

CREATE TABLE reference_table (
  reference_table_key numeric NOT NULL,
  reference_value numeric,
  CONSTRAINT reference_table_pk PRIMARY KEY (reference_table_key)
);

CREATE TABLE other_table (
  other_table_key numeric NOT NULL,
  reference_table_key numeric,
  CONSTRAINT other_table_pk PRIMARY KEY (other_table_key),
  ONSTRAINT other_table_reference_fk FOREIGN KEY (reference_table_key)
      REFERENCES reference_table (reference_table_key) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE SET NULL
);

CREATE TABLE another_table (
  another_table_key numeric NOT NULL,
  do_stuff_key numeric,
  CONSTRAINT another_table_pk PRIMARY KEY (another_table_key),
  ONSTRAINT another_table_reference_fk FOREIGN KEY (do_stuff_key)
      REFERENCES reference_table (reference_table_key) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE SET NULL
);

--there are 50-60 tables which have similar foreign key references to reference_table

我想编写一个查询,告诉我 other_table 和 another_table 中的主键,以及可能更多 reference_value 为 NULL 的表。

在伪代码中:

SELECT table_name, table_primary_key, table_fk_column_name
FROM ?????? some PG table ???????, reference_table
WHERE reference_table.reference_value IS NULL;

结果看起来像:

table_name | table_primary_key | table_fk_column_name | reference_table_pk
---------------------------------------------------------------------------
other_table   | 2                |  reference_table_key | 7
other_table   | 4                |  reference_table_key | 56
other_table   | 45               |  reference_table_key | 454
other_table   | 65765            |  reference_table_key | 987987
other_table   | 11               |  reference_table_key | 3213
another_table | 3                |  do_stuff_key        | 4645
another_table | 5                |  do_stuff_key        | 43546
another_table | 7                |  do_stuff_key        | 464356
unknown_table | 1                |  unkown_column_key   | 435435
unknown_table | 1                |  some_other_column_key | 34543
unknown_table | 3                |  unkown_column_key   | 124
unknown_table | 3                |  some_other_column_key | 123

这类似于 Postgres: SQL to list table foreign keys ,但不是重复的。该问题显示了表结构。我想找到具体的实例。

基本上,如果我要去DELETE FROM reference_table WHERE reference_value IS NULL;,postgres 必须在内部做一些事情来确定它需要将other_table 中第 2 行中的reference_table_key 设置为 NULL。我想看看那些行会是什么。

有没有可以做到这一点的查询?是否有一个修饰符可以传递给 DELETE 调用,告诉我哪些表/行/列会受到该 DELETE 的影响?

【问题讨论】:

    标签: sql postgresql foreign-keys


    【解决方案1】:

    引用列中的 NULL 值

    此查询生成 DML 语句以在所有表​​中查找 所有行,其中列具有外键约束引用另一个表但包含 NULL 值在该列中:

    WITH x AS (
     SELECT c.conrelid::regclass    AS tbl
          , c.confrelid::regclass   AS ftbl
          , quote_ident(k.attname)  AS fk
          , quote_ident(pf.attname) AS pk
     FROM   pg_constraint c
     JOIN   pg_attribute  k ON (k.attrelid, k.attnum) = (c.conrelid, c.conkey[1])
     JOIN   pg_attribute  f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
     LEFT   JOIN pg_constraint p  ON p.conrelid = c.conrelid AND p.contype = 'p'
     LEFT   JOIN pg_attribute  pf ON (pf.attrelid, pf.attnum)
                                   = (p.conrelid, p.conkey[1])
     WHERE  c.contype   = 'f'
     AND    c.confrelid = 'fk_tbl'::regclass  -- references to this tbl
     AND    f.attname   = 'fk_tbl_id'         -- and only to this column
    )
    SELECT string_agg(format(
    'SELECT %L AS tbl
         , %L AS pk
         , %s::text AS pk_val
         , %L AS fk
         , %L AS ftbl
    FROM   %1$s WHERE %4$s IS NULL'
                      , tbl
                      , COALESCE(pk 'NONE')
                      , COALESCE(pk 'NULL')
                      , fk
                      , ftbl), '
    UNION ALL
    ') || ';'
    FROM   x;
    

    产生这样的查询:

    SELECT 'some_tbl' AS tbl
         , 'some_tbl_id' AS pk
         , some_tbl_id::text AS pk_val
         , 'fk_tbl_id' AS fk
         , 'fk_tbl' AS ftbl
    FROM   some_tbl WHERE fk_tbl_id IS NULL
    UNION ALL
    SELECT 'other_tbl' AS tbl
         , 'other_tbl_id' AS pk
         , other_tbl_id::text AS pk_val
         , 'some_name_id' AS fk
         , 'fk_tbl' AS ftbl
    FROM   other_tbl WHERE some_name_id IS NULL;
    

    产生这样的输出:

        tbl    |     pk       | pk_val |    fk        |  ftbl
    -----------+--------------+--------+--------------+--------
     some_tbl  | some_tbl_id  | 49     | fk_tbl_id    | fk_tbl
     some_tbl  | some_tbl_id  | 58     | fk_tbl_id    | fk_tbl
     other_tbl | other_tbl_id | 66     | some_name_id | fk_tbl
     other_tbl | other_tbl_id | 67     | some_name_id | fk_tbl
    
    • 不能可靠地覆盖多列外键或主键。为此,您必须使查询更复杂。

    • 我将所有主键转换为text以覆盖所有类型。

    • 修改或删除这些行以查找指向其他或任何列/表的外键:

      AND    c.confrelid = 'fk_tbl'::regclass
      AND    f.attname = 'fk_tbl_id' -- and only this column
      
    • 使用 PostgreSQL 9.1.4 测试。我使用pg_catalog 表。实际上,我在这里使用的任何东西都不会改变,但这并不能保证在主要版本中。如果您需要它在更新期间可靠地工作,请使用来自 information_schema 的表重写它。这会更慢,但可以肯定。

    • 我没有在生成的 DML 脚本中清理表名,因为 quote_ident() 会因模式限定名称而失败。您有责任避免使用像 "users; DELETE * FROM users;" 这样的有害表名。稍加努力,您可以分别检索架构名称和表名称并使用quote_ident()


    引用列中的 NULL 值

    我的第一个解决方案与您所要求的略有不同,因为您所描述的(据我所知)是不存在的。 NULL 的值是“未知的”,不能被引用。如果您确实想在具有指向 to 的 FK 约束的列中查找具有 NULL 值的行(当然不是指向具有 NULL 值的特定行),那么查询可以大大简化:

    WITH x AS (
     SELECT c.confrelid::regclass   AS ftbl
           ,quote_ident(f.attname)  AS fk
           ,quote_ident(pf.attname) AS pk
           ,string_agg(c.conrelid::regclass::text, ', ') AS referencing_tbls
     FROM   pg_constraint c
     JOIN   pg_attribute  f ON (f.attrelid, f.attnum) = (c.confrelid, c.confkey[1])
     LEFT   JOIN pg_constraint p  ON p.conrelid = c.confrelid AND p.contype = 'p'
     LEFT   JOIN pg_attribute  pf ON (pf.attrelid, pf.attnum)
                                   = (p.conrelid, p.conkey[1])
     WHERE  c.contype = 'f'
     -- AND    c.confrelid = 'fk_tbl'::regclass  -- only referring this tbl
     GROUP  BY 1, 2, 3
    )
    SELECT string_agg(format(
    'SELECT %L AS ftbl
         , %L AS pk
         , %s::text AS pk_val
         , %L AS fk
         , %L AS referencing_tbls
    FROM   %1$s WHERE %4$s IS NULL'
                      , ftbl
                      , COALESCE(pk, 'NONE')
                      , COALESCE(pk, 'NULL')
                      , fk
                      , referencing_tbls), '
    UNION ALL
    ') || ';'
    FROM   x;
    

    在整个数据库中查找所有此类行(注释掉对一个表的限制)。使用 Postgres 9.1.4 测试并为我工作。

    我将引用同一外部列的多个表分组到一个查询中,并添加一个引用表列表以提供概述。

    【讨论】:

    • 这非常非常接近。 SELECT 'some_tbl' AS tbl ,'some_tbl_id' AS pk ,some_tbl_id::text AS pk_val ,'some_col' AS fk ,'fk_tbl' AS ftbl FROM some_tbl WHERE some_col IS NULL 我认为需要引入一个 JOIN。我想在some_table 中找到列,它们是reference_table 的外键,其中reference_table.reference_value 为NULL。我认为 Erwin 的查询会找到 some_table.some_col 为 NULL 并且是 reference_table 的外键的地方。
    • 知道了! ,''' || ftbl || ''' AS ftbl FROM ' || tbl || ' INNER JOIN fk_tbl AS fkt ON ce.coded_element_key = ' || tbl || '.' || fk_col || ' WHERE fkt.reference_value IS NULL', '
    • @Freiheit:我的解决方案与您的要求略有不同,因为您所描述的(据我所知)是不存在的。 NULL 的值未知,无法引用。如果您只想在 fk-constraints 指向它的列中查找具有 NULL 值的行,那么查询可以大大简化。
    • 我的问题的根源是我有一个表reference_table,它应该在reference_value 中有一个值,但没有。这有许多来自我们模式中其他表的外键引用。我的团队怀疑在 some_table 上存储数据时某些代码中存在错误,它没有为“reference_value”分配值。要找出哪些代码可能没有正确设置reference_value,我们必须知道来自some_table 的表/行链接到它。如果我们只有几张带有reference_table 外键的表,这很容易解决。您的查询解决了很多表
    • @Freiheit:很酷,它有帮助!我添加了(更简单的)变体以在引用列中查找 NULL 值 - 面向公众。
    【解决方案2】:

    你想要这个查询的联合:​​

    select *
    from ((select 'other_table' as table_name,
                   other_table_key as primary_key,
                   'reference_table_key' as table_fk,
                   ot.reference_table_key
           from other_table ot left outer join
                reference_table rt
                on ot.reference_table_key = rt.reference_table_key
           where rt.reference_value is null
          ) union all
          (select 'another_table' as table_name,
                   another_table_key as primary_key,
                   'do_stuff_key' as table_fk,
                   at.do_stuff_key
           from another_table at left outer join
                reference_table rt
                on at.do_stuff_key = rt.reference_table_key
           where rt.reference_value is null
          )
         ) t
    

    【讨论】:

    • 这个解决方案并不理想。我列出了两个示例表,但总共大约有 50 个表可能包含 reference_table 的外键。我试图避免为每张桌子一遍又一遍地手动操作。
    • 您可以使用 information_schema 中的表来创建您想要的查询。至少,您可以在 Excel 的两列中列出表和键,并使用 Excel 函数生成每个子查询。
    • 感谢您的提示。我会阅读 information_schema,看看我能找到什么。
    猜你喜欢
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    相关资源
    最近更新 更多