删除行和引用已删除行的其他行

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)

Demo on SQLFiddle

您必须像下面这样在 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';

Online example


如果你想转换现有的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;