Recursividad en PL/SQL – Un ejemplo

El PL/SQL, como la mayoría de lenguajes modernos, nos permite implementar algoritmos recursivos en nuestros procesos. En los modelos relacionales de base de datos, es frecuente encontrarse con estructuras reflexivas. La recursividad nos facilita la programación de tareas que afectan a estos tipos de construcciones. A continuación presentamos un escenario donde aplicando recursividad disminuye la complejidad del proceso.

Hemos diseñado un sistema sencillo que almacena documentos en carpetas. El modelo simplificado de nuestro sistema es el siguiente:

Modelo "Carpetas"

Como se puede observar, una carpeta puede contener otras carpetas, y estas a su vez, otras. El usuario puede guardar una estructura como la que sigue:

Estructura de carpetas

Se requiere un proceso que sea capaz de borrar una carpeta y todo su contenido (sus documentos y sus subcarpetas). Para ello, crearemos un PL que hará uso de la recursividad:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- Para una carpeta concreta, borra sus documentos y sus subcarpetas.
-- Si las subcarpetas contiene documentos o subcarpetas, también son borradas.
--
-- El proceso se llama a si mismo de forma recursiva para cada una de las
-- subcarpetas de la carpeta indicada.
--
-- %param P_ID_CARPETA Id. de la carpeta a borrar
--
PROCEDURE BORRAR_CARPETA (P_ID_CARPETA    CARPETA.ID_CARPETA%TYPE) IS
Cursor cCptHij is
Select cpt.ID_CARPETA
from CARPETA cpt
where cpt.ID_CARPETA_PADRE = P_ID_CARPETA;
BEGIN
-- Primero debemos borrar las carpetas hijas y sus documentos
For rCptHij in cCptHij loop
BORRAR_CARPETA (rCptHij.ID_CARPETA);
end loop;
-- Una vez borradas la posibles carpetas hijas, borramos
-- los documentos de la carpeta y luego ésta
Delete from DOCUMENTO doc
where doc.ID_CARPETA = P_ID_CARPETA;
Delete from CARPETA cpt
where cpt.ID_CARPETA = P_ID_CARPETA;
END;

El proceso consiste en comprobar si la carpeta a borrar tiene subcarpetas, en cuyo caso, se llama a si mismo para eliminarlas.

De este modo tan sencillo hemos solucionado el problema planteado. Si no hubiéramos hecho uso de la recursividad, habríamos tenido que crear un PL más complejo, con un número máximo de niveles de subcarpetas a controlar.

En definitiva, hemos visto como el PL/SQL permite recursividad y también como ésta es útil para tratar datos jerárquicos.

Adjunto los scripts utilizados para aquellos que quieran probarlo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
CREATE TABLE DOCUMENTO
( ID_DOCUMENTO NUMBER(8, 0) NOT NULL
, NOMBRE VARCHAR2(256) NOT NULL
, ID_CARPETA NUMBER(8, 0)
, CONSTRAINT DOCUMENTO_PK PRIMARY KEY (ID_DOCUMENTO) ENABLE);
CREATE TABLE CARPETA
( ID_CARPETA NUMBER(8, 0) NOT NULL
, NOMBRE VARCHAR2(256) NOT NULL
, ID_CARPETA_PADRE NUMBER(8, 0)
, CONSTRAINT CARPETA_PK PRIMARY KEY (ID_CARPETA) ENABLE);
ALTER TABLE DOCUMENTO
ADD CONSTRAINT DOCUMENTO_CARPETA_FK FOREIGN KEY (ID_CARPETA)
                             REFERENCES CARPETA (ID_CARPETA) ENABLE;
ALTER TABLE CARPETA
ADD CONSTRAINT CARPETA_CARPETA_FK FOREIGN KEY (ID_CARPETA_PADRE)
                             REFERENCES CARPETA (ID_CARPETA)ENABLE;
-- CARPETAS
insert into carpeta (ID_CARPETA, NOMBRE, ID_CARPETA_PADRE)
values (1, 'Documentos', null);
insert into carpeta (ID_CARPETA, NOMBRE, ID_CARPETA_PADRE)
values (2, 'Reuniones', 1);
insert into carpeta (ID_CARPETA, NOMBRE, ID_CARPETA_PADRE)
values (3, 'Actas', 2);
insert into carpeta (ID_CARPETA, NOMBRE, ID_CARPETA_PADRE)
values (4, '200100325', 3);
insert into carpeta (ID_CARPETA, NOMBRE, ID_CARPETA_PADRE)
values (5, '200100331', 3);
insert into carpeta (ID_CARPETA, NOMBRE, ID_CARPETA_PADRE)
values (6, 'Mañana', 5);
insert into carpeta (ID_CARPETA, NOMBRE, ID_CARPETA_PADRE)
values (7, 'Tarde', 5);
-- DOCUMENTOS
insert into documento (ID_DOCUMENTO, NOMBRE, ID_CARPETA)
values (1, 'acta.doc', 4);
insert into documento (ID_DOCUMENTO, NOMBRE, ID_CARPETA)
values (2, 'puntos_del_dia.doc', 4);
insert into documento (ID_DOCUMENTO, NOMBRE, ID_CARPETA)
values (3, 'presentacion.ppt', 4);
insert into documento (ID_DOCUMENTO, NOMBRE, ID_CARPETA)
values (4, 'acta_mañana.doc', 6);
insert into documento (ID_DOCUMENTO, NOMBRE, ID_CARPETA)
values (5, 'acta_tarde.doc', 7);
Evolución, innovación y transformación
37 especializaciones avaladas por Oracle
Oportunidades ilimitadas
El equipo marca la diferencia
Posts 100% Oracle
Sigue nuestro día a día