13

这看起来很简单,但我一直无法找到这个问题的答案。

我想要什么?一个主表,其中的行在不再被引用(通过外键)时会自行删除。该解决方案可能特定于 PostgreSql,也可能不特定于 PostgreSql。

如何?我解决此问题的一种方法(实际上是迄今为止唯一的方法)涉及以下内容:对于在行上UPDATEDELETE行上引用此主表的每个表,检查主表中的引用行,还有多少其他行仍然参考引用的行。如果它下降到零,那么我也会在 master 中删除该行。

(如果你有更好的主意,我想知道!)

详细说明: 我有一张被许多其他人引用的主表

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

所有其他表格通常具有相同的格式:

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

如果其中一个不是NULL,它们指的是 中的一行master。如果我去这个并尝试删除它,我会收到一条错误消息,因为它已经被引用:

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

请注意,即使我有很多表引用master. 如何在不引发错误的情况下找出这些信息?

4

3 回答 3

5

您可以执行以下操作之一:

1)将reference_count字段添加到主表。在明细表上使用触发器会增加添加reference count行的master_id时间。当行被删除时减少计数。当reference_count达到 0 - 删除记录。

2) 使用pg_constraint表(详见此处)获取引用表的列表并创建动态 SQL 查询。

master_id3)在每个明细表上创建触发器,在主表中删除。使用 静默错误消息BEGIN ... EXCEPTION ... END

于 2013-01-16T11:35:51.087 回答
3

如果有人想要引用给定主行的所有其他表中的真实行数,这里有一些 PL/pgSQL。请注意,这适用于具有单列约束的普通情况。它更多地涉及多列约束。

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

然后像这样使用它

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

这将返回一个表的列表,这些表引用了 id=1 的主表。

于 2020-01-27T05:17:32.123 回答
-1
SELECT * 
FROM master ma
WHERE EXISTS (
    SELECT *
    FROM other ot
    WHERE ot.master_id = ma.id
    );

或者,反过来:

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

因此,如果您只想更新(或删除) 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
    );
于 2013-01-16T11:24:29.393 回答