usando recursivas modelo jerárquicas jerarquico jerarquicas jerarquica informatica ejemplos definicion datos consultas caracteristicas busqueda arbol sql database-design postgresql

sql - recursivas - jerarquicas



clonando datos jerárquicos (4)

Esto suena como un ejercicio de "SQL For Smarties" de Joe Celko ...

No tengo mi copia a mano, pero creo que es un libro que te ayudará bastante si este es el tipo de problemas que necesitas resolver.

supongamos que tengo una tabla jerárquica de referencia automática construida de la manera clásica como esta:

CREATE TABLE test (name text,id serial primary key,parent_id integer references test); insert into test (name,id,parent_id) values (''root1'',1,NULL),(''root2'',2,NULL),(''root1sub1'',3,1),(''root1sub2'',4,1),(''root 2sub1'',5,2),(''root2sub2'',6,2); testdb=# select * from test; name | id | parent_id -----------+----+----------- root1 | 1 | root2 | 2 | root1sub1 | 3 | 1 root1sub2 | 4 | 1 root2sub1 | 5 | 2 root2sub2 | 6 | 2

Lo que necesito ahora es una función (preferiblemente en sql simple) que tomaría la identificación de un registro de prueba y clonaría todos los registros adjuntos (incluido el dado). Los registros clonados deben tener nuevos identificadores, por supuesto. El resultado deseado le gustaría, por ejemplo:

Select * from cloningfunction(2); name | id | parent_id -----------+----+----------- root2 | 7 | root2sub1 | 8 | 7 root2sub2 | 9 | 7

¿Alguna sugerencia? Estoy usando PostgreSQL 8.3.


Tratar este resultado recursivamente es complicado (aunque posible). Sin embargo, generalmente no es muy eficiente y hay una forma mucho mejor de resolver este problema.

Básicamente, aumentas la tabla con una columna extra que rastrea el árbol hasta la parte superior. Lo llamaré "Upchain". Es solo una cadena larga que se ve así:

name | id | parent_id | upchain root1 | 1 | NULL | 1: root2 | 2 | NULL | 2: root1sub1 | 3 | 1 | 1:3: root1sub2 | 4 | 1 | 1:4: root2sub1 | 5 | 2 | 2:5: root2sub2 | 6 | 2 | 2:6: root1sub1sub1 | 7 | 3 | 1:3:7:

Es muy fácil mantener este campo actualizado utilizando un disparador sobre la mesa. (Disculpas por la terminología, pero siempre he hecho esto con SQL Server). Cada vez que agrega o elimina un registro, o actualiza el campo parent_id, solo necesita actualizar el campo upchain en esa parte del árbol. Es un trabajo trivial porque simplemente toma la cadena ascendente del registro principal y agrega la identificación del registro actual. Todos los registros secundarios se identifican fácilmente utilizando LIKE para verificar los registros con la cadena de inicio en su cadena ascendente.

Lo que está haciendo de manera efectiva es cambiar un poco de actividad de escritura adicional para obtener un gran ahorro cuando llegue a leer los datos.

Cuando quiere seleccionar una rama completa en el árbol, es trivial. Supongamos que quiere la rama bajo el nodo 1. El nodo 1 tiene una cadena ascendente ''1:'' por lo que sabe que cualquier nodo en la rama del árbol debajo de ese nodo debe tener una cadena ascendente que comience ''1: ...''. Entonces haces esto:

SELECT * FROM table WHERE upchain LIKE ''1:%''

Esto es extremadamente rápido (indice el campo upchain por supuesto). Como beneficio adicional, también hace que muchas actividades sean extremadamente simples, como encontrar árboles parciales, nivelar dentro del árbol, etc.

Lo he usado en aplicaciones que rastrean grandes jerarquías de informes de empleados, pero puedes usarlo para casi cualquier estructura de árbol (desglose de partes, etc.)

Notas (para cualquiera que esté interesado):

  • No he dado un paso a paso del código SQL, pero una vez que obtiene el principio, es bastante simple de implementar. No soy un gran programador, así que hablo por experiencia.
  • Si ya tiene datos en la tabla, necesita hacer una actualización única para sincronizar inicialmente las cadenas ascendentes. Nuevamente, esto no es difícil ya que el código es muy similar al código de ACTUALIZACIÓN en los desencadenadores.
  • Esta técnica también es una buena manera de identificar referencias circulares que, de otro modo, podrían ser difíciles de detectar.


@Maximilian : Tiene razón, olvidamos su requisito real. ¿Qué tal un procedimiento almacenado recursivo? No estoy seguro de si esto es posible en PostgreSQL, pero aquí está una versión de SQL Server en funcionamiento:

CREATE PROCEDURE CloneNode @to_clone_id int, @parent_id int AS SET NOCOUNT ON DECLARE @new_node_id int, @child_id int INSERT INTO test (name, parent_id) SELECT name, @parent_id FROM test WHERE id = @to_clone_id SET @new_node_id = @@IDENTITY DECLARE @children_cursor CURSOR SET @children_cursor = CURSOR FOR SELECT id FROM test WHERE parent_id = @to_clone_id OPEN @children_cursor FETCH NEXT FROM @children_cursor INTO @child_id WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE CloneNode @child_id, @new_node_id FETCH NEXT FROM @children_cursor INTO @child_id END CLOSE @children_cursor DEALLOCATE @children_cursor

Su ejemplo se logra mediante EXECUTE CloneNode 2, null (el segundo parámetro es el nuevo nodo padre).