删除行和引用已删除行的其他行
Delete rows and other rows with a reference to the deleted row
我正在使用 PostgreSQL,我有一个 table family
这样的:
+----+-------+-----------+
| id | name | parent_id |
+----+-------+-----------+
| 1 | adam | 0 |
| 2 | eva | 0 |
| 3 | peter | 2 |
| 4 | pan | 3 |
+----+-------+-----------+
现在,当我删除此人时,我还希望 children 也被删除。因此,例如删除 WHERE name='peter'
会从列表中删除 'peter'
和 'pan'
。
当我删除 'eva'
时,她、'peter'
和 'pan'
被删除。
现在我考虑删除初始行并从删除的行中获取id。然后我会继续删除,直到现在 id 被返回。请注意,一个人只有 parent 个,但可以有多个 children.
SQL有解决这个问题的好方法吗?如果没有,我如何找回所有已删除行的 ID?
您可以使用递归 CTE 生成与给定人员关联的 child 个 ID 列表,然后从 table:
中删除所有 ID
WITH RECURSIVE CTE AS (
SELECT id
FROM family
WHERE name = 'eva'
UNION ALL
SELECT f.id
FROM family f
JOIN CTE ON f.parent_id = CTE.id
)
DELETE
FROM family
WHERE id IN (SELECT id FROM CTE)
您必须像下面这样在 table 上使用触发器:
Only for delete action:
CREATE OR REPLACE FUNCTION delete_child() RETURNS TRIGGER AS $$
BEGIN
DELETE FROM family WHERE parent_id = OLD.id;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
Reuse for multiple action on same table:
CREATE OR REPLACE FUNCTION delete_child() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
DELETE FROM family WHERE parent_id = OLD.id;
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
Create a Trigger:
CREATE TRIGGER trigger_delete_child
AFTER DELETE ON family
FOR EACH ROW EXECUTE PROCEDURE delete_child();
最好的解决方案是创建一个用 on delete cascade
定义的合适的外键。这需要在 parent_id 列中存储 NULL
值而不是魔术 "zero":
create table family
(
id int primary key,
name varchar(5),
parent_id int,
foreign key (parent_id) references family on delete cascade
);
那么你只需要:
delete from family
where name = 'peter';
如果你想转换现有的table和数据,你可以这样做:
--- change all zeroes to NULL
update family
set parent_id = null
where parent_id = 0;
-- add a primary key in order to be able to create a foreign key
alter table family add primary key (id);
-- add the foreign key
alter table family
add foreign key (parent_id) references family (id)
on delete cascade;
我正在使用 PostgreSQL,我有一个 table family
这样的:
+----+-------+-----------+
| id | name | parent_id |
+----+-------+-----------+
| 1 | adam | 0 |
| 2 | eva | 0 |
| 3 | peter | 2 |
| 4 | pan | 3 |
+----+-------+-----------+
现在,当我删除此人时,我还希望 children 也被删除。因此,例如删除 WHERE name='peter'
会从列表中删除 'peter'
和 'pan'
。
当我删除 'eva'
时,她、'peter'
和 'pan'
被删除。
现在我考虑删除初始行并从删除的行中获取id。然后我会继续删除,直到现在 id 被返回。请注意,一个人只有 parent 个,但可以有多个 children.
SQL有解决这个问题的好方法吗?如果没有,我如何找回所有已删除行的 ID?
您可以使用递归 CTE 生成与给定人员关联的 child 个 ID 列表,然后从 table:
中删除所有 IDWITH RECURSIVE CTE AS (
SELECT id
FROM family
WHERE name = 'eva'
UNION ALL
SELECT f.id
FROM family f
JOIN CTE ON f.parent_id = CTE.id
)
DELETE
FROM family
WHERE id IN (SELECT id FROM CTE)
您必须像下面这样在 table 上使用触发器:
Only for delete action:
CREATE OR REPLACE FUNCTION delete_child() RETURNS TRIGGER AS $$
BEGIN
DELETE FROM family WHERE parent_id = OLD.id;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
Reuse for multiple action on same table:
CREATE OR REPLACE FUNCTION delete_child() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
DELETE FROM family WHERE parent_id = OLD.id;
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
Create a Trigger:
CREATE TRIGGER trigger_delete_child
AFTER DELETE ON family
FOR EACH ROW EXECUTE PROCEDURE delete_child();
最好的解决方案是创建一个用 on delete cascade
定义的合适的外键。这需要在 parent_id 列中存储 NULL
值而不是魔术 "zero":
create table family
(
id int primary key,
name varchar(5),
parent_id int,
foreign key (parent_id) references family on delete cascade
);
那么你只需要:
delete from family
where name = 'peter';
如果你想转换现有的table和数据,你可以这样做:
--- change all zeroes to NULL
update family
set parent_id = null
where parent_id = 0;
-- add a primary key in order to be able to create a foreign key
alter table family add primary key (id);
-- add the foreign key
alter table family
add foreign key (parent_id) references family (id)
on delete cascade;