2009-05-19 16 views
15

Ho una tabella con dati gerarchici.
Una colonna "ParentId" che contiene l'ID ("ID" - colonna chiave) del suo genitore.Eliminazione di dati gerarchici nella tabella SQL

Quando si elimina una riga, voglio eliminare tutti i bambini (tutti i livelli di nidificazione).

Come fare?

Grazie

risposta

4

Quando il numero di righe non è troppo grande, l'approccio ricorsivo di erikkallen funziona.

Ecco un'alternativa che utilizza una tabella temporanea per raccogliere tutti i bambini:

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) 

Si inizia con la riga con @delete_id e discende da lì. L'affermazione in cui è di proteggere dalla ricorsione; se sei sicuro che non ce ne sono, puoi lasciarlo fuori.

+0

Ho intenzione di provare che – markiz

+0

Non sono così forte in sql. quindi ti chiedo: Perché hai bisogno: "seleziona id dalla tabella dove id = @delete_id" perché non puoi usare @delete_id come valore? – markiz

+0

@markiz: buon punto, modifico la risposta! – Andomar

3

Dipende come si memorizza la gerarchia. Se hai solo ParentID, allora potrebbe non essere l'approccio più efficace che hai preso. Per facilità di manipolazione sotto-albero si dovrebbe avere una colonna aggiuntiva Parents che wouls memorizzare tutti gli ID genitore come:

/1/20/25/40 

In questo modo sarete in grado di ottenere tutti i sub-nodi semplicemente:

where Parents like @NodeParents + '%' 

Secondo approccio
Invece di solo ParentID potresti anche avere valori left e right. Gli inserimenti che eseguono in questo modo sono più lenti, ma le operazioni di selezione sono estremamente veloci. Soprattutto quando si tratta di nodi sub-albero ... http://en.wikipedia.org/wiki/Tree_traversal

Terzo approccio
controllo CTE ricorsive se si utilizza SQL 2005 +

quarto approccio
Se si utilizza SQL 2008, verificare il tipo hierarchyid . Dà abbastanza possibilità per il tuo caso. http://msdn.microsoft.com/en-us/magazine/cc794278.aspx

+0

NO, non voglio per memorizzare tutta genitori a catena in una colonna, perché ci genitori continuo cambiamento è coinvolto. E sarà difficile tenere traccia di tutto ciò. Non si può fare come è adesso? – markiz

+0

Quali sono le operazioni principali sui dati della gerarchia? È inserimenti, aggiornamenti o letture? –

+0

Sono tendenzialmente d'accordo con il primo approccio: abbiamo tabelle di dati gerarchici con cui stiamo facendo la stessa cosa. Aiuta a sbarazzarsi dei bambini, e aiuta anche se è necessario eseguire l'elaborazione basata sul percorso dell'albero (come dover restituire rapidamente tutti i figli di un genitore per il calcolo). Inizialmente, abbiamo provato a utilizzare i trigger per mantenerlo, ma abbiamo davvero scoperto che le implicazioni sulle prestazioni quando si aggiungevano grandi quantità di dati erano proibitive. –

2

Aggiungi un trigger per la tabella come questa

creare grilletto TD_MyTable su MyTable per cancellare come - Elimina un livello di bambini eliminare M da cancellato D join interno myTable M su D.ID = M.ID

Ogni eliminazione chiamerà un'eliminazione sulla stessa tabella, chiamando ripetutamente il trigger. Controlla i libri online per ulteriori regole. Potrebbe esserci una limitazione al numero di volte in cui un trigger può nidificare.

ST

+0

quei trigger disponibili in SQL SERVER 2005 Express? – markiz

+0

Credo che siano comunque devi scriverli da soli in espresso. Non c'è un mago per questo. – souLTower

+0

Credo che il trigger funzionerà, ma il problema con il trigger è che verrà attivato ad ogni eliminazione, anche nel caso in cui voglio cancellare solo una riga ... – markiz

0

Dipende dal database. Se si utilizza Oracle, si potrebbe fare qualcosa di simile:

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

ETA:

Senza CONNECT BY, diventa un po 'più complicato. Come altri hanno suggerito, un trigger o un vincolo di eliminazione a cascata sarebbe probabilmente il più semplice.

+0

Sto usando MS SQL SERVER 2005 express – markiz

4

Aggiungere un vincolo di chiave esterna. Il seguente esempio funziona per MySQL (syntax reference):

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

Ciò operare sul livello di database, il DBMS garantire che una volta riga viene eliminata, verranno eliminate tutte le righe che fanno riferimento, anche.

+0

autoreferenziale le eliminazioni a cascata non sono supportate da SQL Server 2005. Verrà visualizzato un errore quando si tenta di eliminare una riga con righe "secondarie". –

+0

L'autore non aveva specificato un DBMS al punto in cui ho scritto questa risposta. Lo lascerò per riferimento. – soulmerge

+0

Ah questo è abbastanza giusto allora. Grazie per il chiarimento. –

9

Su server SQL: utilizzare una query ricorsiva. Dato CREATE TABLE tmp (Id int, Genitori int), utilizzare

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 
Problemi correlati