2010-05-13 17 views
9

Dada una tabla que representa una estructura de árbol jerárquico y tiene tres columnasUso de SQL para clonar una estructura de árbol representado en una base de datos

  1. ID (clave principal, no-autoincremental)
  2. ParentGroupID
  3. SomeValue

Conozco el nodo más bajo de esa rama, y ​​quiero copiarlo a una nueva rama con el mismo número de padres que también necesitan ser clonados.

Estoy tratando de escribir una sola instrucción SQL INSERT INTO que hará que una copia de cada fila que sea del mismo main tenga la primera parte GroupID en un nuevo GroupID.

Ejemplo tabla que comienza:

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

meta después de ejecutar un INSERT INTO sencilla:

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

estructura de árbol final

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

Los identificadores no siempre están bien espaciadas ya que se muestran los datos de esta demostración, por lo que no siempre puedo suponer que la ID de los padres es 1 menos que e ID actual para las filas que tienen padres.

Además, estoy tratando de hacer esto en T-SQL (para Microsoft SQL Server 2005 y superior).

Esto se siente como un ejercicio clásico que debe tener una respuesta de SQL puro, pero estoy demasiado acostumbrado a programar que mi mente no piensa en SQL relacional.

+3

¿Qué versión de SQL Server estás usando? –

+0

No tengo claro cómo se determinarán los ParentGroupID clonados. ¿Cómo consiguió el clon de B un ParentGroupId de 4 mientras que el clon de A obtuvo un ParentGroupId igual a su fila fuente? – Thomas

+0

Necesito admitir SQL Server 2005. (Actualicé la publicación original para indicar esto) – AmoebaMan17

Respuesta

3

Prueba de esto, basado en una consulta de Quassnoi 's artículo Adjacency List vs Nested Sets: SQL Server:

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 

resultado cuando se ejecuta en los datos de prueba:

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

¡Voy a probarlo! Gracias de nuevo Mark! Necesito comenzar a ponerme al día con tus publicaciones y con Quassnoi, ya que siempre parece que tienes una respuesta. – AmoebaMan17

2

Assming que el DI es una columna de identidad, con auto valores asignados Estoy haciendo esto de la manga, así que las aplicaciones de cualquier error de sintaxis. Espero que los comentarios hagan que el intento sea claro.

/* 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

No veo ninguna llamada a '@@ identity', ¿de dónde la obtienes? – Omu

+0

Estoy de acuerdo, tampoco la veo. Esperaba una cláusula OUTPUT en el IDMap CTE. Tal vez fue masticado por la niebla de la noche. Actualizaré la consulta. – mdma

Cuestiones relacionadas