简体   繁体   English

获取所有引用(通过外键)表中特定行的行

[英]Get all the rows referencing (via foreign keys) a particular row in a table

This seems so simple, but I haven't been able to find an answer to this question.这看起来很简单,但我一直无法找到这个问题的答案。

What do I want?我想要什么? A master table with rows that delete themselves whenever they are not referenced (via foreign keys) anymore.一个主表,其中的行在不再被引用(通过外键)时会自行删除。 The solution may or may not be specific to PostgreSql.该解决方案可能特定于也可能不特定于 PostgreSql。

How?如何? One of my approaches to solving this problem (actually, the only approach so far) involves the following: For every table that references this master table, on UPDATE or DELETE of a row, to check for the referenced row in master, how many other other rows still refer to the referenced row.我解决这个问题的方法之一(实际上,到目前为止唯一的方法)涉及以下内容:对于引用此主表的每个表,在行的UPDATEDELETE上,检查 master 中引用的行,还有多少其他其他行仍然引用引用的行。 If it drops down to zero, then I delete that row in master as well.如果它降为零,那么我也会删除 master 中的那一行。

(If you have a better idea, I'd like to know!) (如果你有更好的主意,我想知道!)

In detail: I have one master table referenced by many others详细说明:我有一个被许多其他人引用的主表

CREATE TABLE master (
  id serial primary key,
  name text unique not null
);

All the other tables have the same format generally:所有其他表通常具有相同的格式:

CREATE TABLE other (
  ...
  master_id integer references master (id)
  ...
);

If one of these are not NULL , they refer to a row in master .如果其中一个不是NULL ,则它们引用master的一行。 If I go to this and try to delete it, I will get an error message, because it is already referred to:如果我去这个并尝试删除它,我会收到一条错误消息,因为它已经被引用了:

ERROR:  update or delete on table "master" violates foreign key constraint "other_master_id_fkey" on table "other"
DETAIL:  Key (id)=(1) is still referenced from table "other".
Time: 42.972 ms

Note that it doesn't take too long to figure this out even if I have many tables referencing master .请注意,即使我有很多引用master表,也不会花太长时间弄清楚这一点。 How do I find this information out without having to raise an error?如何在不引发错误的情况下找到这些信息?

You can do one of the following:您可以执行以下操作之一:

1) Add reference_count field to master table. 1) 将reference_count字段添加到主表。 Using triggers on detail tables increase the reference count whenever a row with this master_id is added.每当添加具有此master_id的行时,在详细表上使用触发器会增加reference count Decrease the count, when row gets deleted.当行被删除时减少计数。 When reference_count reaches 0 - delete the record.reference_count达到 0 时 - 删除记录。

2) Use pg_constraint table (details here ) to get the list of referencing tables and create a dynamic SQL query. 2) 使用pg_constraint表(详情请点击此处)获取引用表的列表并创建动态 SQL 查询。

3) Create triggers on every detail table, that deletes master_id in main table. 3) 在每个master_id上创建触发器,删除主表中的master_id Silence error messages with BEGIN ... EXCEPTION ... END .使用BEGIN ... EXCEPTION ... END错误消息静音。

In case someone wants a real count of rows in all other tables that reference a given master row, here is some PL/pgSQL.如果有人想要引用给定主行的所有其他表中的实际行数,这里有一些 PL/pgSQL。 Note that this works in plain case with single column constraints.请注意,这适用于具有单列约束的普通情况。 It gets more involved for multi-column constraints.它更多地涉及多列约束。

CREATE OR REPLACE FUNCTION count_references(master regclass, pkey_value integer,
    OUT "table" regclass, OUT count integer)
    RETURNS SETOF record 
    LANGUAGE 'plpgsql'
    VOLATILE 
AS $BODY$
declare
  x record;           -- constraint info for each table in question that references master
  sql text;           -- temporary buffer
begin
  for x in
    select conrelid, attname
    from pg_constraint
    join pg_attribute on conrelid=attrelid and attnum=conkey[1]
    where contype='f' and confrelid=master
      and confkey=( -- here we assume that FK references master's PK
        select conkey
        from pg_constraint
        where conrelid=master and contype='p'
      )
  loop
    "table" = x.conrelid;
    sql = format('select count(*) from only %s where %I=$1', "table", x.attname);
    execute sql into "count" using pkey_value;
    return next;
  end loop;
end
$BODY$;

Then use it like然后像这样使用它

select * from count_references('master', 1) where count>0

This will return a list of tables that have references to master table with id=1.这将返回一个表列表,这些表引用了 id=1 的主表。

SELECT * 
FROM master ma
WHERE EXISTS (
    SELECT *
    FROM other ot
    WHERE ot.master_id = ma.id
    );

Or, the other way round:或者,反过来:

SELECT * 
FROM other ot
WHERE EXISTS (
    SELECT *    
    FROM master ma
    WHERE ot.master_id = ma.id
    );

SO if you want to update (or delete) only the rows in master that are not referenced by other , you could:因此,如果您只想更新(或删除) master 中未被other引用的行,您可以:

UPDATE master ma
SET id = 1000+id
  , name = 'blue'
WHERE name = 'green'
AND NOT EXISTS (
    SELECT *
    FROM other ot
    WHERE ot.master_id = ma.id
    );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM