我如何 SELECT all (table, column) 引用相同的列作为外键

How do I SELECT all (table, column) referencing the same column as foreign key

(TL;最后是DR)

我正在合并 2 个大小合适的 postgres 数据库。 由于存在 ID 冲突和许多外键,我会喜欢 UPDATE foo SET bar_id = bar_id + 100000 CASCADE 是 SQL 中的一个东西,所以它神奇地相应地更新了所有内容。不幸的是,它不是。

所以我想使用一个 LOOP 结构(见下文),它将简单地编辑各处的引用。 我想要一个 select 查询 return 所有 table_name,column_name 引用我想要的列。

DO
$$
DECLARE
    rec record;
BEGIN
    FOR rec IN 
    (SELECT table_name, column_name FROM /*??*/ WHERE /*??*/)   -- <<< This line    
    LOOP  
    EXECUTE format('UPDATE %I SET %s = %s + 100000 ;',  
        rec.table_name,rec.column_name,rec.column_name); 
    END LOOP;
END;
$$
LANGUAGE plpgsql;

我已经知道如何让所有 tables (+column_name) 具有我在外键列与其引用的列共享名称时使用的特定 column_name .或者即使它是 column_name 我知道的列表:

SELECT col.table_name, col.column_name
        FROM information_schema.columns col 
        right join
        information_schema.tables tab 
        ON col.table_name = tab.table_name 
        WHERE column_name = 'foo_id' 
        --  IN ('FOO_ID','BAR_FOO_ID') | or : like '%foo_id' | both works well most of the time
        and tab.table_type = 'BASE TABLE' 

但是…… 我现在有一个 table PLACES,其中 place_id 列在至少 60 个不同的约束条件下被引用(匹配 LIKE '%place_id')。 然后是引用以其他方式命名的地点 ID 的列 ,例如 'foo_currentplace'、'foo_storageroom'、'foo_lastrecomposition_place'、'operating_theatre' 等。另一方面,有从 placetype table 引用 'placetype_id' 的列是 LIKE '%place%' ,我不想更改 placetype_id,所以我们无法猜测是哪一列包括或不仅包括他们的名字。

我知道有 information_schema.table_constraints table,但它没有告诉引用的列。 如果我们可以从约束名称中获得定义,则可以匹配:
ILIKE format('%%REFERENCES %s(%s)%%',table_name,column_name)
但定义也不是 table_constraints table 的一部分。

(对于那些想知道的人,我正在研究与绝育服务相关的医院数据库。)

我想要什么/TL;DR

我需要一个 SELECT 查询(或函数定义)returning 整个数据库的所有列 (schema_name,table_name,column_name)(table_name,column_name) 具有引用一个外键约束指定的列(参数)。

评论可能太长了。

Postgres 绝对支持级联更新约束(参见 here)。但是,这是约束的 定义 的一部分,而不是 update 语句:

ALTER TABLE foo 
    ADD FOREIGN KEY fk_foo_bar_id FOREIGN KEY (bar_id) REFERENCES bar(bar_id)
    ON UPDATE CASCADE;
----^ this is the important part

注意:您需要重新定义所有外键约束,使它们成为级联更新约束。

而且,作为编辑评论,我通常会避免级联更新外键约束(因为我不喜欢主键更改值的想法)。但是,这绝对是它们有用的地方。

然后,如果您更改 bar.bar_id,更改将反映在 foo 中。 Here 是一个 db<>fiddle.

好的,我已经完成了:-)
以下查询 returns 数据库中的每一列引用 FOO_TABLE.foo_column 作为外键:

SELECT 
fk.table_schema as schema, --optional in my case, I only use public
fk.table_name as table,
substring(fk.constraint_def, 14, position(')' in constraint_def)-14) as column
FROM
    (SELECT tc.*,
     pg_get_constraintdef(c.oid) as constraint_def
     --,c.oid 
     from pg_constraint c
     left join information_schema.table_constraints tc
     on c.conname = tc.constraint_name
     where tc.constraint_type = 'FOREIGN KEY')
     as fk    
WHERE constraint_def ILIKE format('%%REFERENCES %s(%s)%%',
                                 'FOO_TABLE','foo_column')  
ORDER BY table_schema,table_name,3;

Test it there

我发现 information_schema.table_constraints 大部分信息来自 pg_constraint,包括内部参考 OID,并且有一个内置函数 pg_get_constraintdef() 返回从 OID.
定义约束对象 然后,定义的一些子字符串足以提取 column_name AND 使用我在问题中准备的 (I)LIKE 过滤器过滤引用的列。

---------------- 其他答案-----------------

我根据改进@Abelisto 建议构建的另一个可接受的查询:

SELECT table_name, column_name
FROM (SELECT table_name, SUBSTR(column_name, 2, LENGTH(column_name)-2) as column_name,
             referenced_table,SUBSTR(referenced_column, 2, LENGTH(referenced_column)-2) as referenced_column
      FROM(select
             conrelid::regclass::text as table_name,
            (select array_agg(attname) from pg_attribute where conrelid = attrelid and attnum = any(conkey))::text as column_name,
             confrelid::regclass::text as referenced_table,
            (select array_agg(attname) from pg_attribute where confrelid = attrelid and attnum = any(confkey))::text as referenced_column
             from pg_constraint where contype = 'f' 
             ) b ) a
WHERE (referenced_table, referenced_column) = 'FOO_TABLE','foo_column');

Test it there

我认为表演在那里并不重要,所以应该根据副需求来选择。我认为我的第一个解决方案具有获得约束定义的优势,如果你想改变它(例如,添加一个 ON UPDATE CASCADE 子句),但第二个似乎更“紧凑”,只是为了返回table.column.