2010-05-13 13 views
9

Data una tabella che rappresenta una struttura gerarchica ad albero e ha tre colonneUtilizzare SQL per clonare una struttura ad albero rappresentato in un database

  1. ID (chiave primaria, non-autoincrementing)
  2. ParentGroupID
  3. SomeValue

Conosco il nodo più basso di quel ramo e voglio copiarlo in un nuovo ramo con lo stesso numero di genitori che devono anche essere clonati.

Sto provando a scrivere una singola istruzione SQL INSERT INTO che creerà una copia di ogni riga che è dello stesso main ha è parte uno GroupID in un nuovo GroupID.

Esempio tabella di iniziare:

ID | ParentGroupID | SomeValue 
------------------------ 
1 |  -1  | a 
2 |  1  | b 
3 |  2  | c 

gol dopo ho eseguito una semplice INSERT INTO:

ID | ParentGroupID | SomeValue 
------------------------ 
1 |  -1  | a 
2 |  1  | b 
3 |  2  | c 
4 |  -1  | a-cloned 
5 |  4  | b-cloned 
6 |  5  | c-cloned 

finale struttura ad albero

+--a (1) 
| +--b (2) 
|  +--c (3) 
| 
+--a-cloned (4) 
| +--b-cloned (5) 
|  +--c-cloned (6) 

Gli ID non sono sempre ben distanziati perché questi dati dimostrativi vengono visualizzati, quindi non posso sempre presumere che l'ID del genitore sia 1 in meno di th L'ID corrente per le righe che hanno genitori.

Inoltre, sto cercando di farlo in T-SQL (per Microsoft SQL Server 2005 e versioni successive).

Sembra un esercizio classico che dovrebbe avere una risposta puramente SQL, ma sono troppo abituato a programmare che la mia mente non pensa in SQL relazionale.

+3

Quale versione di SQL Server si sta utilizzando? –

+0

Non sono chiaro come debbano essere determinati i ParentGroupID clonati. In che modo il clone di B ha ottenuto un ParentGroupId di 4 mentre il clone di A ha ottenuto un ParentGroupId uguale alla sua riga di origine? – Thomas

+0

Devo supportare SQL Server 2005. (Ho aggiornato il post originale per indicarlo) – AmoebaMan17

risposta

3

Prova questo, sulla base di una query da articolo Adjacency List vs Nested Sets: SQL ServerQuassnoi s':

WITH q AS 
(
    SELECT h.*, 1 AS level 
    FROM Table1 h 
    WHERE id = 3 
    UNION ALL 
    SELECT hp.*, level + 1 
    FROM q 
    JOIN Table1 hp 
    ON  hp.id = q.ParentGroupID 
), q2 AS (
    SELECT 
     ID, 
     ParentGroupID, 
     SomeValue, 
     (SELECT MAX(level) FROM q) - level AS level 
    FROM q 
) 
INSERT INTO table1 
SELECT 
    (SELECT MAX(ID) FROM Table1) + level + 1 AS ID, 
    CASE WHEN level = 0 THEN -1 
     ELSE (SELECT MAX(ID) FROM Table1) + level 
    END AS ParentGroupID, 
    SomeValue + '-cloned' 
FROM q2 

Risultato quando eseguito sul tuo dati di test:

ID ParentGroupID SomeValue 
1 -1    a   
2 1    b   
3 2    c   
4 -1    a-cloned 
5 4    b-cloned 
6 5    c-cloned 
+0

Ho intenzione di provarlo! Grazie ancora Marco! Devo iniziare a recuperare i post di te e di Quassnoi, dal momento che sembra che tu abbia sempre una risposta! – AmoebaMan17

2

Assming che ID è una colonna di identità, con l'auto valori assegnati. Lo sto facendo a braccio, quindi mi compiaccio per eventuali errori di sintassi. Spero che i commenti chiariscano l'intento.

/* Find all ancestors for a given ID */ 
WITH Ancestors(ChildID, AncestorID) AS 
(
    SELECT id AS ChildID, id As AncestorID 
    FROM YourTable WHERE ParentGroupID=--1 
    UNION ALL 
    SELECT a.ChildID, d.ParentGroupID 
    FROM Ancestors AS a INNER JOIN 
     YourTable d ON d.ID = a.AncestorID 
    WHERE (b1.ParentGroupID <> -1)) 
), 
/* Insert new rows for all ancestors of a given ID and save the results, so we have access to the new ID. we also have a column for the old ID. */ 
IDMap(ID, ParentGroupID, SomeValue, OldID) AS 
{ 
    // insert, using identity column assigned ID, and save the old ID 
    INSERT INTO YourTable SELECT d.ParentGroupID, d.SomeValue+"-cloned", d.ID FROM YourTable d 
     INNER JOIN Ancestors a ON a.ChildID = d.ID 
     WHERE a.AncestorID=<the ID to clone> 
} 
/* Now update the parentID in the inserted data to the new ID */ 
UPDATE YourTable 
SET ParentGroupID = map.ID 
FROM YouTable t INNER JOIN (SELECT * FROM IDMap) map 
    ON t.ParentGroupID=map.OldID 
+0

Non vedo alcuna chiamata a '@@ identity', dove la trovi? – Omu

+0

Sono d'accordo, non la vedo neanche io. Mi aspettavo una clausola OUTPUT su IDMap CTE. Forse è stato masticato dalla nebbia notturna del cervello. Aggiornerò la query – mdma

Problemi correlati