简体   繁体   English

如何从表中删除树节点及其子记录(没有级联删除)?

[英]How to delete a tree node and its childs records from a table (without cascade delete)?

I have this table in postgreSQL v9.1: 我在postgreSQL v9.1中有这个表:

CREATE TABLE ad_treenodemm
(
  ad_tree_id numeric(10,0) NOT NULL,
  node_id numeric(10,0) NOT NULL,
  ad_client_id numeric(10,0) NOT NULL,
  ad_org_id numeric(10,0) NOT NULL,
  name character varying(60) NOT NULL,
  isactive character(1) NOT NULL DEFAULT 'Y'::bpchar,
  created timestamp without time zone NOT NULL DEFAULT now(),
  createdby numeric(10,0) NOT NULL,
  updated timestamp without time zone NOT NULL DEFAULT now(),
  updatedby numeric(10,0) NOT NULL,
  parent_id numeric(10,0),
  seqno numeric(10,0),
  CONSTRAINT ad_treenodemm_pkey PRIMARY KEY (ad_tree_id , node_id ),
  CONSTRAINT adtree_adtreenodemm FOREIGN KEY (ad_tree_id)
      REFERENCES adempiere.ad_tree (ad_tree_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT ad_treenodemm_isactive_check CHECK (isactive = ANY (ARRAY['Y'::bpchar, 'N'::bpchar]))
)


Important columns description: 重要栏目说明:
* ad_tree_id = The tree group id (connected to ad_tree table) * ad_tree_id =树组ID(连接到ad_tree表)
* node_id = The node id * node_id =节点ID
* parent_id = The parent node id (if 0 => means the node is on the top) * parent_id =父节点id(如果0 =>表示节点在顶部)

The rests of columns can be ignored. 列的休止符可以忽略。


For example, I have the ad_treenodemm table data presentation like this: 例如,我有这样的ad_treenodemm表数据表示:

# Group1 (all node belows are assigned with ad_tree_id=1001)
    -Accounting (node_id=101, parent_id=0)
        -Costing (node_id=202, parent_id=101)
            -Cost Type (node_id=103, parent_id=202)
            -Cost Element (node_id=24, parent_id=202)
        -Client Accounting Processor (node_id=105, parent_id=101)
        -Reset Accounting (node_id=6, parent_id=101)
            ...

    -Finance (node_id=4110, parent_id=0)
        ...

# Group2 (all node belows are assigned with ad_tree_id=1002)
    ...

Let's say, I want to delete Accounting node and its child nodes in the Group1. 假设我想删除Group1中的Accounting节点及其子节点。 That means, it also deletes node: Costing, Cost Type, Cost Element, Reset Accounting,...etc. 这意味着,它还会删除节点:成本核算,成本类型,成本要素,重置会计等等。 How to do it? 怎么做?

The solution can be in SQL or Java language with JDBC (but SQL would be preferred if possible). 解决方案可以是带有JDBC的SQL或Java语言(但如果可能,SQL将是首选)。


UPDATE: I found a solution with WITH RECURSIVE (CTE) sql, however it's not too elegant: 更新:我找到了WITH RECURSIVE(CTE)sql的解决方案,但它并不太优雅:

WITH RECURSIVE temp(ad_tree_id, node_id, parent_id) AS (
    SELECT a.ad_tree_id, a.node_id, a.parent_id
    FROM ad_treenodemm a 
    WHERE ad_tree_id=1001 AND node_id=101      -- look at this

    UNION ALL

    SELECT b.ad_tree_id, b.node_id, b.parent_id
    FROM ad_treenodemm b
    INNER JOIN temp c on c.node_id = b.parent_id
    WHERE b.ad_tree_id=c.ad_tree_id
)
DELETE FROM ad_treenodemm a
WHERE (a.ad_tree_id, a.node_id) IN (
    SELECT ad_tree_id, node_id FROM temp
);

You see that I put the argument ( WHERE ad_tree_id=1001 AND node_id=101) inside the WITH clause. 您看到我将参数(WHERE ad_tree_id = 1001 AND node_id = 101)放在WITH子句中。 Anyone know how to improve the SQL by putting the argument statement outside the WITH clause? 有人知道如何通过将参数语句放在WITH子句之外来改进SQL吗?

For anyone who want to experiment the query without deleting the records, use this: 对于想要在不删除记录的情况下试验查询的任何人,请使用以下命令:

WITH RECURSIVE temp(ad_tree_id, node_id, parent_id) AS (
    SELECT a.ad_tree_id, a.node_id, a.parent_id
    FROM ad_treenodemm a 
    WHERE ad_tree_id=1001 AND node_id=101

    UNION ALL

    SELECT b.ad_tree_id, b.node_id, b.parent_id
    FROM ad_treenodemm b
    INNER JOIN temp c on c.node_id = b.parent_id
    WHERE b.ad_tree_id=c.ad_tree_id
)
SELECT * FROM ad_treenodemm a
WHERE (a.ad_tree_id, a.node_id) IN (
    SELECT ad_tree_id, node_id FROM temp
)
ORDER BY a.parent_id, a.node_id

Since you don't want to "alter the table structure", you are down to a recursive query anyway (or a function doing the work recursively). 由于您不想“改变表结构”,因此无论如何都要进行递归查询 (或递归执行工作的函数)。 Because adding a FK constraint would qualify as "altering the table structure". 因为添加FK约束将符合“改变表结构”的条件。

Without these restrictions the most elegant solution would be to fix the NULL values you mentioned and add a NOT NULL constraint to the column. 如果没有这些限制,最优雅的解决方案是修复您提到的NULL值并向列添加NOT NULL约束 Then add the FK constraint with ON DELETE CASCADE as mentioned in the comments, first by @lc. 然后使用注释中提到的ON DELETE CASCADE添加FK约束 ,首先由@lc添加。

Recursive query 递归查询

DELETE with writeable CTE could look like this: 具有可写CTE的DELETE可能如下所示:

WITH RECURSIVE x AS (
   SELECT ad_tree_id, node_id
   FROM   ad_treenodemm
   WHERE (ad_tree_id, node_id) = (1,5)   -- enter dead node walking here

   UNION  ALL
   SELECT a.ad_tree_id, a.node_id
   FROM   x
   JOIN   ad_treenodemm a ON a.parent_id = x.node_id
   )
DELETE FROM ad_treenodemm a
USING  x
WHERE (a.ad_tree_id, a.node_id) = (x.ad_tree_id, x.node_id)

To do this in one step you need PostgreSQL 9.1 or later for data-modifying CTEs . 要在一个步骤中执行此操作,您需要PostgreSQL 9.1或更高版本来进行数据修改CTE Else you have to run a separate SELECT to collect the rows and then DELETE . 否则,您必须运行单独的SELECT以收集行,然后再执行DELETE

-> Live demo @sqlfiddle - >现场演示@sqlfiddle

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

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