3

私は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]))
)


重要な列の説明:
* ad_tree_id = ツリー グループ ID (ad_tree テーブルに接続)
* node_id = ノード ID
* parent_id = 親ノード ID (0 の場合 => ノードが一番上にあることを意味します)

残りの列は無視できます。


たとえば、次のような 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)
    ...

たとえば、Group1 のAccountingノードとその子ノードを削除したいとします。つまり、次のノードも削除されます: Costing、Cost Type、Cost Element、Reset Accounting...など。どうやってするの?

ソリューションは、JDBC を使用した SQL または Java 言語で作成できます (ただし、可能であれば SQL を使用することをお勧めします)。


更新: 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
);

WITH 句の中に引数 ( WHERE ad_tree_id=1001 AND node_id=101) を入れていることがわかります。引数ステートメントを WITH 句の外に置いて 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

    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
4

1 に答える 1

5

「テーブル構造を変更」したくないので、とにかく再帰クエリ(または再帰的に作業を行う関数)になります。FK制約を追加すると、「テーブル構造の変更」と見なされるためです。

これらの制限がない場合、最も洗練された解決策は、NULL前述の値を修正し、列にNOTNULL制約を追加することです。次に、コメントに記載されているように、最初に@lcでFK制約を追加します。ON DELETE CASCADE

再帰クエリ

DELETE書き込み可能なCTEを使用すると、次のようになります。

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)

これを1つのステップで行うには、データ変更CTE用のPostgreSQL9.1以降が必要です。SELECTそれ以外の場合は、行を収集するために別の実行を実行してから、を実行する必要がありますDELETE

->ライブデモ@sqlfiddle

于 2012-10-12T17:19:51.087 に答える