2009-05-19 14 views
15

私は階層データを持つテーブルを持っています。
親のId( "ID" - キー列)を保持する列 "ParentId"。SQLテーブル内の階層データの削除

行を削除するときに、すべての子(すべてのネスティングレベル)を削除したいとします。

どうすればよいですか?

ありがとうございました

答えて

4

行数がそれほど大きくない場合、erikkallenの再帰的アプローチが機能します。

は、ここですべての子供たちを収集するために一時テーブルを使用して代替です:

create table #nodes (id int primary key) 
insert into #nodes (id) values (@delete_id) 
while @@rowcount > 0 
    insert into #nodes 
    select distinct child.id 
    from table child 
    inner join #nodes parent on child.parentid = parent.id 
    where child.id not in (select id from #nodes) 

delete 
from table 
where id in (select id from #nodes) 

それは@delete_id持つ行で始まり、そこから下降します。 whereステートメントは再帰から保護することです。何もないと確信が持てば、あなたはそれを放棄することができます。

+0

私はそれを試してみます – markiz

+0

私はSQLであまり強くないです。だから私はこれを尋ねます: なぜ必要なのですか?「id = @delete_idのテーブルからIDを選択」 なぜ@delete_idを値として使うことができないのですか? – markiz

+0

@markiz:良い点、私は答えを編集します! – Andomar

3

階層をどのように保存するかによって異なります。 ParentIDしか持っていない場合は、あなたが取った最も効果的なアプローチではないかもしれません。

where Parents like @NodeParents + '%' 

:あなたは、単に、すべてのサブノードを取得することができます

/1/20/25/40 

この方法:サブツリー操作を容易にするために、あなたは次のようにすべての親IDを格納しwouls追加の列Parentsを持つべきです2番目のアプローチ
ParentIDの代わりに、leftrightという値を使用することもできます。この方法で挿入する挿入は遅くなりますが、選択操作は非常に高速です。
チェック再帰CTEのは、あなたがSQL 2008を使用している場合は、hierarchyid型の種類を確認あなたはSQL 2005+

第四のアプローチを使用している場合
...サブツリーノードでhttp://en.wikipedia.org/wiki/Tree_traversal

第三のアプローチを扱う場合は特に。それはあなたのケースに十分な可能性を与えます。 http://msdn.microsoft.com/en-us/magazine/cc794278.aspx

+0

はNO、私は列全体の親チェーンを保存したくない、そこに一定の両親が関与して変化するので。そしてそれをすべて追跡するのは難しいでしょう。 今のところそれはできませんか? – markiz

+0

階層データに対する主な操作は何ですか?それは挿入、更新、または読み込みですか? –

+0

私は最初のアプローチに同意する傾向があります。同じことをしている階層的なデータの表があります。子を取り除くのに役立ちます。また、ツリーのパスベースの処理(計算のために親の子をすべて素早く返さなければならない場合など)が必要な場合にも役立ちます。 元々これを維持するためにトリガーを使用しようとしましたが、大量のデータを追加する際のパフォーマンスの影響は厳しいものでした。 –

2

として、削除のためにmytableは上のトリガーTD_MyTableを作成するには、この

のように表にトリガーを追加 - D.IDにmytableはM に参加削除Dの内側からMを削除する子ども の1つのレベルを削除します= M.ID

各削除は同じテーブルで削除を呼び出し、繰り返しトリガを呼び出します。追加ルールについては、オンラインで書籍をチェックしてください。トリガーがネストできる回数には制限があります。

ST

+0

これらのトリガーはSQL Server 2005 Expressで使用できますか? – markiz

+0

しかし、あなたは彼らを急いで自分で書く必要があると思います。それのためのウィザードはありません。 – souLTower

+0

私はトリガーが動作すると思いますが、トリガーの問題は、1つの行だけを削除したい場合でも、すべての削除時に有効になるということです。 – markiz

0

データベースによって異なります。 Oracleを使用している場合、あなたはこのような何かを行うことができます:

DELETE FROM Table WHERE ID IN (
    SELECT ID FROM Table 
    START WITH ID = id_to_delete 
    CONNECT BY PRIOR.ID = ParentID 
) 

ETA:CONNECT BYなし

が、それは少しトリッキーを取得します。他の人が示唆しているように、トリガまたはカスケード削除の制約がおそらく最も簡単です。

+0

MS SQL SERVER 2005 expressを使用しています – markiz

4

外部キー制約を追加します。次の例は、MySQL(syntax reference)の作品:

ALTER TABLE yourTable 
ADD CONSTRAINT makeUpAConstraintName 
FOREIGN KEY (ParentID) REFERENCES yourTable (ID) 
ON DELETE CASCADE; 

これはデータベースレベルで動作し、DBMSが一度行が削除されていることを確認し、すべての参照の行も削除されます。

+0

自己参照カスケード削除はSQL Server 2005ではサポートされていません。「子」行の行を削除しようとするとエラーが発生します。 –

+0

著者はこの回答を書いた時点でDBMSを指定していませんでした。私は参考のためにそれを残します。 – soulmerge

+0

ああ、十分に公正です。明確化のためにありがとう。 –

9

SQL Serverの場合:再帰的なクエリを使用します。指定されたテーブルのTMP(同上int型、親int)をCREATE、あなたが望むもの

WITH x(Id) AS (
    SELECT @Id 
    UNION ALL 
    SELECT tmp.Id 
     FROM tmp 
     JOIN x ON tmp.Parent = x.Id 
) 
DELETE tmp 
    FROM x 
    JOIN tmp ON tmp.Id = x.Id 
0

トリガは唯一の32のレベルの深さ以下の階層のために使用することができます。

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/05/11/defensive-database-programming-fun-with-triggers.aspx

+0

はいよりも優れていると私は本当に驚いています。最大レベルの深さは約8.9です。 とにかく、データベースで実行されるすべての削除コマンドでトリガーを有効にする必要はないので、トリガーを使用するつもりはないと思います。 – markiz