这似乎很简单,但是我无法找到这个问题的答案。
我想要什么?具有行的主表会在不再引用(通过外键)时删除自己。解决方案可能特定于PostgreSql,也可能不特定于此。
How?解决此问题的一种方法(实际上,到目前为止,唯一的方法)涉及以下内容:对于引用该主表的每个表,在行UPDATE
或DELETE
上,要检查主数据库中的引用行,还有多少其他行仍引用该引用行。如果它降到零,那么我也要删除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
的表,也无需花太多时间就可以弄清楚。我如何找到这些信息而不必引发错误?
您可以执行以下任一操作:
1)将reference_count
字段添加到主表。每当添加带有reference count
的行时,在明细表上使用触发器都会增加master_id
。当删除行时减少计数。 reference_count
达到0时-删除记录。
2)使用pg_constraint
表(详细信息here)获取引用表的列表并创建动态SQL查询。
3)在每个明细表上创建触发器,从而删除主表中的master_id
。使用BEGIN ... EXCEPTION ... END
沉默错误消息。
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
);
如果有人想要引用引用给定主行的所有其他表中的行的真实计数,则这里是一些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 %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的主表的引用。