0% encontró este documento útil (0 votos)
11 vistas203 páginas

Todos Los Apuntes PDF

El documento es un conjunto de apuntes sobre la ampliación de bases de datos para estudiantes de 3º grado en Ingeniería Informática. Incluye temas como diseño avanzado de bases de datos relacionales, normalización, modelos alternativos como XML y MongoDB, y el lenguaje Datalog. Está estructurado en secciones que abordan tanto conceptos teóricos como prácticos relacionados con la gestión de bases de datos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
11 vistas203 páginas

Todos Los Apuntes PDF

El documento es un conjunto de apuntes sobre la ampliación de bases de datos para estudiantes de 3º grado en Ingeniería Informática. Incluye temas como diseño avanzado de bases de datos relacionales, normalización, modelos alternativos como XML y MongoDB, y el lenguaje Datalog. Está estructurado en secciones que abordan tanto conceptos teóricos como prácticos relacionados con la gestión de bases de datos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 203

Todos-los-apuntes.

pdf

Dashito

Ampliación de Bases de Datos

3º Grado en Ingeniería Informática

Facultad de Informática
Universidad Complutense de Madrid

Reservados todos los derechos.


No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Ampliación de Bases de Datos
Grado en Ingenierı́a Informática
Grupos B y C. Curso 2.018-2019

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
2

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Índice general

I Diseño avanzado de bases de datos relacionales 7

1. Diseño avanzado de bases de datos relacionales con XAMPP 9


1.1. Introducción a MySQL-XAMPP . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.1.1. XAMPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2. Control de acceso en SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.3. Introducción a PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1. Introducción a PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.2. Formularios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.4. Bases de datos con PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.4.2. Conexión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
1.4.3. Selección de una base de datos . . . . . . . . . . . . . . . . . . . . . . 45
1.4.4. Consultas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
1.4.5. Actualizaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
1.4.6. Consultas preparadas . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
1.4.7. Actualizaciones preparadas . . . . . . . . . . . . . . . . . . . . . . . . 53
1.5. Sesiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

2. Normalización 59
2.1. ¿Qué es la normalización? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.2. Redundancias y anomalı́as . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.3. Conceptos necesarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.3.1. Notación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.3.2. Dependencias funcionales . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.4. Formas normales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
2.4.1. Primera forma normal . . . . . . . . . . . . . . . . . . . . . . . . . . 63
2.4.2. Segunda forma normal . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.4.3. Tercera forma normal . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
2.4.4. Forma normal de Boyce-Codd (FNBC) . . . . . . . . . . . . . . . . . 67

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Ampliación de Bases de Datos
Banco de apuntes de la
II Modelos alternativos de bases de datos 75
3. XML 77
3.1. Modelo semiestructurado de bases de datos . . . . . . . . . . . . . . . . . . . 77
3.1.1. Lenguaje XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.1.2. Bases de datos con XML . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.2. Lenguaje XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.3. Documentos bien formados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.3.1. Document Type Definition . . . . . . . . . . . . . . . . . . . . . . . . 82
3.4. Lenguajes para bases de datos de XML . . . . . . . . . . . . . . . . . . . . . 88
3.4.1. XPath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.4.2. XQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.4.3. SGBD eXist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
3.4.4. xQuery tester . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

4. MongoDB 99
4.1. Conceptos básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.1.1. ¿Qué es MongoDB? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.1.2. Documentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.1.3. Tipos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
4.1.4. Colecciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
4.2. Inserción y borrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.3. Consultas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
4.3.1. Consultas sobre documentos embebidos . . . . . . . . . . . . . . . . . 115
4.3.2. Lı́mites, saltos y ordenaciones . . . . . . . . . . . . . . . . . . . . . . 117
4.4. Actualizaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.5. Indexación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
4.6. MapReduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
4.7. MongoDB y PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

5. Datalog 137
5.1. Bases de datos deductivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5.2. Datalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5.3. El lenguaje Datalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.3.1. Hechos y objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
5.3.2. Ajustes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
5.3.3. Reglas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
5.3.4. Reglas automáticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.3.5. Mecanismo de cómputo de Datalog . . . . . . . . . . . . . . . . . . . 145
5.3.6. Programando con Datalog . . . . . . . . . . . . . . . . . . . . . . . . 145
5.3.7. Reglas recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
5.4. Datalog avanzado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
5.4.1. Reuniones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
5.4.2. Agrupaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
5.4.3. Funciones de agregación . . . . . . . . . . . . . . . . . . . . . . . . . 152
5.4.4. Aritmética . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
5.4.5. Negación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
5.5. Más allá de Datalog: Prolog . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
5.5.1. ¿Qué es la programación declarativa? . . . . . . . . . . . . . . . . . . 156
5.5.2. Prolog: listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

III Funcionamiento interno de un SGBD 161


6. Almacenamiento de datos e ı́ndices 163
6.1. Almacenamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
6.1.1. RAID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
6.2. Índices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
6.2.1. Árboles B + . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
6.2.2. Tablas Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171

7. Transacciones y control de la concurrencia 179


7.1. Transacciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
7.1.1. Modelo simple de transacciones . . . . . . . . . . . . . . . . . . . . . 179
7.2. Control de la concurrencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
7.2.1. Protocolos basados en bloqueos . . . . . . . . . . . . . . . . . . . . . 183

8. Procesamiento de consultas y optimización 195


8.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
8.2. Pasos del procesamiento de una consulta . . . . . . . . . . . . . . . . . . . . 196
8.2.1. Análisis léxico, sintáctico y validación . . . . . . . . . . . . . . . . . . 196
8.2.2. Optimización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
8.2.3. Algunas heurı́sticas para la optimización de consultas . . . . . . . . . 198
8.2.4. Reglas de transformación de expresiones . . . . . . . . . . . . . . . . 199

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
6

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Parte I

Diseño avanzado de bases de datos


relacionales

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 1

Diseño avanzado de bases de datos


relacionales con XAMPP

1.1. Introducción a MySQL-XAMPP


MySQL http://www.mysql.com/ es un sistema gestor de bases de datos relacional.
Es el SGBD de código abierto más conocido a nivel mundial.
El acceso a las bases de datos de MySQL se realiza a través de una aplicación cliente:
• Consola de MySQL (instalada con el propio SGBD).
• MySQL Workbench.
• phpMyAdmin (XAMPP).
• Otros.
Trabajando con la consola de MySQL:
• Se ejecuta con:
mysql −u <n o m b r e u s u a r i o > −p

• Mostrar las bases de datos disponibles:


mysql> SHOW DATABASES;

• Cambiar la base de datos actual:


mysql> USE <nombre db >;

Ejecutar una sentencia SQL sobre la base de datos actual:


mysql> SELECT DNI , Nombre FROM E s t u d i a n t e s ;
mysql> INSERT INTO E s t u d i a n t e s VALUES ( . . . ) ;
mysql> CREATE TABLE A s i g n a t u r a s (
...
);

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.1: Ventana principal de XAMPP

Ejecutar un script con sentencias SQL:


mysql> s o u r c e n o m b r e a r c h i v o . s q l ;

La administración de una base de datos mediante sentencias SQL es demasiado tediosa.

Existen clientes que permiten crear los esquemas relacionales de una base de datos a
través de una interfaz gráfica.

phpMyAdmin es una herramienta para administrar bases de datos MySQL a través de


un navegador web.

phpMyAdmin necesita Apache + PHP para funcionar.

XAMPP es una distribución que incluye Apache, MariaDB, PHP y la herramienta


phpMyAdmin.

Es gratuito y se puede encontrar en http://www.apachefriends.org/en/xampp.html.

1.1.1. XAMPP
Se especifica el nombre de la base de datos.

10

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.2: Creando una base de datos con XAMPP

El ”cotejamiento” es opcional. Contiene el conjunto de reglas de comparación y orde-


nación del texto en la base de datos (depende de cada idioma).

1.2. Control de acceso en SQL


El lenguaje SQL se compone de:

• DDL (Data definition language): CREATE, DROP, ALTER ...


• DML (Data manipulation language): SELECT, INSERT, UPDATE, DELETE, CALL...
• DCL (Data control language): GRANT, REVOKE
• TCL (Transaction control language): COMMIT, ROLLBACK, SAVEPOINT...

Los SGBD permiten definir permisos sobre el tipo de operaciones que pueden realizar
los usuarios.

Si un usuario intenta realizar una operación para la que no tiene permiso, el SGBD la
rechazará.

El estándar de SQL define los siguientes permisos:

11

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.3: Contenido de la base de datos libreria

• Leer datos (SELECT)


• Insertar datos (INSERT)
• Actualizar datos (UPDATE)
• Borrar datos (DELETE)

Existe un usuario especial (administrador) que tiene acceso total a las bases de datos
almacenadas y puede asignar permisos a los demás usuarios.

En MySQL/XAMPP, recibe el nombre de root y no tiene contraseña asignada por


defecto.

Crear y eliminar usuarios:

• Según el estándar de SQL, al conceder un permiso a un usuario que no existe éste


se crea implı́citamente.
• En MySQL se permite (y se recomienda) crear un usuario explı́citamente mediante
CREATE USER.
CREATE USER ’ NombreUsuario ’ IDENTIFIED BY ’ Contrase ña ’

• Para eliminar un usuario se utiliza la cláusula DROP USER.

12

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.4: Definiendo la estructura de una tabla categoria

DROP USER ’ NombreUsuario ’

Gestión de permisos:

• Concesión de permisos. En el estándar SQL se utiliza la cláusula GRANT.


GRANT P r i v i l e g i o 1 , P r i v i l e g i o 2 , ...
ON NombreTabla
TO Usuario1 , Usu ario2 , . . .

donde cada privilegio puede ser:


◦ SELECT
◦ INSERT
◦ UPDATE
◦ ALL PRIVILEGES
◦ Y muchos más.
• Conceder permisos:
◦ Ejemplo:
CREATE USER ’ Manuel ’ IDENTIFIED BY ’ 1 2 3 4 ’
GRANT SELECT ON C o n t a c t o s TO Manuel

13

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.5: Definiendo relaciones entre tablas categoria

◦ Podemos indicar * en lugar de NombreTabla para conceder el permiso en


todas las tablas.
◦ En el caso del privilegio UPDATE se permite especificar qué columnas pueden
ser actualizadas.
GRANT UPDATE( Nombre , A p e l l i d o s ) ON C o n t a c t o s TO Manuel

◦ Existe un usuario especial llamado public. Los permisos concedidos a dicho


usuario se concederán automáticamente a los usuarios nuevos que se añadan
a partir de ese momento.
• Revocar permisos:
◦ Mediante la cláusula REVOKE
REVOKE P r i v i l e g i o 1 , P r i v i l e g i o 2 , ...
ON NombreRelacion
FROM Usuari o1 , Usuario2 , . . .

◦ Ejemplo:
REVOKE ALL PRIVILEGES ON ∗ FROM Manuel

• Transferencia de privilegios:
◦ La cláusula GRANT permite conceder al usuario afectado la capacidad de poder
conceder el privilegio correspondiente a otros usuarios de la base de datos.

14

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.6: Definiendo relaciones entre tablas categoria

◦ Para ello se añade WITH GRANT OPTION al final de la cláusula GRANT.


◦ Ejemplo:

GRANT SELECT ON C o n t a c t o s TO Manuel WITH GRANT OPTION

◦ El usuario Manuel podrá permitir conceder acceso a la tabla Contactos a


otros usuarios.
◦ Supongamos que el usuario A concede un privilegio a B con posibilidad de
transferencia, y B concede ese mismo privilegio a C.
◦ Si A revoca el privilegio concedido a B, el usuario C también perderá ese mis-
mo privilegio salvo que exista un usuario D que también se lo haya concedido.
◦ La concesión de un determinado privilegio a los usuarios se puede representar
mediante un grafo dirigido, donde el administrador (ABD) es la raı́z (ver
figura 1.10).
◦ Un usuario tiene el privilegio correspondiente si existe un camino en el grafo
desde el administrador hasta dicho usuario.

15

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.7: Contenido de la tabla categoria

1.3. Introducción a PHP


1.3.1. Introducción a PHP
¿Qué es PHP?
PHP es un lenguaje de scripts que se ejecuta en el lado del servidor.

Su código está incluido en una página HTML clásica.

Otros lenguajes de guiones para el servidor: ASP, JSP.

El resultado de la ejecución del código se integra en la página HTML que es enviada


al explorador.

Este procesamiento del código en el servidor no es visible para el cliente.

Esta tecnologı́a permite realizar páginas web dinámicas. Su contenido puede ser com-
pletado en el momento de la llamada a la página gracias a cierta información. Por
ejemplo: la información extraı́da de un formulario o de una base de datos.

Con el siguiente programa en el servidor:

16

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.8: Modo consola de XAMPP categoria

<!DOCTYPE>
<html>
<head>
< t i t l e >Mi p r i m e r a p á g i n a PHP</ t i t l e >
</head>
<body>
<p>
<?php echo ” B i e n v e n i d o a l mundo de PHP! ” ; ?>
</p>
</body>
</html>

se construye la siguiente página web:


<!DOCTYPE>
<html>
<head>
< t i t l e >Mi p r i m e r a p á g i n a PHP</ t i t l e >
</head>
<body>

17

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 1.9: Gestión de usuarios categoria

Figura 1.10: Transferencia de privilegios

18

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<p>
B i e n v e n i d o a l mundo de PHP!
</p>
</body>
</html>

Delimitadores PHP
Delimitadores de los scripts de PHP:
Opción 1:
< s c r i p t l a n g u a g e=”PHP”>
...
</ s c r i p t >

Opción 2:
<?php . . . ?>

También es posible configurar el servidor para admitir otros formatos:


<\ %...\ %>

Bloques PHP
Dentro de un bloque PHP (entre dos delimitadores) puede haber:
Una sola instrucción:
<?php echo ” Hola mundo ! ” ?>

Una secuencia de instrucciones separadas por puntos y comas:


<?php
$suma = 4 ;
$suma += 3 ;
echo $suma
?>

19

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Comentarios PHP
Escritura en la página: echo y print.
<?php
// De una s o l a lı́ n e a
# Otro de una s o l a lı́ n e a
/∗ Y e s t e e s un c o m e n t a r i o de m ú l t i p l e s lı́ n e a s ∗/
?>

Nombres de variables: $nombre (sensible a mayúsculas y minúsculas)

Los tipos en PHP


Lenguaje débilmente tipado:

• No se declaran las variables (porque no hay que especificar el tipo).


• Se crean al usarse por primera vez.
• El tipo se infiere del contexto:
<?php
$cad = ” Hola ” ;
$bool = true ;
$num = 1 3 ;
?>

Conversión automática de tipos en expresiones.

Tipos básicos:

• boolean
• integer
• float
• string

Tipos compuestos:

• array
• Object

Tipo especial: NULL (constante NULL).

20

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Cadenas de caracteres (string)
Pueden ser de cualquier longitud.

Cadenas literales:encerradas entre comillas simples (’...’) o dobles (”...”).

Para utilizar comillas simples o dobles en una expresión como parte del texto es nece-
sario especificarlas como \’ o \”:
<?php
echo ”Don \ ’ t l e t me down” ;
?>

Las cadenas de caracteres de la cadena son accesibles con llaves:


<?php
$cad = ” Hola ” ;
echo $cad { 1 } ; // Muestra l a l e t r a ’ o ’ ;

?>

La primera posición es la 0.

Cadenas de caracteres entre comillas dobles


Cuando se encuentra un $:

• Se interpreta lo que sigue como una variable.


• Se sustituye la variable por su valor.

Se puede encerrar la variable o su nombre entre llaves:


<?php
$nombre = ” Pablo ” ;
echo ”Te l l a m a s $nombre ! ” ; // Te l l a m a s Pablo !
echo ”Te l l a m a s { $nombre } ! ” ;
echo ”Te l l a m a s ${nombre } ! ” ;
?>

21

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Operadores para cadenas
Concatenación: operador . (punto):
<?php
$nombre = ” Pablo ” ;
$ a p e l l i d o = ” Fernández ” ;
echo $nombre . ” ” . $ a p e l l i d o ; // Pablo Fernández
?>

Concatenación y asignación en un paso: operador .=:


<?php
$nombre = ” Pablo ” ;
$nombre .= ” Fernández ” ;
echo $nombre ; // Pablo Fernández
?>

Arrays
Los arrays en PHP son mapas con orden, es decir, son pares de parejas clave-valor.

Claves: pueden ser integer o string. Pueden estar ambos tipos en un mismo array:
<?php
$num = 1 2 ;
$ m a t r i z = a r r a y ( ” f o o ” => ” bar ” , 12 => t r u e ) ;
echo $ m a t r i z [ ” f o o ” ] ; // bar
echo $ m a t r i z [ 1 2 ] ; // 1
}
?>

Si no se indica clave, se toma el máximo entero usado +1:


<?php
// Este a r r a y . . . }
a r r a y ( 5 => 4 3 , 3 2 , 5 6 , ”b” => 1 2 ) ;
// . . . e s i g u a l que e s t e o t r o
a r r a y ( 5 => 4 3 , 6 => 3 2 , 7 => 5 6 , ”b” => 1 2 ) ;
?>

22

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Para PHP las matrices son arrays de arrays.
Ejemplo:
<?php
$ c i u d a d e s = a r r a y ( ’ESPAÑA ’=> a r r a y ( ’ Madrid ’ , ’ B a r c e l o n a ’ , ’
Zaragoza ’ ) ,
’FRANCIA ’=> a r r a y ( ’ P a rı́ s ’ , ’ Nantes ’ ) ) ;
echo $ c i u d a d e s [ ’ESPAÑA ’ ] [ 0 ] ;
echo $ c i u d a d e s [ ’FRANCIA ’ ] [ 1 ] ;
?>

Modificación / creación de valores (y eliminación):


<?php
$ m a t r i z = a r r a y ( 5 => 1 , 12 => 2 ) ;
$ m a t r i z [ ] = 5 6 ; // I g u a l que $ m a t r i z [ 1 3 ] = 5 6 ;
$ m a t r i z [ ”x” ] = 4 2 ; // Nuevo e l e m e n t o con c l a v e ”x”
u n s e t ( $ m a t r i z [ 5 ] ) ; // E l im i na e s e e l e m e n t o
unset ( $matriz ) ; // E li m in a l a m a t r i z completa
?>

Los ı́ndices (claves enteras) no se reutilizan.


<?php
$ m a t r i z = a r r a y ( 1 , 2 , 3 ) ; // Í n d i c e s 0 , 1 , 2
u n s e t ( $ m a t r i z [ 2 ] ) // Eliminamos e l v a l o r 3 ( ı́ n d i c e 2 )
$ m a t r i z [ ] = 4 ; // $ m a t r i z [ 3 ] = 4 , aunque e l 2 e s t é l i b r e
$ m a t r i z = a r r a y v a l u e s ( $ m a t r i z ) ; // Reindexa e l a r r a y
?>

Constantes
Las constantes son sensibles a mayúsculas y minúsculas.
Definición:
<?php
d e f i n e ( ”CONSTANTE” , ” Hola amigo ! ” ) ;
echo CONSTANTE; // muestra ” Hola amigo ! ”
?>

23

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
El alcance de una costante definida es el script donde se encuentra.
Existen muchas constantes predefinidas.

Operadores
Son similares a los de C++:
+ - * / % ++ -- .(para las cadenas)
= += -= *= /= .=
== != < <= >=
! && ||
=== (igualdad y tipos idénticos)
!== (desigualdad o tipos distintos)

Instrucciones de control
También son similares a las de C++
if(condición){...} else {...} o if(condicion){...} elseif(condicion) {...}
switch(expresión){
case valor1: ...; break;
case valor2: ...; break;
...
default: ...
}
Nota: existe una sintaxis ligeramente distinta si queremos intercalar código HTML: <?php
if (condición)?>
código HTML
<?php endif; ?>
while(condición){
...
}
do
...
while(condición)
for(inicialización; condición; incremento) {
...
}
Al igual que en el caso anterior, la sintaxis de los bucles puede variar ligeramente si se
intercala código HTML.

24

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Procesamiento de colecciones
Procesamiento de los elementos de un array:

foreach(array as $valor)...

foreach(array as $clave => $valor)...

Se itera para cada elemento del array, asignando el valor a $valor (y la clave a $key en
el segundo caso).

<?php
$ m a t r i z = a r r a y ( ” a ” , ”b” , ” c ” , ”d” , ” e ” ) ;
fo reac h ( $matriz as $val ) {
echo ” $ v a l ” ;
};
f o r e a c h ( $ m a t r i z a s $key => $ v a l ) {
echo ” $key : $ v a l ” ;
}
?>

Funciones
Una función se define a partir de nombre y de sus sus parámetros:

Sintaxis:
function nombre($par1 [=valor1], $par2 [=valor2], ... , $parN [=valorN])
{ ...}
...
}

Invocación: nombre($arg1, $arg2, ... , $argN);

Se pueden omitir argumentos por el final, usándose entonces los valores por defecto.

Algunos ejemplos:
<?php
// Algunas d e c l a r a c i o n e s :
function hola () {
echo ” ¡ H o l a ! ” ;
}
f u n c t i o n producto ( $valor1 , $valor2 ) {
return $valor1 ∗ $valor2
}

25

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
// Algunas l l a m a d a s :
hola () ;
p r o d u ct o ( 3 , 7 ) ;
?>

Por defecto los parámetros se pasan por valor.


Paso de parámetro por referencia: function nombre(&$parámetro) { ... }
Devolución de valores: instrucción return.
Ejemplo:
<?php
f u n c t i o n f o o (&$a , $b=” Hola ” ) {}
...
return $res
...
$ r = f o o ( $una , $dos ) ;
$ r = f o o ( $una ) ;
?>

Ámbito de las variables


El ámbito de una variable es el script donde se encuentra. Su duración queda restringida
al tiempo de duración del script. Si se vuelve a ejecutar el script se vuelve a crear.
Una variable definidad en una función posee un ámbito local (el de la función).
Una variable definida fuera de una función no es visible desde ella.
Para saltarse estas limitaciones es posible (aunque no recomendable) usar global o
$GLOBALS.
<?php
$num = 1 2 ;
function foo () {
g l o b a l $num ; // Acceso a l a v a r i a b l e g l o b a l
echo $num ;
echo $GLOBALS [ ’num ’ ] ;
}
?>

26

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
1.3.2. Formularios
Introducción
En HTML existen dos métodos principales para interactuar con el usuario:

• Los vı́nculos.
• Los formularios.

Los vı́nculos simplemente llaman a una página web: interacción sencilla.

Los formularios son capaces de enviar una gran cantidad de información.

Los vı́nculos
Los vı́nculos pueden enviar información a la página que se llama.

Sintaxis: Nombre-url?variable-1=valor-1&...&variable-n=valor-n.

El carácter ? introduce la lista de parámetros separados por el carácter &. Cada


parámetro está constituido por una pareja variable=valor.

Ejemplo:
pagina1.php:
<?php $nombre=Pepe ’ ; ?>
...
<body>
...
<a h r e f =”p a g i n a 2 . php? nombre=<?php echo $nombre ; ?”>Pagina 2 >/a>
...
</body>

La visualización de pagina1.php en el explorador queda de la siguiente forma:


...
<body>
...
<a h r e f=” p a g i n a 2 . php? nombre=Pepe>Pagina 2 </a>
...
</body>

Si pagina2.php es:

27

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
...
<body>
...
<?php echo ” Página 2 − Hola $nombre ; ?>
...
</body>

¿Que mostrará pagina2.php?

”Página 2 - Hola”

¿Por qué?. $nombre no está definida en pagina2.php. Necesitamos variables globales.

Generación dinámica de formularios


Generación dinámica de formularios Como otros elementos de una página HTML,
PHP permite generar dinámicamente los formularios. Algunos casos:

Generar todo el formulario.

Generar valores iniciales en entradas de texto.

Generar una lista de opciones.

Inicialización de un campo de texto En este ejemplo se inicializa una entrada con


cierto nombre:
<form a c t i o n=” e n t r a d a . php” method=”POST”>
Nombre:< i n p u t type=” t e x t name=”nombre” v a l u e=”<?php echo $nombre ?>”>
<i n p u t type=” submit ” name=” a c e p t a r ” v a l u e=” Aceptar ”>
</form>

Se supone que en algún sitio está inicializado $nombre.

Generación de una lista de selección única Ejemplo:


<!DOCTYPE html >
<head><t i t l e >Generar una l i s t a de o p c i o n e s de s e l e c c i ó n única </ t i t l e ></
head>
<body>
<div>
<?php
// L i s t a de l o s i d i o m a s para m os t ra r en l a l i s t a ,
// con l a forma de una m a t r i z a s o c i a t i v a que da e l c ó d i g o

28

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
// d e l idioma ( c l a v e de l a m a t r i z ) y e l nombre d e l idioma .
$idiomas disponibles = array (
’E ’ => ’ Espa ñol ’ ,
’F ’ => ’ F r a n cé s ’ ,
’ I ’ => ’ I t a l i a n o ’ ) ;
// Código d e l idioma d e l u s u a r i o .
$idioma = ’E ’ ;
?>

Generación de una lista de selección única Ejemplo:


<!−− c r e a c i ó n d e l f o r m u l a r i o −−>
<form a c t i o n=” e n t r a d a . php” method=”POST”>
Idioma :< br />
< s e l e c t name=” idioma ”>
<?php
// Código PHP que g e n e r a l a p a r t e dinámica d e l f o r m u l a r i o .
// R e c o r r e r l a l i s t a para m os t ra r y r e c u p e r a r e l c ó d i g o
// y e l nombre .
f o r e a c h ( $ i d i o m a s d i s p o n i b l e s a s $ c ó d i g o => $nombre ) {
// Determinar s i l a lı́ n e a debe e s t a r s e l e c c i o n a d a
// − sı́ , s i e l c ó d i g o e s i g u a l a l c ó d i g o d e l idioma
// del usuario
// − s i e s e l caso , poner e l a t r i b u t o ” s e l e c t e d ” en
// l a e t i q u e t a ” o p t i o n ” ; s i no , no poner nada
$ s e l e c c i ó n = ( $ c ó d i g o == $idioma ) ? ’ s e l e c t e d ’ : ’ ’ ;
// Generar l a e t i q u e t a ” o p t i o n ” con l a v a r i a b l e $ c ó d i g o
// l a o p c i ó n ” v a l u e ” , l a v a r i a b l e $ s e l e c c i ó n
// para l a i n d i c a c i ó n de s e l e c c i ó n y l a v a r i a b l e $nombre
// para e l t e x t o mostrado en l a l i s t a .
echo ”<o p t i o n v a l u e =\” $ c ó d i g o \” $ s e l e c c i ó n >$nombre</o p t i o n >” ;
}
?>
</ s e l e c t >
</form>
</div>
</body>
</html>

Generación de una lista de selección múltiple Ejemplo:

29

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<!DOCTYPE html>
<head><t i t l e >Generar una l i s t a de o p c i o n e s de s e l e c c i ó n m ú l t i p l e </ t i t l e
></head>
<body>
<div>
<?php
// F r u t a s para m o st ra r en l a l i s t a , con l a forma
// de una m a t r i z a s o c i a t i v a que da e l c ó d i g o
// de l a f r u t a ( c l a v e de l a m a t r i z ) y e l nombre de l a f r u t a .
$frutas del mercado = array (
’A ’ => ’ A l b a r i c o q u e s ’ ,
’C ’ => ’ C e r e z a s ’ ,
’F ’ => ’ F r e s a s ’ ,
’P ’ => ’ M e l o c o t o n e s ’ ,
’ ? ’ => ’No s a b e ’ ) ;
// F r u t a s p r e f e r i d a s d e l u s u a r i o , con l a forma
// de una m a t r i z que da e l c ó d i g o de l a s f r u t a s c o r r e s p o n d i e n t e s .
$ f r u t a s p r e f e r i d a s = a r r a y ( ’A ’ , ’F ’ ) ;
// A d v e r t e n c i a : veremos más a d e l a n t e cómo r e c u p e r a r
// e s t a i n f o r m a c i ó n en una b a s e de d a t o s .
?>

Generación de una lista de selección múltiple Ejemplo:


<!−− c r e a c i ó n d e l f o r m u l a r i o −−>
<form a c t i o n=” e n t r a d a . php” method=”POST”>
F r u t a s p r e f e r i d a s :< br />
< s e l e c t name=” f r u t a s [ ] ” m u l t i p l e s i z e=” 8”>
<?php
// Código PHP que g e n e r a l a p a r t e dinámica d e l f o r m u l a r i o .
// R e c o r r e r l a l i s t a para m os t r ar y r e c u p e r a r e l c ó d i g o
// y e l nombre .
f o r e a c h ( $ f r u t a s d e l m e r c a d o a s $ c ó d i g o => $nombre ) {
// Determinar s i l a lı́ n e a debe e s t a r s e l e c c i o n a d a
// − sı́ , s i e l c ó d i g o f i g u r a en l a l i s t a de l a s f r u t a s
// p r e f e r i d a s d e l u s u a r i o => b úsqueda de $ c ó d i g o
// en $ f r u t a s p r e f e r i d a s con l a f u n c i ó n i n a r r a y
// − s i e s e l caso , poner e l a t r i b u t o ” s e l e c t e d ” en
// l a e t i q u e t a ” o p t i o n ” ; s i no , no poner nada .
$ s e l e c c i ó n =
i n a r r a y ( $ c ó d i g o , $ f r u t a s p r e f e r i d a s ) ? ’ s e l e c t e d ’ : ’ ’ ;
// Generar l a e t i q u e t a ” o p t i o n ” con l a v a r i a b l e $ c ó d i g o
// para e l a t r i b u t o ” v a l u e ” , l a v a r i a b l e $ s e l e c c i ó n

30

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
// para l a i n d i c a c i ó n de s e l e c c i ó n y l a v a r i a b l e $nombre
// para e l t e x t o mostrado en l a l i s t a .
echo ”<o p t i o n v a l u e =\” $ c ó d i g o \” $ s e l e c c i ó n >$nombre</o p t i o n >” ;
}
?>
</ s e l e c t >
</form>
</body>
</html>

Procesamiento de formularios
Existen tres formas para procesar la información de un formulario con scripts de PHP:

El formulario en HTML puro (sin ningún elemento dinámico) se relaciona con el script
de PHP a través del atributo action.

Colocar el formulario en un script de PHP (para construir alguna parte de manera


dinámica) y que otro script PHP lo procese.

Colocar el formulario en un script de PHP que lo construya dinámicamente y lo procese


(indicando su propio nombre en la opción action).

Ninguna de estas formas es mejor que las demás. La elección de una u otra depende
de cada caso.

Recuperación de los datos de formularios


Recuperación de los datos de formularios

Cuestión: ¿cómo podemos tener acceso a los datos enviados cuando submitimos la
información de un formulario.

Toda la información del formulario se almacena automáticamente en unas variables


superglobales: $ POST y $ GET (según sea el valor del atributo method: POST o GET).

También están disponibles en la variable superglobal $ REQUEST que agrupa a las an-
teriores.

Con la siguiente página web creamos un formulario y le enviamos la información a un script


de PHP:
<!DOCTYPE html>
<head><t i t l e >Entrada de datos </ t i t l e ></head>
<body>

31

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<form a c t i o n=” p r o c e s o . php” method=” p o s t ”>
<div>
Nombre : <i n p u t type=” t e x t ” name=”nombre” v a l u e=” ” />
<i n p u t type=” submit ” name=” a c e p t a r ” v a l u e=” Aceptar ” />
</div>
</form>
</body>
</html>

En este script de PHP accedemos a la información que un formlario le ha enviado:


<!DOCTYPE>
<head>
< t i t l e >Proceso </ t i t l e >
</head>
<body>
<div>
<?php
// V i s u a l i z a c i ó n de l o s d a t o s c o n t e n i d o s
// en l a s m a t r i c e s $ POST y $ REQUEST .
echo ’ $ POST [ \ ’ nombre \ ’ ] −> ’ , $ POST [ ’ nombre ’ ] , ’<br \> ’ ;
echo ’$ REQUEST [ \ ’ nombre \ ’ ] −> ’ ,$ REQUEST [ ’ nombre ’ ] , ’<br \> ’ ;
?>
</div>
</body>
</html>

En este script de PHP accedemos a la información que un formulario le ha enviado:


<!DOCTYPE html>
<head><t i t l e >Proceso </ t i t l e ></head>
<body>
<div>
<?php
// V i s u a l i z a c i ó n de l o s d a t o s c o n t e n i d o s
// en l a s m a t r i c e s $ POST y $ REQUEST .
echo ’ $ POST [ \ ’ nombre \ ’ ] −> ’ , $ POST [ ’ nombre ’ ] , ’<br \> ’ ;
echo ’$ REQUEST [ \ ’ nombre \ ’ ] −> ’ ,$ REQUEST [ ’ nombre ’ ] , ’<br \> ’ ;
?>
</div>
</body>
</html>

32

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Otra posibilidad para acceder a la información de un formulario consiste en importar
en variables PHP la información.

Función para importar: import request variables(cadena tipos, cadena prefijo)


donde:

• tipos: representa el tipo de información deseada (P o p para POST y G o g para


GET).
• prefijo: prefijo para el nombre de la variable.

El último script se puede escribir de la siguiente manera:


<!DOCTYPE html>
<head><t i t l e >Proceso </ t i t l e ></head>
<body>
<div>
<?php
// Importando d a t o s d e l f o r m u l a r i o
// método POST y p r e f i j o f o r m
i m p o r t r e q u e s t v a r i a b l e s ( ’P ’ , ’ f o r m ’ ) ;
echo ’ $form nombre = ’ , $form nombre , ’<br \> ’ ;
?>
</div>
</body>
</html>

Utilizar una matriz para acceder a los datos enviados


Es posible utilizar una notación de tipo matriz en el atributo name de las etiquetas:
<input>, <select> y <textarea>.

Un ejemplo:
<form a c t i o n=” p r o c e s a r . php” method=”POST”><div>
A p e l l i d o : <i n p u t type=” t e x t ” name=” e n t r a d a [ ] ”>
Nombre:< i n p u t type=” t e x t name=” e n t r a d a [ ] ”>
<i n p u t type=” submit ” name=” a c e p t a r ” v a l u e=” Aceptar ”>
</form>

La aparición de entrada[] como valor de name produce una única variable entrada
con formato de matriz (array). El primer elemento tiene la clave 0.

33

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Es posible utilizar una notación de tipo matriz indicando el nombre de clave de
entrada[].
Un ejemplo:
<form a c t i o n=” p r o c e s a r . php” method=”POST”><div>
A p e l l i d o : <i n p u t type=” t e x t ” name=” e n t r a d a [ a p e l l i d o ] ”>
Nombre:< i n p u t type=” t e x t name=” e n t r a d a [ nombre ] ”>
<i n p u t type=” submit ” name=” a c e p t a r ” v a l u e=” Aceptar ”>
</form>

Acceso a la información de cada tipo de zona


Acceso a la información de las zonas de texto
Consideremos que un formulario tiene las siguientes zonas de texto:
Apellido :
<i n p u t type=” t e x t ” name=” a p e l l i d o ” v a l u e=” ”
s i z e=” 20 ” maxlength=” 20 ” />
Contrase ña :
<i n p u t type=” password ” name=” c o n t r a s e ñ a ” v a l u e=””
s i z e=” 20 ” maxlength=” 20 ” />
<br />Comentario :< br />
<t e x t a r e a name=” c o m e n t a r i o ” rows=” 4” c o l s=” 50 ”></t e x t a r e a >
<br />

En las siguientes variables se encuentra la información: $ POST[apellido], $ POST[contrase~


na]
y $ POST[comentario].

Acceso a la información de los botones de opción


Consideremos que un formulario tiene el siguiente botón de opción:
Sexo :
<i n p u t type=” r a d i o ” name=” s e x o ” v a l u e=”H” />Hombre
<i n p u t type=” r a d i o ” name=” s e x o ” v a l u e=”M” />Mujer
<i n p u t type=” r a d i o ” name=” s e x o ” v a l u e=” ? ”
checked=” checked ” />No s a b e

Si se selecciona la opción Hombre en la variable $ POST[sexo] encontraremos el valor


”H”.

34

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Acceso a la información de las casillas de verificación

Consideremos que un formulario tiene el siguientes casillas de verificación:


Colores p r e f e r i d o s :
<i n p u t type=” checkbox ” name=” a z u l ” v a l u e=”b” />Azul
<i n p u t type=” checkbox ” name=” b l a n c o ” />Blanco
<i n p u t type=” checkbox ” name=” r o j o ” />Rojo
<i n p u t type=” checkbox ” name=” nosabe ”
checked=” checked ” />No s a b e

Si se selecciona la opciones azul y rojo tendremos: $ POST[azul]=b y $ POST[rojo]=on.

Acceso a la información de una lista de selección única

Consideremos que un formulario tiene la siguiente lista de selección única:


Idioma :
< s e l e c t name=” idioma ”>
<o p t i o n v a l u e=”E” s e l e c t e d=” s e l e c t e d ”>Espa ñol </o p t i o n >
<o p t i o n v a l u e=”F” >Francés </o p t i o n >
<o p t i o n v a l u e=” I ”>I t a l i a n o </o p t i o n >
</ s e l e c t >

Si no se selecciona nada tendremos: $ POST[idioma]=E.

Acceso a la información de una lista de selección múltiple

Consideremos que un formulario tiene la siguiente lista de selección múltiple:


<br />F r u t a s p r e f e r i d a s :< br />
< s e l e c t name=” f r u t a s [ ] ” m u l t i p l e=” m u l t i p l e ” s i z e=” 8”>
<o p t i o n v a l u e=”A”>A l b a r i c o q u e s </o p t i o n >
<o p t i o n v a l u e=”C”>Cerezas </o p t i o n >
<o p t i o n v a l u e=”F”>F r e s a s </o p t i o n >
<o p t i o n v a l u e=”P”>Melocotones </o p t i o n >
<o p t i o n v a l u e=” ? ” s e l e c t e d=” s e l e c t e d ”>
No sabe </o p t i o n >
</ s e l e c t >

Si se seleccionan fresas y albaricoques tendremos: $ POST[frutas]=[A, F].

35

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Validación de datos
Limpieza de los espacios innecesarios:

• Es posible quitar los espacios innecesarios (al principio y al final de la cadena) en


las zonas de entrada de texto libre con la función trim.
• Ejemplo: $nombre=trim($ POST[’nombre’]);

Dato obligatorio:

• Es muy sencillo comprobar si hay información en una entrada de texto.


• Ejemplo:
$nombre=t r i m ( $ POST [ ’ nombre ’ ] ) ;
i f ( $nombre==’ ’ ) {
// P r o c e s a r r e s p u e s t a
}

Longitud máxima de cadena:

Con la función strlen se puede controlar la longitud de las cadenas de caracteres


introducidas en las zonas de entrada de texto.

Ejemplo:
$nombre=t r i m ( $ POST [ ’ nombre ’ ] ) ;
i f ( s t r l e n ( $nombre ) >25){
// P r o c e s a r r e s p u e s t a
}

Cadenas de caracteres con formato:

Con las funciones eregi y ereg se puede controlar el formato de una cadena de carac-
teres.

ereg tiene en cuenta las mayúsculas mientras que eregi no.

Ejemplo:

• Una cadena de caracteres debe comenzar por una letra, seguida de letras (y ca-
racteres: ,#,*,$ ). Debe de tener una longitud mı́nima de 4:

36

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
$ c o n t r a s e ñ a=t r i m ( $ POST [ ’ c o n t r a s e ñ a ’ ] ) ;
i f ( ! e r e g i ( ’ ˆ [ a−z ] [ a−z0 −9 #∗$ ] { 3 , } ’ , $ c o n t r a s e ñ a ) ) {
// P r o c e s a r c o n t r a s e ñ a i n v á l i d a
}

• eregi no diferencia entre mayúsculas y minúsculas.


• ^: marca de comienzo.
• [a-z]: carácter entre a y z.
• [a-z0-9 #*$]{3,}: le siguen al menos tres caracteres del conjunto de los incluidos.

Validez de una fecha:

Tomemos, por ejemplo, el formato de fecha: DD/MM/AAAA. Por lo tanto, son inváli-
das: 01/01/99 y 32/01/1999.

Comprobación del formato:


$ f e c h a n a c i m i e n t o=t r i m ( $ POST [ ’ f e c h a n a c i m i e n t o ’ ] ) ;
$ f o r m a t o f e c h a= ’ ˆ [ 0 − 9 ] { 1 , 2 } / [ 0 − 9 ] { 1 , 2 } / [ 0 − 9 ] { 4 } $ ;
i f ( ! ereg ( $formato fecha , $fecha nacimiento ) ) {
// P r o c e s a r f e c h a i n v á l i d a
}

^: marca de comienzo.

[0-9]{1,2}: una o dos cifras.

/: seguido del cáracter ’’/’’.

[0-9]{4}:seguido de cuatro cifras.

$: fin de la información de la fecha. No puede haber nada más.

Validez de una fecha:

Existen tres métodos similares para saber si una fecha con formato correcto es válida.

Con la función explode:


$dma=e x p l o d e ( ’/ ’ , $fecha nacimiento ) ;
//$dma [ 0 ] : e l dı́ a
//$dma [ 1 ] : e l mes
//$dma [ 2 ] : e l año

37

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
i f ( ! c h e c k d a t e ( $dma [ 1 ] , $dma [ 0 ] , $dma [ 2 ] ) ) {
// P r o c e s a r f e c h a i n v á l i d a
}

Con la función explode y list:


$ l i s t ( $dia , $mes , $año )=e x p l o d e ( ’ / ’ , $ f e c h a n a c i m i e n t o ) ;
i f ( ! c h e c k d a t e ( $dia , $mes , $año ) ) {
// P r o c e s a r f e c h a i n v á l i d a
}

Validez de una fecha:

Con la función ereg:


$ f o r m a t o f e c h a= ’ ˆ [ 0 − 9 ] { 1 , 2 } / [ 0 − 9 ] { 1 , 2 } / [ 0 − 9 ] { 4 } $ ;
i f ( ! e r e g ( $ f o r m a t o f e c h a , $ f e c h a n a c i m i e n t o , $dma ) ) {
// P r o c e s a r f e c h a con formato i n c o r r e c t o
} else {
//dma [ 1 ] : e l dı́ a
//dma [ 2 ] : e l mes
//dma [ 3 ] : e l año
i f ( ! c h e c k d a t e ( $dma [ 2 ] , $dma [ 1 ] , $dma [ 3 ] ) ) {
// P r o c e s a r f e c h a i n v á l i d a
}

Juntándolo todo: un ejemplo


Un ejemplo Deseamos validar el siguiente formulario
<form method=” p o s t ” a c t i o n=” i n d e x 2 . php”>
<l a b e l > Nombre </ l a b e l >
<br />
<i n p u t type=” t e x t ” name=”nombre” v a l u e=”<?php echo $nombre ?>” />
<br />
<l a b e l > Edad </ l a b e l >
<br />
<i n p u t type=” t e x t ” name=” edad ” s i z e=”3” v a l u e=”<?php echo $edad ?>”
/>
<br />
<l a b e l > E−m a i l </ l a b e l >

38

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<br />
<i n p u t type=” t e x t ” name=” e m a i l ” v a l u e=”<?php echo $ e m a i l ?>” />
<br />
<i n p u t type=” submit ” v a l u e=” Enviar ” />
</form>

con las siguientes restricciones:


El nombre es un campo obligatorio.

La edad tiene que estar entre 3 y 130 años.

La dirección de correo electronica tiene que ser correcta.


Declaramos un página validaciones.php auxiliar:
<?php
function validaRequerido ( $valor ) {
i f ( t r i m ( $ v a l o r ) == ’ ’ ) {
return f a l s e ;
} else {
return true ;
}
}
f u n c t i o n v a l i d a r E n t e r o ( $ v a l o r , $ o p c i o n e s=n u l l ) {
i f ( f i l t e r v a r ( $ v a l o r , FILTER VALIDATE INT , $ o p c i o n e s ) === FALSE) {
return f a l s e ;
} else {
return true ;
}
}
function validaEmail ( $valor ) {
i f ( f i l t e r v a r ( $ v a l o r , FILTER VALIDATE EMAIL) === FALSE) {
return f a l s e ;
} else {
return true ;
}
}
?>

Comentarios:
filter var($valor, FILTER VALIDATE INT, $opciones) es un filtro que permite va-
lidar si una variable contiene un valor numérico. Opcionalmente (como en este caso)
se puede indicar unos valores máximos y mı́nimos permitidos.

39

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
La función validarEntero funcionarı́a también si no pasamos un intervalo de máximos
y mı́nimos ya que $opciones tiene un valor por defecto.
filter var($valor, FILTER VALIDATE EMAIL): es un filtro (predefinido) que permite
validar si una variable contiene una dirección de correo electrónico válida.
Declaramos un otra página validado.php auxiliar para indicar que todo ha ido bien:
<!DOCTYPE>
<html>
<head>
< t i t l e > F o r m u l a r i o </ t i t l e >
<meta http−e q u i v=” Content−Type” c o n t e n t=” t e x t / html ; c h a r s e t=u t f
−8” />
</head>
<body>
<s t r o n g > Sus d a t o s han s i d o e n v i a d o s c o r r e c t a m e n t e </s t r o n g >
</body>
</html>

La función isset determina si una variable está definida (tiene contenido) o no. Juntándolo
todo:
<?php
// Importamos e l a r c h i v o con l a s v a l i d a c i o n e s .
r e q u i r e o n c e ’ v a l i d a c i o n e s . php ’ ;
// Guarda l o s v a l o r e s de l o s campos en v a r i a b l e s , s i e m p r e y cuando s e
haya e n v i a d o e l f o r m u l a r i o , s i n o s e gu a r d ar á n u l l .
$nombre = i s s e t ( $ POST [ ’ nombre ’ ] ) ? $ POST [ ’ nombre ’ ] : n u l l ;
$edad = i s s e t ( $ POST [ ’ edad ’ ] ) ? $ POST [ ’ edad ’ ] : n u l l ;
$ e m a i l = i s s e t ( $ POST [ ’ e m a i l ’ ] ) ? $ POST [ ’ e m a i l ’ ] : n u l l ;
// Este a r r a y g u a r d a r á l o s e r r o r e s de v a l i d a c i ó n que s u r j a n .
$ e r r o r e s = array () ;
// Pregunta s i e s t á l l e g a n d o una p e t i c i ó n por POST, l o que s i g n i f i c a que
e l u s u a r i o e n v i ó e l f o r m u l a r i o .
i f ($ SERVER [ ’REQUEST METHOD ’ ] == ’POST ’ ) {
// V a l i d a que e l campo nombre no e s t é v a cı́ o .
i f ( ! v a l i d a R e q u e r i d o ( $nombre ) ) {
$ e r r o r e s [ ] = ’ El campo nombre e s i n c o r r e c t o . ’ ;
}
// V a l i d a l a edad con un rango de 3 a 130 a ños .
$opciones edad = array (
’ o p t i o n s ’ => a r r a y (
// D e f i n i m o s e l rango de edad e n t r e 3 a 1 3 0 .
’ min range ’ => 3 ,
’ max range ’ => 130

40

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
)
);
i f ( ! v a l i d a r E n t e r o ( $edad , $ o p c i o n e s e d a d ) ) {
$ e r r o r e s [ ] = ’ El campo edad e s i n c o r r e c t o . ’ ;
}

// V a l i d a que e l campo e m a i l s e a c o r r e c t o .
i f ( ! validaEmail ( $email ) ) {
$ e r r o r e s [ ] = ’ El campo e m a i l e s i n c o r r e c t o . ’ ;
}
// V e r i f i c a s i ha e n c o n t r a d o e r r o r e s y de no haber r e d i r i g e a l a
p á g i n a con
// e l mensaje de que pasó l a v a l i d a c i ó n .
i f (! $errores ){
h e a d e r ( ’ L o c a t i o n : v a l i d a d o . php ’ ) ;
exit ;
}
}
?>

<!DOCTYPE>
<html>
<head>
< t i t l e > F o r m u l a r i o </ t i t l e >
<meta http−e q u i v=” Content−Type” c o n t e n t=” t e x t / html ; c h a r s e t=u t f
−8” />
</head>
<body>
<?php i f ( $ e r r o r e s ) : ?>
<ul>
<?php f o r e a c h ( $ e r r o r e s a s $ e r r o r ) : ?>
< l i > <?php echo $ e r r o r ?> </ l i >
<?php e n d f o r e a c h ; ?>
</ul>
<?php e n d i f ; ?>
<form method=” p o s t ” a c t i o n=” i n d e x 2 . php”>
<l a b e l > Nombre </ l a b e l >
<br />
<i n p u t type=” t e x t ” name=”nombre” v a l u e=”<?php echo $nombre
?>” />
<br />
<l a b e l > Edad </ l a b e l >

41

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<br />
<i n p u t type=” t e x t ” name=” edad ” s i z e=”3 ” v a l u e=”<?php echo
$edad ?>” />
<br />
<l a b e l > E−m a i l </ l a b e l >
<br />
<i n p u t type=” t e x t ” name=” e m a i l ” v a l u e=”<?php echo $ e m a i l ?>
” />
<br />
<i n p u t type=” submit ” v a l u e=” Enviar ” />
</form>
</body>
</html>

Comentarios:

require once ’validaciones.php’; permite incluir las funciones de validación.

Con una lista no ordenada (generada dinámicamente) se muestran los errores conteni-
dos en el array $errores.

Con header(’Location: validado.php’); se muestra la página una página que in-


dica que no ha habido errores.

1.4. Bases de datos con PHP


1.4.1. Introducción
Cuestión: persistencia de la información.

La utilización de bases de datos es el método estándar para el almacenamiento de datos


en la web. Ejemplos:

• Catálogos de productos.
• Lista de clientes.
• Lista de transacciones realizadas, etc.

PHP permite una conexión a una gran número de sistemas de base de datos: MySQL,
ORACLE, Microsoft SQL Server, Informix, SQLite, etc.

PHP admite ODBC (Open DataBase Connectivity) y, por lo tanto, puede conectarse
a cualquier base de datos que soporte ODBC.

La versión 5 de PHP ofrece dos extensiones para trabajar con BB.DD.:

42

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• MySQL (prefijo mysql ).
• MySQLi (prefijo mysqli ).

Ambas extensiones son muy parecidas.

Operaciones con las bases de datos en MySQL:

Conexión/desconexión.

Selección de la base de datos.

Consultas.

Actualizaciones.

1.4.2. Conexión
mysqli connect: permite establecer una conexión con una base de datos MySQL. Paráme-
tros:

host: nombre o dirección IP al debe debe conectarse. Ejemplo: localhost.

usuario: nombre del usuario debe de utilizarse para establecer la conexión. Ejemplo:
root@localhost.

na: cadena vacı́a, significa sin contraseña.


contrase~

nombre base: nombre de la base de datos (parámetro opcional).

puerto: número de puerto para la conexión al servidor MySQL (parámetro opcional).

La función mysqli connect devuelve un objeto con un identificador mysqli o el valor FALSE
en caso de error. mysqli close: permite cerrar una conexión durante la ejecución de un
script. Parámetros:

conexión: identificador de conexión devuelto por mysqli connect.

La función mysqli close devuelve TRUE en caso de éxito y FALSE en caso de error. Ejemplo:
< t i t l e >Conexión y d e s c o n e x i ó n </ t i t l e >
<?php
// D e f i n i c i ó n de una peque ña f u n c i ó n que a b r e una c o n e x i ó n .
f u n c t i o n c o n e c t a r ( $host , $ u s u a r i o , $ c o n t r a s e n i a= ’ ’ ) {
$db = @ m y s q l i c o n n e c t ( $host , $ u s u a r i o , $ c o n t r a s e n i a ) ;
i f ( $db ) {
echo ’ Conexión r e a l i z a d a c o r r e c t a m e n t e .< br /> ’ ;
echo ’ I n f o r m a c i ó n s o b r e e l s e r v i d o r : ’ ,
m y s q l i g e t h o s t i n f o ( $db ) , ’<br /> ’ ;

43

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
echo ’ V e r s i ó n d e l s e r v i d o r : ’ ,
m y s q l i g e t s e r v e r i n f o ( $db ) , ’<br /> ’ ;
} else {
printf (
’ E r r o r %d : %s .< br /> ’ ,
mysqli connect errno () , mysqli connect error () ) ;
}
r e t u r n $db ;
}
// D e f i n i c i ó n de una peque ña f u n c i ó n que c i e r r a una c o n e x i ó n .
function desconectar ( $conexion ) {
i f ( $conexion ) {
$ok = @ m y s q l i c l o s e ( $ c o n e x i o n ) ;
i f ( $ok ) {
echo ’ Desco nex ión r e a l i z a d a c o r r e c t a m e n t e .< br /> ’ ;
} else {
echo ’ F a l l o en l a d e s c o n e x i ó n . <br /> ’ ;
}
} else {
echo ’ Conexión no a b i e r t a .< br /> ’ ;
}
}

// Primera prueba de c o n e x i ó n / d e s c o n e x i ó n .
echo ’<b>Primera prueba </b><br /> ’ ;
$db = c o n e c t a r ( ’ l o c a l h o s t ’ , ’ b l a s ’ , ’ web ’ ) ;
d e s c o n e c t a r ( $db ) ;
// Segunda prueba de c o n e x i ó n / d e s c o n e x i ó n .
echo ’<b>Segunda prueba </b><br /> ’ ;
$db = c o n e c t a r ( ’ xampp ’ , ’ d e s c o n o c i d o ’ , ’ d e s c o n o c i d o ’ ) ;
d e s c o n e c t a r ( $db ) ;
?>

Comentarios:

Utilizamos el carácter @ para no mostrar las alertas generadas por las funciones en caso
de error.

mysqli connect errno(): devuelve el número que corresponde a cada error (un cero
si no hay error).

mysqli connect error(): devuelve una cadena de caracteres con la descripción del
error (la cadena vacı́a si no hay error).

44

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
1.4.3. Selección de una base de datos
mysqli select db permite seleccionar (si no se ha hecho con mysqli connect) o mo-
dificar la base de datos seleccionada.

Parámetros:

• conexión: identificador devuelto por mysqli connect.


• nombre base: nombre de la base de datos.

La función mysqli select db devuelve TRUE en caso de éxito y FALSE en caso de error.

Con PHP también podemos crear una base de datos. Ejemplo:


<?php
$db = @ m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ r o o t ’ , ’ ’ ) ;
i f ( $db ) {
echo ’ Conexión r e a l i z a d a c o r r e c t a m e n t e .< br /> ’ ;
echo ’ I n f o r m a c i ó n s o b r e e l s e r v i d o r : ’ ,
m y s q l i g e t h o s t i n f o ( $db ) , ’<br /> ’ ;
echo ’ V e r s i ó n d e l s e r v i d o r : ’ ,
m y s q l i g e t s e r v e r i n f o ( $db ) , ’<br /> ’ ;
} else {
printf (
’ E r r o r %d : %s .< br /> ’ ,
mysqli connect errno () , mysqli connect error () ) ;
};
// C r e a t e d a t a b a s e
$ s q l = ”CREATE DATABASE myDB” ;
i f ( m y s q l i q u e r y ( $db , $ s q l ) ) {
echo ” Database c r e a t e d s u c c e s s f u l l y ” ;
} else {
echo ” E r r o r c r e a t i n g d a t a b a s e : ” . m y s q l i e r r o r ( $db ) ;
}
@ m y s q l i c l o s e ( $db ) ;
?>

Y crear tablas:
<?php
$db = @ m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ r o o t ’ , ’ ’ , ’mydb ’ ) ;
i f ( $db ) {
echo ’ Conexión r e a l i z a d a c o r r e c t a m e n t e .< br /> ’ ;
echo ’ I n f o r m a c i ó n s o b r e e l s e r v i d o r : ’ ,
m y s q l i g e t h o s t i n f o ( $db ) , ’<br /> ’ ;
echo ’ V e r s i ó n d e l s e r v i d o r : ’ ,

45

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
m y s q l i g e t s e r v e r i n f o ( $db ) , ’<br /> ’ ;
} else {
printf (
’ E r r o r %d : %s .< br /> ’ ,
mysqli connect errno () , mysqli connect error () ) ;
};
// s q l t o c r e a t e t a b l e
$ s q l = ”CREATE TABLE c a t e g o r i a s (
i d INT ( 6 ) UNSIGNED AUTO INCREMENT PRIMARY KEY,
c a t e g o r i a VARCHAR( 3 0 ) NOT NULL
)” ;
i f ( m y s q l i q u e r y ( $db , $ s q l ) ) {
echo ” Table c a t e g o r i a s c r e a t e d s u c c e s s f u l l y ” ;
} else {
echo ” E r r o r c r e a t i n g t a b l e : ” . m y s q l i e r r o r ( $db ) ;
}
@ m y s q l i c l o s e ( $db ) ;
?>

1.4.4. Consultas
mysqli query permite realizar una consulta en una base de datos. Parámetros:

conexión: identificador de conexión devuelto por mysqli connect.

consulta: cadena de caracteres que contiene la consulta que se va a ejecutar.

mysqli query devuelve un identificador (de tipo mysqli result) en caso de éxito y el valor
FALSE en caso contrario.
Con la función mysqli num rows podemos conocer el número de filas del resultado.

La ejecución de una consulta devuelve un resultado que puede ser leı́do por las siguien-
tes funciones:

• mysqli fetch array


• mysqli fetch assoc
• mysqli fetch object
• mysqli fetch row

Básicamente, estas funciones hacen lo mismo: leen una fila del resultado y avanzan el
puntero a la siguiente fila.

Si no hay ninguna fila que leer devuelven el valor NULL.

46

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Diferencia de formatos entre estas funciones:
mysqli fetch assoc: devuelve una matriz asociativa cuya clave es el nombre de la
columna.

mysqli fetch row: devuelve una matriz con enteros como ı́ndices.

mysqli fetch object: devuelve la fila actual con formato de objeto.

mysqli fetch array: depende de un parámetro opcional:

• MYSQLI NUM: matriz de ı́ndices enteros como mysqli fetch row.


• MYSQLI ASSOC: matriz asociativa como mysqli fetch assoc.
• MYSQLI BOTH: ambos a la vez. Es la opción predeterminada.

Sintaxis:
matriz mysqli fetch array(objeto resultado, tipo)
matriz mysqli fetch assoc(objeto resultado)
objeto mysqli fetch object(objeto resultado)
matriz mysqli fetch row(objeto resultado)
donde:
resultado: es el identificador devuelto por mysqli query.

tipo: algunas de las opciones de mysqli fetch array


Ejemplos de fetch utilizando las funciones anteriores:
<!DOCTYPE html>
<head><t i t l e >C o n su l ta no p r e p a r a d a : p ro b a r l a s d i f e r e n t e s t é c n i c a s de
f e t c h </ t i t l e >
</head>
<body>
<?php
// I n c l u s i ó n d e l a r c h i v o que c o n t i e n e l a d e f i n i c i ó n
// de l a f u n c i ó n ’ m o s t r a r m a t r i z ’ .
r e q u i r e ( ’ . . / i n c l u d e / f u n c i o n e s . i n c . php ’ ) ;
// Conexión ( con s e l e c c i ó n de l a b a s e de d a t o s ) .
$db = m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ eniweb ’ , ’ web ’ , ’ e n i ’ ) ;
i f ( ! $db ) {
e x i t ( ’ F a l l o en l a c o n e x i ó n . ’ ) ;
}
// E j e c u c i ó n de una c o n s u l t a
$ s q l = ’SELECT id , nombre , p r e c i o s i n i v a FROM c o l e c c i o n LIMIT 4 ’ ;
$ c o n s u l t a = m y s q l i q u e r y ( $db , $ s q l ) ;
// Primer f e t c h con m y s q l i f e t c h r o w .

47

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
$ f i l a = mysqli fetch row ( $consulta ) ;
mostrar matriz ( $ f i l a , ’ mysql fetch row ’ ) ;
// Segundo f e t c h con m y s q l i f e t c h a s s o c .
$ f i l a = mysqli fetch assoc ( $consulta ) ;
mostrar matriz ( $ f i l a , ’ mysql fetch assoc ’ ) ;

Ejemplos de fetch utilizando las funciones anteriores:


// T e r c e r f e t c h con m y s q l i f e t c h a r r a y :
// −> s i n segundo parámetro = MYSQLI BOTH
$ f i l a = mysqli fetch array ( $consulta ) ;
mostrar matriz ( $ f i l a , ’ mysql fetch array ’ ) ;
// Cuarto f e t c h con m y s q l i f e t c h o b j e c t .
$ f i l a = mysqli fetch object ( $consulta ) ;
echo ”<p /><b>m y s q l f e t c h o b j e c t </b><br />” ;
echo ”\ $ f i l a −>i d = $ f i l a −>id<br />” ;
echo ”\ $ f i l a −>nombre = $ f i l a −>nombre<br />” ;
echo ”\ $ f i l a −>p r e c i o s i n i v a = $ f i l a −>p r e c i o s i n i v a <br />” ;
// Quinto f e t c h de nuevo con m y s q l i f e t c h r o w :
// −> en p r i n c i p i o , ya no hay f i l a s .
$ f i l a = mysqli fetch row ( $consulta ) ;
i f ( $ f i l a === NULL) {
echo ’<p /><b>Quinto f e t c h : nada más</b> ’ ;
}
// Des con exi ón .
$ok = m y s q l i c l o s e ( $db ) ;
?>
</body>
</html>

Resultados:
mysqli fetch row:
0=1
1 = Recursos informáticos
2 = 22.44
mysqli fetch assoc:
id = 2
nombre = TechNote
precio siniva = 9.48
mysqli fetch array:

48

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
0=3
id = 3
1 = Prácticas Técnicas
nombre = Prácticas Técnicas
2 = 25.59
precio siniva = 25.59

mysqli fetch objectt:

$fila->id = 4
$fila->nombre = Pack Técnico
$fila->precio siniva = Pack Técnico

Ejemplo de la lectura de la totalidad de un resultado:


<!DOCTYPE html>
<head><t i t l e >C o n su l ta no p r e p a r a d a : l e c t u r a </ t i t l e ></head>
<body>
<?php
// Conexión ( con s e l e c c i ó n de l a b a s e de d a t o s ) .
$db = m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ eniweb ’ , ’ web ’ , ’ e n i ’ ) ;
i f ( ! $db ) {
e x i t ( ’ F a l l o en l a c o n e x i ó n . ’ ) ;
}
// E j e c u c i ó n de una c o n s u l t a
$ s q l = ’SELECT id , t i t u l o FROM l i b r o WHERE i d c o l e c c i o n = 1 ’ ;
$ c o n s u l t a = m y s q l i q u e r y ( $db , $ s q l ) ;
// L e c t u r a d e l r e s u l t a d o .
while ( $ f i l a = mysqli fetch assoc ( $consulta ) ) {
echo $ f i l a [ ’ i d ’ ] , ’ − ’ , $ f i l a [ ’ t i t u l o ’ ] , ’<br /> ’ ;
}
// Des con exi ón .
$ok = m y s q l i c l o s e ( $db ) ;
?>
</body>
</html>

1.4.5. Actualizaciones
Actualizar datos supone ejecutar consultas INSERT, UPDATE o DELETE.

La función mysqli affected rows permite conocer el número de filas afectadas por la
última consulta INSERT, UPDATE o DELETE.

49

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
La función mysqli insert id devuelve el último valor generado para la columna con
el tipo AUTO INCREMENT por una consulta INSERT

Ejemplo:
<!DOCTYPE html>
<head><t i t l e >C o n su l ta no p r e p a r a d a : a c t u a l i z a c i ó n </ t i t l e ></head>
<body>
<?php
// D e f i n i c i ó n de una peque ña f u n c i ó n de v i s u a l i z a c i ó n de l a
lista
// de l a s c o l e c c i o n e s .
f u n c t i o n m o s t r a r c o l e c c i o n e s ( $db ) {
$ s q l = ’SELECT ∗ FROM c o l e c c i o n ’ ;
$ c o n s u l t a = m y s q l i q u e r y ( $db , $ s q l ) ;
echo ”<b>L i s t a de l a s c o l e c c i o n e s :</b><br />” ;
while ( $ f i l a = mysqli fetch assoc ( $consulta ) ) {
echo $ f i l a [ ’ i d ’ ] , ’ − ’ , $ f i l a [ ’ nombre ’ ] ,
’ − ’ , $ f i l a [ ’ p r e c i o s i n i v a ’ ] , ’<br /> ’ ;
}
}
// Conexión ( con s e l e c c i ó n de l a b a s e de d a t o s ) .
$db = m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ eniweb ’ , ’ web ’ , ’ e n i ’ ) ;
i f ( ! $db ) {
e x i t ( ’ F a l l o en l a c o n e x i ó n . ’ ) ;
}

// V i s u a l i z a c i ó n de c o n t r o l .
m o s t r a r c o l e c c i o n e s ( $db ) ;
// C o n su l ta INSERT .
$ s q l = ”INSERT INTO c o l e c c i o n ( nombre , p r e c i o s i n i v a ) ” .
”VALUES( ’ Pack O f i m á t i c a ’ , 5 5 . 9 2 ) ” ;
$ c o n s u l t a = m y s q l i q u e r y ( $db , $ s q l ) ;
$ i d e n t i f i c a d o r = m y s q l i i n s e r t i d ( $db ) ;
echo ’ I d e n t i f i c a d o r de l a nueva c o l e c c i ó n = ’ ,
$ i d e n t i f i c a d o r , ’<br /> ’ ;
// C o n s ul ta UPDATE.
$ s q l = ”UPDATE c o l e c c i o n SET p r e c i o s i n i v a = p r e c i o s i n i v a ∗
1.05 ” .
”WHERE p r e c i o s i n i v a < 25 ” ;
$ c o n s u l t a = m y s q l i q u e r y ( $db , $ s q l ) ;
$número = m y s q l i a f f e c t e d r o w s ( $db ) ;
echo ” $número c o l e c c i ó n / c o l e c c i o n e s más.< br />” ;
// V i s u a l i z a c i ó n de c o n t r o l .

50

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
m o s t r a r c o l e c c i o n e s ( $db ) ;
?>
</body>
</html>

Inserción: Borrado:
<?php
$db = @ m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ r o o t ’ , ’ ’ , ’mydb ’ ) ;
i f ( $db ) {
echo ’ Conexión r e a l i z a d a c o r r e c t a m e n t e .< br /> ’ ;
echo ’ I n f o r m a c i ó n s o b r e e l s e r v i d o r : ’ ,
m y s q l i g e t h o s t i n f o ( $db ) , ’<br /> ’ ;
echo ’ V e r s i ó n d e l s e r v i d o r : ’ ,
m y s q l i g e t s e r v e r i n f o ( $db ) , ’<br /> ’ ;
} else {
printf (
’ E r r o r %d : %s .< br /> ’ ,
mysqli connect errno () , mysqli connect error () ) ;
};
// s q l t o d e l e t e a r e c o r d
$ s q l = ”DELETE FROM l i b r o s WHERE i d=3” ;

i f ( m y s q l i q u e r y ( $db , $ s q l ) ) {
echo ” Record d e l e t e d s u c c e s s f u l l y ” ;
} else {
echo ” E r r o r d e l e t i n g r e c o r d : ” . m y s q l i e r r o r ( $db ) ;
}
@ m y s q l i c l o s e ( $db ) ;
?>

1.4.6. Consultas preparadas


Una consulta preparada es una consulta parametrizada.

Los parámetros se representan por el carácter: ”?”.

En cada ejecución de este tipo de consultas se utiliza el valor actual de las variables
PHP asociadas a los parámetros.

Su interés consiste en poder utilizar varias veces la misma consulta con valores dife-
rentes sin tener que analizar de nuevo la consulta. Mejora el rendimiento.

51

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Pasos a realizar para realizar una consulta preparada:

Preparar la consulta (mysqli prepare).

Vincular las variables PHP con los parámetros de la consulta (mysqli stmt bind param).

Ejecutar la consulta (mysqli stmt execute).

Conocer el número de filas del resultado (mysqli num rows).

Vincular las variables PHP a las columnas del resultado (mysqli bind result).

Extraer las filas del resultado (mysqli stmt fetch).

Cerrar la consulta preparada (mysqli stmt close).

Sintaxis de mysqli stmt bind param:

booleano mysqli stmt bind param(objeto consulta, cadena tipos, mixto variables).

consulta es el nombre de la consulta que hemos parametrizado.

tipos es una cadena de caracteres donde indicamos los tipos de las variables a vincular.
Casos:

• i: variable de tipo entero.


• d: variable de tipo decimal.
• s: variable de tipo cadena de caracteres.

variables: lista de variables a vincular.

Ejemplo:
<!DOCTYPE html>
<head><t i t l e >C o n su l ta p r e p ar a d a : l e c t u r a </ t i t l e ></head>
<body>
<?php
// Conexión y s e l e c c i ó n de l a b a s e de d a t o s .
$db = m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ eniweb ’ , ’ web ’ , ’ e n i ’ ) ;
i f ( ! $db ) {
e x i t ( ’ F a l l o en l a c o n e x i ó n . ’ ) ;
}
// P r e p a r a c i ó n de l a c o n s u l t a .
$ s q l = ’SELECT id , t i t u l o FROM l i b r o WHERE i d c o l e c c i o n = ? ’ ;
$ c o n s u l t a = m y s q l i p r e p a r e ( $db , $ s q l ) ;
// Enl ace de l o s p a r ám e tro s .
$ok = m y s q l i s t m t b i n d p a r a m ( $ c o n s u l t a , ’ i ’ , $ i d c o l e c c i o n ) ;
// E j e c u c i ó n de l a c o n s u l t a .

52

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
$id coleccion = 1;
$ok = m y s q l i s t m t e x e c u t e ( $ c o n s u l t a ) ;
// Enl ace de l a s columnas d e l r e s u l t a d o .
$ok = m y s q l i s t m t b i n d r e s u l t ( $ c o n s u l t a , $id , $ tı́ t u l o ) ;
// L e c t u r a d e l r e s u l t a d o .
echo ”<b>C o l e c c i ó n número $ i d c o l e c c i o n </b><br />” ;
while ( mysqli stmt fetch ( $consulta ) ) {
echo ” $ i d − $ tı́ t u l o <br />” ;
}

// Nueva e j e c u c i ó n y l e c t u r a d e l r e s u l t a d o
// ( no v a l e r e h a c e r l o s e n l a c e s ) .
$id coleccion = 3;
$ok = m y s q l i s t m t e x e c u t e ( $ c o n s u l t a ) ;
echo ”<b>C o l e c c i ó n número $ i d c o l e c c i o n </b><br />” ;
while ( mysqli stmt fetch ( $consulta ) ) {
echo ” $ i d − $ tı́ t u l o <br />” ;
}
// Des con exi ón .
$ok = m y s q l i c l o s e ( $db ) ;
?>
</body>
</html>

1.4.7. Actualizaciones preparadas


Pasos a realizar para realizar una consulta preparada:

Preparar la consulta (mysqli prepare).

Vincular las variables PHP con los parámetros de la consulta (mysqli stmt bind param).

Ejecutar la consulta (mysqli stmt execute).

Conocer el número de filas del resultado (mysqli affected rows).

Conocer el valor del último identificador generado para una columna con el tipo
AUTO INCREMENT (mysqli stmt insert id).

Cerrar la consulta preparada (mysqli stmt close).

Ejemplo:

53

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<!DOCTYPE html>
<head><t i t l e >C o n s ul ta pr e p a r ad a : a c t u a l i z a c i ó n </ t i t l e ></head>
<body>
<?php
// Conexión y s e l e c c i ó n de l a b a s e de d a t o s .
$db = m y s q l i c o n n e c t ( ’ l o c a l h o s t ’ , ’ eniweb ’ , ’ web ’ , ’ e n i ’ ) ;
i f ( ! $db ) {
e x i t ( ’ F a l l o en l a c o n e x i ó n . ’ ) ;
}
// P r e p a r a c i ó n de l a c o n s u l t a .
$ s q l = ’UPDATE c o l e c c i o n SET g a s t o s s i n i v a = ? ’ .
’WHERE g a s t o s s i n i v a IS NULL ’ ;
$ c o n s u l t a = m y s q l i p r e p a r e ( $db , $ s q l ) ;
// E nlac e de l o s p a r á me tros .
$ok = m y s q l i s t m t b i n d p a r a m ( $ c o n s u l t a , ’ d ’ , $ g a s t o s s i n i v a ) ;

// E j e c u c i ó n de l a c o n s u l t a .
$gastos siniva = 1;
$ok = m y s q l i s t m t e x e c u t e ( $ c o n s u l t a ) ;
echo ’ Número de c o l e c c i o n e s m o d i f i c a d a s = ’ ,
m y s q l i s t m t a f f e c t e d r o w s ( $ c o n s u l t a ) , ’<br /> ’ ;
// P r e p a r a c i ó n de l a c o n s u l t a .
$ s q l = ’INSERT INTO c o l e c c i o n ( nombre ) VALUES( ? ) ’ ;
$ c o n s u l t a = m y s q l i p r e p a r e ( $db , $ s q l ) ;
// E nlac e de l o s p a r á me tros .
$ok = m y s q l i s t m t b i n d p a r a m ( $ c o n s u l t a , ’ s ’ , $nombre ) ;
// E j e c u c i ó n de l a c o n s u l t a .
$nombre = ’ S o l u c i o n e s I n f o r m á t i c a s ’ ;
$ok = m y s q l i s t m t e x e c u t e ( $ c o n s u l t a ) ;
echo ’ I d e n t i f i c a d o r de l a nueva c o l e c c i ó n = ’ ,
m y s q l i s t m t i n s e r t i d ( $ c o n s u l t a ) , ’<br /> ’ ;
// Descon exi ón .
$ok = m y s q l i c l o s e ( $db ) ;
?>
</body>
</html>

1.5. Sesiones
Introducción:

54

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
El protocolo HTTP es un protocolo ”sin estado”:

• Nada permite saber en qué página ha estado antes el usuario.


• Nada permite saber qué usuario solicita la página.

Para un sitio web interactivo es necesario identificar a un usuario que recorre las páginas
de su web (no pensar que son dos usuarios distintos) y, en general, conservar cierta
información del usuario de una página a otra.

El término sesión designa el periodo de tiempo correspondiente a la navegación conti-


nua de un usuario en un sitio web.

Gestionar las sesiones significa estar en condiciones de identificar el momento en que


un nuevo usuario accede a la página y conservar información hasta que abandona el
sitio web.
PHP ofrece un conjunto de funciones que facilitan la gestión de las sesiones de acuerdo con
los siguientes principios:
Un identificador único es automáticamente atribuido a cada sesión.

Este identificador es transmitido de una página a otra.

Los datos que se desean conservar durante la sesión se indican a PHP.


Principales funciones:
session start: abre una nueva sesión o reactiva la sesión actual.

session id: devuelve (o modifica) el identificador de la sesión.

session name: devuelve (o modifica) la variable en la que se almacena el identificador


de la sesión.

session destroy: elimina la sesión.


La matriz $ SESSION permite manipular fácilmente las variables de la sesión. session start:
Esta función consulta el entorno para detectar si una sesión ha sido abierta para el
usuario actual. Si es ası́, los datos guardados son recuperados. En caso contrario, se
abre una nueva sesión con la atribución de un identificador.

Esta función devuelve siempre TRUE.

Cualquier script relacionado con la gestión de sesiones tiene que invocar esta función
para tener acceso a las variables de sesión.

Si no se ha abierto todavı́a la sesión, está función intentará almacenar una cookie que
contenga el identificador de sesión en el sistema del usuario.

55

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Después de llamar a la función session start, los datos de la sesión puede manipularse
directamente en la matriz $ SESSION.

Para leer o modificar un valor de esta matriz solamente hay que acceder a él utilizando
su clave.

Para guardar un nuevo dato en la sesión, solo hay que almacenar este dato en esta
matriz con la clave que se quiera.

session id:
Invocada sin parámetro devuelve el valor del identificador de la sesión.

Si no se ha invocado antes session start no tendrá ningún valor.

Si se invoca con un parámetro modifica el identificador de la sesión. Utilidad de poco


interés en la mayorı́a de los casos.
session name:
Invocada sin parámetro devuelve el nombre de la variable en la que se almacena el
identificador de la sesión.

Siempre devuelve un valor aunque no se haya llamado a la función session start.

Si se invoca con un parámetro modifica el nombre de la variable. Utilidad de poco


interés en la mayorı́a de los casos.
session destroy:
Después de llamar a esta función la sesión deja de existir. Una llamada posterior a
session start abrirá una nueva sesión.

Esta función no elimina los datos de la sesión hasta que no finalice la ejecución del
script actual. Para eliminar inmediatamente la información se puede asignar una matriz
vacı́a a $ SESSION.

Tampoco destruye la cookie de sesión utilizada.


pagina-1.php
<?php
// A b r i r / r e a c t i v a r l a s e s i ó n .
session start () ;
// Guardar dos d a t o s en l a s e s i ó n .
$ SESSION [ ’ nombre ’ ] = ’ O l i v i e r ’ ;
$ SESSION [ ’ d a t o s ’ ] = // e s una m a t r i z . . .
a r r a y ( ’ nombre ’=> ’ O l i v i e r ’ , ’ a p e l l i d o ’=> ’ H e u r t e l ’ ) ;
?>

56

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<!DOCTYPE html>
<head><t i t l e >S e s i ó n − Página 1</ t i t l e ></head>
<body>
<div><a h r e f=” pagina −2.php”>Página 2</a></div>
</body>
</html>

pagina-2.php
<?php
// Llamada a s e s s i o n s t a r t .
session start () ;
?>
<!DOCTYPE html>
<head><t i t l e >S e s i ó n − Página 2</ t i t l e ></head>
<body>
<div>
<?php
// Llamada a s e s s i o n s t a r t .
session start () ;
// V i s u a l i z a c i ó n .
echo ’ $ SESSION [ \ ’ nombre \ ’ ] = ’ ,
i s s e t ( $ SESSION [ ’ nombre ’ ] ) ?$ SESSION [ ’ nombre ’ ] : ’ ’ ,
’<br /> ’ ;
echo ’ $ SESSION [ \ ’ d a t o s \ ’ ] [ \ ’ a p e l l i d o \ ’ ] = ’ ,
i s s e t ( $ SESSION [ ’ d a t o s ’ ] [ ’ a p e l l i d o ’ ] ) ?
$ SESSION [ ’ d a t o s ’ ] [ ’ a p e l l i d o ’ ] : ’ ’ ,
’<br /> ’ ;
?>
</div>
</body>
</html>

57

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
58

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 2

Normalización

2.1. ¿Qué es la normalización?


La traducción del esquema conceptual al lógico no es única. No todas las alternativas
posibles son igual de buenas.

Es útil contar con una medida de la calidad de la agrupación de los atributos en


relaciones.

Las formas normales son un indicador de esta calidad.

La falta de calidad de un diseño deficiente provoca problemas:

• Redundancias de Datos.
• Anomalı́as de actualización.
• Filas incorrectas.
• Exceso de espacio ocupado.
• Un diseño relacional sin redundancias es menos vulnerable a inconsistencias y
anomalı́as de actualización.

La Forma Normal (FN) satisfecha por un esquema relacional determina:

• Su grado de calidad respecto a esos problemas.


• Cuanto más alta es la FN en la que está: mejor calidad.
• Una FN se define con unas normas que debe cumplir el esquema basadas en
Dependencias Funcionales (DFs) y Multivaloradas (DMs).
• La Normalización mejora esos problemas descomponiendo el esquema relacional
en otros que cumplan FN más exigentes (aquellas con numeración más alta).

59

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
2.2. Redundancias y anomalı́as
Consideremos las siguientes tablas:

Empleados
Id-empleado NombreE DirecciónE Puesto Salario Centro
123A Ana Almansa c/Argentales Profesor 20.000 Informática
456B Bernardo Botı́n c/Barcelona Administrativo 15.000 Matemáticas
789C Carlos Crespo c/Cruz Catedrático 30.000 CC.Empresariales
012D David Dı́az c/Daroca Ayudante 10.000 Informática
Centros
NombreC DirecciónC Teléfono
Informática Complutense 123
Matemáticas Complutense 456
CC.Empresariales Somosaguas 789
Empleados-Centros
Id-empleado NombreP DirecciónP Puesto Salario Centro DirecciónC Teléfono
123A Ana Almansa c/Argentales Profesor 20.000 Informática Complutense 123
456B Bernardo Botı́n c/Barcelona Administrativo 15.000 Matemáticas Complutense 456
789C Carlos Crespo c/Cruz Catedrático 30.000 CC.Empresariales Somosaguas 789
012D David Dı́az c/Daroca Ayudante 10.000 Informática Complutense 123

Vemos que en la tabla Empleados-Centros se guarda mucha información. En concreto,


se guarda información sobre empleados y sobre centros un poco ”mezclada”.

Vemos que el teléfono, que va asociado al centro y no al empleado, aparece repetido


para todos los empleados del mismo centro (primera y cuarta fila).

Anomalı́as de actualización:

• Anomalı́as de inserción:
◦ Cuando se inserta sin respetar la dependencia funcional. Ejemplo: añadir un
empleado a Informática con un teléfono distinto de 123.
◦ Cuando se inserta el consecuente de la dependencia funcional sin el antece-
dente. Ejemplo: no se puede dar de alta un centro sin dar de alta un empleado.
• Anomalı́as de modificación: en aquellos casos con información redundante modi-
ficar solamente algunas de sus apariciones. Ejemplo: para modificar el teléfono de
Informática es preciso modificar todas sus apariciones.
• Anomalı́as de eliminación: cuando se eliminan todas las filas redundantes de una
dependencia funcional. Ejemplo: si se eliminan todos los empleados de un centro
también se elimina el centro.

60

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
2.3. Conceptos necesarios
2.3.1. Notación
Necesitamos representar de forma adecuada tanto la estructura de la tabla como las filas
de ella. Seguiremos la siguiente notación:

Consideremos la siguiente relación:

prof esor(id, nombre, salario, dpto, edif icio, presupuesto)

Utilizaremos la letra r para referirnos a un nombre de relación cualquiera.

Utilizaremos letras mayúsculas para referirnos a nombres de atributos cualesquiera:

r(A, B, C, D, ...)

Denotamos los conjuntos de atributos mediante letras griegas α, β, γ, etc.

Utilizamos la letra ρ para denotar el conjunto de todos los atributos de una relación r.

Por tanto, en lugar de escribir r(A, B, C, D, ...), escribiremos r(ρ).

Una instancia de una relación r es un conjunto de tuplas que se adapta al esquema de


dicha relación. Se representa de manera tabular.

Denotamos por t una tupla cualquiera de la instancia.

La notación t[β] representa la proyección de la tupla t sobre los atributos del conjunto
β.

Ejemplo: t1 [nombre, salario] = (P epe, 1000)

2.3.2. Dependencias funcionales


Claves
En la bases de datos es habitual imponer una serie de restricciones sobre los datos de
una tabla. Por ejemplo:

• A cada profesor le corresponde un único identificador.


• Cada profesor pertenece a un único departamento.
• Cada departamento se encuentra situado en un único edificio.

Una instancia de r que satisface las restricciones impuestas es una instancia legal de
r.

61

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
¿Cómo se expresan formalmente estas restricciones?

1. Claves.
2. Dependencias funcionales.

Superclave:

• Sea r(ρ) una relación, y ρ su conjunto de atributos.


• Un subconjunto α ⊆ ρ es una superclave de r(ρ) si para par de tuplas t, t0 de
cualquier instancia legal de r(ρ) se cumple:

t[α] = t0 [α] ⇒ t[ρ] = t0 [ρ]

Es decir, la igualdad de valores para un conjunto de campos determina la igualdad


de una tupla completa.

Clave candidata: Un subconjunto β ⊆ ρ es una clave candidata de r(ρ) si β es superclave


y no existe ningún subconjunto estricto α ⊂ β tal que α sea superclave. Es decir, si
quitamos algún campo a β ya no es clave.

Clave primaria: cualquier clave candidata.

Dependencia funcional
Una instancia de r(ρ) satisface la dependencia funcional α → β si para cada par de
tuplas t, t0 de la instancia se cumple:

t[α] = t0 [α] ⇒ t[β] = t0 [β]

Es decir, el valor de un conjunto de campos determina el valor de otro conjunto de


campos.

¿De dónde salen las dependencias funcionales?:

• Muchas son obvias (las dedice el diseñador).


• Otras son inferidas.

2.4. Formas normales


La forma normal de una tabla se refiere a:

• La forma normal más exigente que satisface dicha tabla.


• Representa el grado o nivel hasta donde se ha normalizado.

62

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
La forma normal de una BD se refiere a la forma normal más exigente que satisfacen
todas sus tablas.

Las formas normales más habituales, por orden ascendente de exigencia de las propie-
dades deseadas, son:

Forma normal Nivel de restricción


Primera (1FN) Muy poco restrictiva
Segunda (2FN)
Tercera (3FN)
Boyce/Codd (FNBC)
Cuarta (4FN)
Quinta (5FN)
Sexta (6FN) Muy restrictiva

En general, los diseños prácticos exigen, al menos, 3FN.

2.4.1. Primera forma normal


Un tabla está en primera forma normal (1FN) si los dominios de sus atributos sólo
pueden ser atómicos.

De esta forma se evitan multivalorados y compuestos.

Esta restricción se considera parte de la definición formal del Modelo Relacional y de


SQL. Es impuesta al pasar del modelo de Entidad/Relación al modelo relacional.

Ejemplo:

• Consideremos la tabla Centros. Es razonable pensar que un centro puede tener


muchos teléfonos.

Centros:
NombreC DirecciónC Teléfonos
Informática Complutense 123, 321, 213
Matemáticas Complutense 456
CC. Empresariales Somosaguas 789, 987

Evidentemente no está en forma normal. Cuestión: ¿cómo representar los teléfo-


nos?.
• Veamos varias formas de representar los valores de estos atributos multivaluados.
Por supuesto, todo está en 1FN aunque no todo posee la misma calidad. Por lo
tanto, 1FN es muy insuficiente por sı́ sola para determinar un diseño relacional
con calidad.

63

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Solución 1: Eliminar el atributo Teléfonos y crear una nueva relación que asocie
en cada fila un centro con un teléfono.
Centros:
NombreC DirecciónC
Informática Complutense
Matemáticas Complutense
CC. Empresariales Somosaguas
Teléfonos:
NombreC Teléfono
Informática 123
Informática 321
Informática 213
Matemáticas 456
CC. Empresariales 789
CC. Empresariales 987
Consecuencias:
◦ La clave de la 1a relación debe formar parte de clave de la 2a relación.
◦ Suceden anomalı́as cuando se borra un centro (en la tabla Centros) y olvida-
mos borrar los teléfonos asociados.
◦ La integridad referencial (FK) asegura evitar estas anomalı́as. El NombreC
de Teléfonos se hace FK con apunte a NombreC de Centros.
• Solución 2: ampliar la clave de la relación de manera que incluya al atributo
multivalorado.
NombreC DirecciónC Teléfono
Informática Complutense 123
Informática Complutense 321
Informática Complutense 213
Matemáticas Complutense 456
CC. Empresariales Somosaguas 789
CC. Empresariales Somosaguas 987
Consecuencias:
◦ Inconveniente: añade redundancia que provoca anomalı́as.
• Solución 3: si se conoce la cardinalidad máxima del atributo multivalorado se
pueden crear tantas columnas como la cardinalidad máxima
NombreC DirecciónC Teléfono1 Teléfono2 Teléfono3
Informática Complutense 123 321 213
Matemáticas Complutense 456 null null
CC. Empresariales Somosaguas 789 987 null

64

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Consecuencias:
◦ Inconveniente: uso de valores null.

Si el atributo multivalorado es compuesto, por ejemplo, representar varias direccio-


nes para un empleado: Empleados(Id empleado, NombreP,{Direcciones(Calle, Ciudad,
CódigoPostal)}).

Esta relación se puede descomponer en dos:

• Empleados(Id empleado, NombreP)


• DireccionesP(Id empleado, Calle, Ciudad, CódigoPostal)

Este procedimiento de desanidamiento se puede aplicar recursivamente a cualquier


relación con atributos multivalorados:

• teniendo en cuenta que es necesario propagar:


◦ la clave de la relación original a la clave de la nueva relación
◦ que contiene, además, la clave que identifica unı́vocamente al atributo multi-
valorado.

2.4.2. Segunda forma normal


Ejemplo:

• Consideremos la tabla:
Personal-proyectos
Id-empleado NúmeroP Horas NombreE NombreP
123A P-1 16 Ana Almansa Proyecto 1
012D P-1 8 David Dı́az Proyecto 1
012D P-2 4 David Dı́az Proyecto 2
NO está en 2a FN pero sı́ en 1a FN.
• Dependencias funcionales:
◦ (Id − empleado, N umeroP ) →DF 1 Horas
◦ Id − empleado →DF 2 N ombreE
◦ N umeroP →DF 3 N ombreP
• Problema: Todos los atributos no dependen de la PK completa. Alguno solo de
parte de ella (de algún atributo, no de todos).
• Existen anomalı́as de actualización causadas por DF2 y DF3. Como sus antede-
centes no son clave, puede haber varias filas con los mismos valores para estas
dependencias funcionales.

65

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
La 2FN evita este tipo de anomalı́as. Se basa en el concepto de Dependencia funcional
completa.

Dependencia funcional completa: la dependencia funcional α → β es completa si no


hay dependencia funcional en α − {Ai } → Y para algún Ai ∈ α.

Dependencia funcional parcial : la dependencia funcional α → β es parcial si hay de-


pendencia funcional en α − {Ai } → β para algún Ai ∈ α.

Un tabla está en 2FN si cada atributo que no forme parte de ninguna clave candidata
depende funcional y completamente de cada clave candidata.

¿Cómo lograr que una tabla esté en 2FN?:

• El procedimiento es dividir la tabla en tantas nuevas tablas como DFs que no


sean completas.
• El ejemplo anterior se traduce en:
DF1: PP1(Id-empleado,NúmeroP,Horas)
DF2: PP2(Id-empleado,NombreE)
DF3: PP3(NúmeroP,NombreP)
• Este procedimiento asegura que el resultado está, al menos, en segunda forma
normal.

2.4.3. Tercera forma normal


Consideremos la siguiente tabla:

Empleados-departamentos:
Id-empleado NombreE DirecciónE CódigoD NombreD DirectorD
123A Ana Almansa c/ Argentales DS Sistemas 999Z
012D David Dı́az c/ Daroca DS Sistemas 999Z

Existen dos dependencias funcionales ”claras”:

• Id − empleado →DF 1 (N ombreE, DireccionE, CodigoD)


• CodigoD →DF 2 (N ombreD, DirectorD)

Existe una dependencia funcional adicional: Id−empleado →DF 3 (N ombreD, DirectorD)


a través de la transitividad de las anteriores.

Dependencia funcional transitiva: la dependencia funcional α → β es transitiva si existe


un conjunto de campos γ que cumplen:

1. juntos no forman una clave candidata,

66

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
2. no son subconjunto de ninguna clave candidata y
3. se cumple que α → β y β → γ.

Un tabla está en 3FN si:

1. satisface la segunda forma normal y


2. todos los atributos que no forman parte de una CC no dependen transitivamente
de ninguna CC.

El procedimiento para normalizar esta relación consiste en descomponerla en los atri-


butos definidos por la dependencia funcional responsable de la transitividad.

En este ejemplo se descompone en dos tablas:

• Para →DF 1 : ed1(Id-empleado,NombreE,DireccionE,CodigoD)


• Para →DF 2 : ed2(CodigoD,NombreD,DirectorD)

2.4.4. Forma normal de Boyce-Codd (FNBC)


Cierre de un conjunto de dependencias
Para entender la forma normal de Boyce-Codd es preciso introducir nuevos conceptos
sobre dependencias funcionales.

Propiedades de las DFs:

• Una dependencia funcional α → β es trivial si y sólo si β ⊆ α:


◦ Por ejemplo: AB → A, C → C.
◦ Las DFs triviales siempre se satisfacen por cualquier instancia.
• El hecho de que un conjunto de atributos α sea superclave de una relación r(ρ)
se puede expresar mediante la dependencia funcional α → ρ.
• Podemos añadir atributos en el lado izquierdo o eliminar en el lado derecho de
una DF sin alterar su satisfactibilidad:
◦ Si se cumple α → β, también se cumple αγ → β
◦ Si se cumple α → βγ, también se cumple α → β

Cierre de un conjunto de DFs:

• Sea r(ρ) una relación, y F un conjunto de DFs.


• Decimos que F implica la DF α → β si en toda instancia de r(ρ) en la que se
satisfagan las dependencias funcionales de F , también se satisface la dependencia
α → β.
◦ Ejemplo: El conjunto {A → B, B → C} implica A → C.

67

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Sea F un conjunto de DFs de una relación r(ρ). El cierre de F (escrito F ∗ ) es el
conjunto de DFs que están implicadas por F .
• Se puede obtener el cierre de cualquier conjunto F mediante los tres axiomas de
Armstrong:
◦ Reflexividad: Si β ⊆ α, entonces se cumple α → β.
◦ Aumentatividad: Si α → β entonces αγ → βγ para cualquier conjunto γ.
◦ Transitividad: Si α → β y β → γ, entonces α → γ.
• Estos tres axiomas son correctos y completos:
◦ Correctos: no dan lugar a DFs incorrectas.
◦ Completos: son suficientes para obtener F ∗ .
• Aunque las reglas de reflexividad, aumentatividad y transitividad son suficientes
para obtener F ∗ , suele ser útil la aplicación de otras reglas que se deducen de las
primeras:
◦ Union: Si α → β y α → γ, entonces α → βγ.
◦ Descomposición: Si α → βγ, entonces α → β y α → γ.
◦ Pseudotransitividad: Si α → β y γβ → δ, entonces αγ → δ.
• Ejemplo:
◦ Consideremos la tabla:
id nombre salario dpto edificio presupuesto
INF1 Laura Estévez álgebra 1600 FM 30000
INF2 Juan Herrero Sistmas inf. 1550 FM 25000
INF3 Javier Guzmán álgebra 1600 FM 30000
◦ Sea F = {(id → nombre, dpto), (id → salario), (dpto → edif icio), (dpto →
presupuesto)}
◦ Enumera algunas de las DFs que forman parte de F ∗ .
• Calculo de F ∗ :
◦ Existe un algoritmo para calcular F ∗ .
◦ Consiste en la aplicación exhaustiva de los tres axiomas de Armstrong.
◦ Este algoritmo no se utiliza en la práctica, ya que el conjunto F ∗ es demasiado
grande.
◦ Existe una manera de saber si una dependencia funcional α → β pertenece a
F ∗ sin tener que enumerar todo el conjunto F ∗ .
◦ La herramienta utilizada será el cierre de un conjunto de atributos.
• Cierre de un conjunto de atributos:
◦ Sea α ⊆ ρ un conjunto de atributos en r(ρ) y un conjunto F de dependencias
funcionales. El cierre de α (denotado α∗ ) bajo F es el conjunto de atributos
de r(ρ) que está funcionalmente determinado por α.

68

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
◦ Es decir, el mayor conjunto de atributos posible β tales que α → β ∈ F ∗
◦ Ejemplo: Sea F = {A → CE, C → D, CE → B} entonces A∗ = {A, C, E, D, B}

Ejercicio. Dado el siguiente conjunto de dependencias:

A→B
A→C
CG → H
CG → I
B→H

Calcula {AG∗ }

Utilidades del cierre de un atributo:

1. Averiguar si α es superclave de r(ρ): α es superclave si y sólo si α∗ = ρ.


2. Comprobar si α → β pertenece a F ∗ : α → β ∈ F si y sólo si β ⊆ α∗ .
3. Calcular F ∗ : para cada α ⊆ ρ, y para cada conjunto β ⊆ α∗ , la dependencia
α → β pertenece a F ∗ .

Ejemplo:

• Consideremos la relación:
id nombre salario dpto edificio presupuesto
INF1 Laura Estévez álgebra 1600 FM 30000
INF2 Juan Herrero Sistmas inf. 1550 FM 25000
INF3 Javier Guzmán álgebra 1600 FM 30000
• Dados: (id → nombre, dpto), id → salario, dpto → edif icio, dpto → presupuesto
• ¿Es id superclave?. id∗ = {id, nombre, dpto, salario, edif icio, presupuesto}. Por
tanto, id es superclave.
• ¿se deduce dpto → edif icio, salario, a partir de estas DFs?. dpto∗ = {dpto, edif icio, presupuesto}.
Como salario 6∈ dpto∗ , no se deduce.

Formulación de la forma normal de Boyce-Codd (FNBC)


La FNBC es más estricta que la reformulación de la 3FN:

• La FNBC evita otras redundancias que la 3FN no puede.


• Pero la FNBC no siempre es posible conseguirla.

69

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Un esquema de relación r(ρ) está en forma normal de Boyce-Codd con respecto a un
conjunto de dependencias funcionales F si y sólo si para todo α → β ∈ F ∗ se cumple
uno de los siguientes requisitos:

• α → β es una DF trivial.
• α es superclave de ρ.

Ejemplo:

• Consideremos la siguiente relación r(profesor,DNI-profesor,asignatura).


• Sea F formado por: prof esor → DN I −prof esor y DN I −prof esor → prof esor
• Sin embargo, la relación no está en BCNF, ya que las siguientes relaciones perte-
necen a F ∗ : prof esor → DN I − prof esor y DN I − prof esor → prof esor
• Ninguna de ellas es trivial, y ni profesor ni DNI-profesor forman superclaves.

Transformación a BCNF:

• Partimos de una relación r(ρ) y un conjunto F de dependencias funcionales.


• Paso 1: Determinar una dependencia α → β ∈ F ∗ que no cumpla las condiciones
de la BCNF. Si no hay, finalizar.
• Paso 2: Descomponer r(ρ) en dos relaciones:
S
◦ r1(α β)
◦ r2(ρ − (β − α))
• Paso 3: Aplicar recursivamente el proceso de transformación a r1 y a r2 por
separado.

Ejemplo:

• Sea r(A, B, C, D, E) y el siguiente conjunto de dependencias funcionales: F =


{A → B, BC → D}
• La dependencia A → B no cumple las condiciones de la BCNF:
◦ A → B no es trivial.
◦ A no es superclave porque A∗ = {A, B}
• Por tanto, descomponemos la tabla (A, B, C, D, E) en dos tablas: (A, B) y (A, C, D, E).
• La relación r1(A, B) ya está en BCNF.
• En general, cualquier relación con dos atributos está en BCNF.
• Pasamos a la tabla r2(A, C, D, E).
• ¿Existe una DF en F ∗ que no cumpla las dos condiciones de BCNF?.
• No podemos encontrarla en F , ya que F = {A → B, BC → D}, y ninguna de
estas dos dependencias tiene sentido en (A, C, D, E).

70

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 2.1: Test sobre BCNF

• Sin embargo, la dependencia AC → D está contenida en el cierre de F y no


cumple ninguna de las dos condiciones porque:
◦ AC → D no es trivial.
◦ AC no es superclave en (A, C, D, E), porque {AC}∗ = {A, C, B, D}. ¿Cómo
hemos obtenido esta dependencia? Lo veremos a continuación.
• Descomponemos (A,C,D,E) en dos: (A,C,D) y (A,C,E).
• Tanto (A, C, D) como (A, C, E) están en BCNF. ¿Por qué lo sabemos? Lo
veremos a continuación.
• Por tanto, hemos finalizado. Resultado de la descomposición: (A, B) (A,C,D)
(A,C,E)

Transformación a BCNF:

• ¿Cómo saber si existe una dependencia en F ∗ que no cumple las condiciones de


la BCNF sin necesidad de calcular F ∗ ?
• Para la descomposición de la relación inicial (primera llamada recursiva) pue-
de demostrarse que si F no contiene DFs que violen las condiciones de BCNF,
entonces F ∗ tampoco las tiene.
• Sin embargo, esto no es cierto para las relaciones que resultan de descomponer la
relación inicial en dos. Es necesario aplicar un algoritmo más sofisticado.
• Dada una relación r(ρ) y un conjunto de dependencias funcionales F , queremos
saber si existe una dependencia en F ∗ que no cumpla las condiciones de BCNF.
Ver figura 2.1.

Ejemplo:

• Sea (A, B, C, D, E)
• Volvemos al ejemplo anterior: F = {A → B, BC → D}
• Como estamos descomponiendo la relación inicial, sólo es necesario buscar depen-
dencias funcionales que no cumplan las condiciones de BCNF en F .
◦ En nuestro caso, A → B no cumple las condiciones.
• Para (A, B):

71

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
{A}∗ = {A, B} contiene todo {A, B} {B}∗ = {B} no contiene atributos de
{A, B} − {B}
{A, B}∗ = {A, B} contiene todo {A, B}
◦ Aplicamos el algoritmo de búsqueda de DFs que violen las condiciones de
BCNF en la tabla (A, B)
◦ Por tanto (A,B) está en BCNF. Eso ya lo sabı́amos, porque cualquier relación
con dos atributos está en BCNF.
• Para (A, C, D, E):
◦ Aplicamos el algoritmo de búsqueda de DFs que violen las condiciones de
BCNF.
{A}∗ = {A, B}
{C}∗ = {C}
{D}∗ = {D}
{E}∗ = {E}
{A, C}∗ = {A, C, B, D}
{A, D}∗ = {A, D, B}
{A, E}∗ = {A, E, B}
{C, D}∗ = {C, D}
{C, E}∗ = {C, E}
{D, E}∗ = {D, E}
{A, C, D}∗ = {A, C, D, B}
{A, C, E}∗ = {A, C, E, B, D}
{A, D, E}∗ = {A, D, E, B}
{C, D, E}∗ = {C, D, E}
{A, C, D, E}∗ = {A, C, D, E, B}
◦ Aplicamos el algoritmo de búsqueda de DFs que violen las condiciones de
BCNF: {A, C}∗ = {A, C, B, D}
◦ La DF que no cumple las condiciones de BCNF es: AC → D
• Comprobamos (A, C, D):
{A}∗ = {A, B}
{C}∗ = {C}
{D}∗ = {D}
{A, C}∗ = {A, C, B, D}
{A, D}∗ = {A, D, B}
{C, D}∗ = {C, D}
Está en BCNF.

Comprobamos (A, C, E):

72

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
{A}∗ = {A, B}
{C}∗ = {C}
{E}∗ = {E}
{A, C}∗ = {A, C, B, D}
{A, E}∗ = {A, E, B}
{C, E}∗ = {C, E}
{A, C, E}∗ = {A, C, E, B, D}

Está en BCNF.

73

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
74

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Parte II

Modelos alternativos de bases de


datos

75

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 3

XML

3.1. Modelo semiestructurado de bases de datos


Modelos lógicos de base de datos:
• Modelo relacional (el más usado).
• Modelo jerárquico.
• Modelo de red.
• Modelo orientado a objetos.
• Modelo semiestructurado (XML).
• Y alguno más.
Caracterı́sticas generales del modelo semiestructurado:
• Se pueden ver como una relajación de algunas de las caracterı́sticas del modelo
relacional.
• Algunas entidades permiten la omisión de información en ciertos atributos.
• Existen distintos tipos posibles para un mismo atributo.
• Algunos atributos pueden no disponer de una estructura predefinida o no ser
atómicos.
Estándares para manejar datos semiestructurados:
• OEM: Object Exchange Model.
• XML:eXtensible Markup Language.
• JSON: JavaScript Object Notation.
Ventajas e inconvenientes:
• Mayor flexibilidad en la representación de los datos.
• Consultas y modificaciones más ineficientes que en el modelo relacional.

77

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
3.1.1. Lenguaje XML
Es un lenguaje de marcado (markup).

Permite generar documentos con anotaciones legibles por una persona.

Definido como estándar por el W3C (World Wide Web Consortium).

Aspecto parecido a HTML, con una diferencia importante:

• HTML: Define la estructura de las páginas Web.


• XML: Define una estructura de datos arbitraria.

XML no se concibió inicialmente para modelizar bases de datos.

XML se utiliza normalmente como lenguaje común de intercambio de datos entre


sistemas heterogéneos.

Ventajas:

• Legibilidad.
• Representación jerárquica de la información.
• Numerosos intérpretes de XML disponibles.

Inconvenientes:

• Ineficiencia en espacio y tiempo.

Algunos formatos de archivo basados en XML:

• XHTML : Extensible Hypertext Markup Language.


• SVG : Scalable Vector Graphics.
• MathML : Mathematical Markup Language.
• X3D : Extensible 3D Graphics.
• ODF : Open Document Format (archivos .odt).
• OOXML : Office Open XML (archivos .docx).
• WSDL : Web Services Description Language.

78

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
3.1.2. Bases de datos con XML
Una base de datos XML es un sistema que almacena datos XML de manera persistente.
Sistemas gestores de bases de datos XML nativos:
• BaseX (http://basex.org/)
• eXistdb (http://www.exist-db.org/)
• Sedna (http://sedna.org/)
Existe un lenguaje estándar para realizar consultas sobre las bases de datos XML:
XQuery.

3.2. Lenguaje XML


Ejemplo de código XML:
<?xml v e r s i o n =”1.0” e n c o d i n g=”UTF−8”?>
<agenda>
<c o n t a c t o d n i =”51233412H”>
<nombre>David</nombre>
<a p e l l i d o s >Álvez Campos</ a p e l l i d o s >
<d i r e c c i ó n >
<c a l l e >Paseo de Ondarreta </ c a l l e >
<numero>5</numero>
<c o d i g o −p o s t a l >20018</ c o d i g o −p o s t a l >
<l o c a l i d a d >San S e b a s t i á n </ l o c a l i d a d >
</ d i r e c c i ó n >
< t e l e f o n o t i p o =”c a s a ”>943102321</ t e l e f o n o >
< t e l e f o n o t i p o =”m o v i l ”>617702341</ t e l e f o n o >
</c o n t a c t o >
<c o n t a c t o d n i =”46821354T”>
<nombre>Vı́ c t o r </nombre>
<a p e l l i d o s >Martı́n Moreno</ a p e l l i d o s >
< t e l e f o n o t i p o =”c a s a ”>914621100</ t e l e f o n o >
</c o n t a c t o >
</agenda>

La cabecera de un documento XML


<?xml v e r s i o n =”1.0” e n c o d i n g=”UTF−8”?>

está delimitada por los sı́mbolos <? y ?>


• Indica que el documento se corresponde con la versión 1.0 del estándar de XML
y que utiliza la codificación UTF-8.
• La cabecera también puede contener otras declaraciones, tales como referencias
al DTD o XML Schema asociado al documento XML, e instrucciones de procesa-
miento.
Los elementos de un archivo XML están compuestos de una etiqueta de inicio, una
etiqueta de fin, y un contenido:

79

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<a p e l l i d o s >Martı́n Moreno</ a p e l l i d o s >

Sintaxis de los elementos:

• Los nombres de las etiquetas son identificadores formados por letras, números y
caracteres de guión/subrayado. Deben comenzar por una letra.
• Un documento XML ha de contener un elemento raı́z que contenga a los demás.
• Si un elemento no tiene contenido (no contiene otras etiquetas), puede utilizarse
una sintaxis alternativa:
< t e l e f o n o t i p o =”c a s a ” num=”913102321”/ >

Atributos:

• Cada elemento puede contener cero, uno o más atributos.


• Los atributos se colocan en la etiqueta de inicio del elemento al que van asociados.
Cada atributo consta de un nombre y un valor.
• Las reglas para el nombre de un atributo son las mismas que para los nombres
de etiquetas. El valor ha de estar delimitado entre comillas simples (’) o comillas
dobles (”).

Comentarios: delimitados por <!-- y -->.


<!−− Esto e s un c o m e n t a r i o −−>

Entidades: sirven para representar caracteres que tienen un significado especial en XML
(forman parte de la sintaxis), tales como los sı́mbolos <, >. Comienzan por & y terminan
por ;

Entidad Sı́mbolo
&amp; &
&lt; <
&gt; >
&quot; ”
&apos; ’

Secciones CDATA: Sirven para expresar contenido que contenga caracteres especiales
(<, >, etc.), sin necesidad de utilizar entidades. Están delimitadas por <![CDATA[ y
]]>. Ejemplo:
<c ó d i g o −f u e n t e > <![CDATA[
i f ( x > 0 && x <= 1 0 ) then p r i n t ( ” Hola ” )
]] > </ c ó d i g o −f u e n t e >

80

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Sin secciones CDATA:
<c ó d i g o −f u e n t e >
i f ( x &g t ; 0 &amp;&amp ; x &l t ;= 1 0 ) then
p r i n t ( ” Hola ” )
</c ó d i g o −f u e n t e >

Un pequeño dilema: la información como elemento o como atributos:


• No existe ninguna regla general que indique cómo representar la información, pero
ha de tenerse en cuenta lo siguiente.
• Como atributos:
◦ Para valores que son atómicos.
◦ Ocupan menos espacio (no hay etiquetas de inicio/cierre).
◦ Son más adecuados para claves primarias y externas.
• Como elementos:
◦ Cuando su información puede ser compuesta (varios elementos).
◦ Permiten agrupar varios elementos del mismo o distinto tipo.
◦ Son recomendables cuando el valor es muy extenso, o requiere de una sección
CDATA.

3.3. Documentos bien formados


Un documento está bien formado si cumple las normas básicas de XML a nivel sintácti-
co:
• Tiene una cabecera.
• Tiene un único elemento raı́z.
• Las etiquetas de los elementos están correctamente anidadas.
• Los valores de los atributos se encuentran delimitados por comillas simples o
dobles.
Los documentos bien formados pueden ser interpretados por cualquier librerı́a o herra-
mienta de manipulación de documentos XML.
En muchas ocasiones es deseable especificar restricciones adicionales:
• Etiquetas permitidas/prohibidas.
• Atributos permitidos/obligatorios.
• Tipo de contenido: entero, cadena, etc.
Existen tecnologı́as que permiten concretar el contenido de un fichero XML más allá
de una simple comprobación sintáctica:

81

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Document type definition (DTD).
• XML Schema.
• Relax NG (no es estándar W3C).

Un documento XML es válido con respecto a una DTD (o un Schema) si está bien
formado y su contenido se adecua a las restricciones impuestas por dicho DTD (o
Schema).

3.3.1. Document Type Definition


Una DTD (Document Type Definition) es un conjunto de declaraciones que definen los
elementos y atributos que pueden aparecer en un documento determinado.

Componentes:

• Declaraciones <!ELEMENT>.
• Declaraciones <!ATTLIST>.
• Declaraciones <!ENTITY> (permiten especificar abreviaturas, no las usaremos).

Declaraciones ELEMENT:

• Permiten definir los nombres de elementos (etiquetas) permitidos en un documen-


to XML.
• Sintaxis:
<!ELEMENT nombre c o n t e n i d o >

Ejemplo:
<!ELEMENT d i r e c c i ó n ( c a l l e , número , c ó d i g o −p o s t a l , l o c a l i d a d )>

define un elemento <dirección> que contiene los siguientes elementos: <calle>,


<número>, <códigopostal> y <localidad>.

• Podemos expresar el contenido de un elemento mediante una secuencia de ele-


mentos hijo:
<!ELEMENT nombre ( elem −1 , elem −2 , . . . , elem−n)>

indica que el elemento <nombre> ha de incluir los elementos <elem-1>, <elem-2>,


..., <elem-n>, y en el orden indicado. Esta especificación permite la siguiente
estructura:
<nombre>
<elem−1> . . . </elem−1>
<elem−2> . . . </elem−2>
...
<elem−n> . . . </elem−n>
</nombre>

82

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Podemos utilizar el operador (|) para expresar distintas alternativas en el conte-
nido de un elemento:
<!ELEMENT nombre ( elem −1 | elem −2 | ... | elem−n)>

• Esta vez el elemento <nombre> debe incluir <elem-1>, o bien, <elem-2>, ..., o
bien, <elem-n>. Ejemplo:
<!ELEMENT p o s i c i ó n ( d i r e c c i ó n | c o o r d e n a d a s )>

Este código permite:


<p o s i c i ó n >
<d i r e c c i ó n > . . . </ d i r e c c i ó n >
</ p o s i c i ó n >

o:
<p o s i c i ó n >
<c o o r d e n a d a s > . . . </c o o r d e n a d a s >
</ p o s i c i ó n >

Es posible mezclar secuencias con alternativas. Ejemplo:


<!ELEMENT p o s i c i ó n ( d i r e c c i ó n | ( l a t i t u d , l o n g i t u d ) )>

permite:
<p o s i c i ó n >
<d i r e c c i ó n > . . . </ d i r e c c i ó n >
</ p o s i c i ó n >
<p o s i c i ó n >
<l a t i t u d > . . . </ l a t i t u d >
<l o n g i t u d > . . . </ l o n g i t u d >
</ p o s i c i ó n >

Para indicar que un elemento sólo puede contener texto en su interior, utilizamos
#PCDATA. Ejemplo:
<!ELEMENT c a l l e (#PCDATA)>

<c a l l e >Avenida de P o r t u g a l </ c a l l e >

La palabra clave #PCDATA puede utilizarse dentro de una secuencia, o combinada


con otras alternativas:
<!ELEMENT d e s c r i p c i ó n (#PCDATA | ( t i t u l o , d e t a l l e s )) >

Se pueden especificar reglas de cardinalidad dentro del contenido de un elemento.

En ausencia de reglas de cardinalidad los elementos de una secuencia deben aparecer


una y sólo una vez.

83

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Los operadores ?, *, + permiten expresar otras reglas de cardinalidad:

Operador Significado
? El elemento puede no aparecer o aparecer una sóla vez
+ El elemento puede aparecer una o más veces
* El elemento puede aparecer cero, una, o más veces

Ejemplo:
<!ELEMENT c o n t a c t o ( nombre , a p e l l i d o s , d i r e c c i ó n ? , t e l e f o n o ∗)>

permite:
<c o n t a c t o d n i =”51233412H”>
<nombre>David</nombre>
<a p e l l i d o s >Álvez Campos</ a p e l l i d o s >
<d i r e c c i ó n > . . . </ d i r e c c i ó n >
< t e l e f o n o t i p o =”c a s a ”>943102321</ t e l e f o n o >
< t e l e f o n o t i p o =”m o v i l ”>617702341</ t e l e f o n o >
</c o n t a c t o >
<c o n t a c t o d n i =”51233412H”>
<nombre>David</nombre>
<a p e l l i d o s >Álvez Campos</ a p e l l i d o s >
</c o n t a c t o >

Otro ejemplo:
<!ELEMENT body (#PCDATA | b | i ) ∗ >

Define una etiqueta <body> que puede contener en su interior texto intercalado con los
elementos <b> e <i>.

Otros tipos de contenido:

• EMPTY: el elemento no contiene nada en su interior.


• ANY: libertad total en el contenido.

Más ejemplos:
<!ELEMENT p r o d u c t o EMPTY>

permite:
<p r o d u c t o i d =”e34 ” c a n t i d a d =”2”/>

<!ELEMENT d e s c r i p c i ó n ANY>

permite:
<d e s c r i p c i ó n >
<t i t u l o >D e s c r i p c i ó n d e l o b j e t o </ t i t u l o >
E s t e e s e l c o n t e n i d o de l a d e s c r i p c i ó n .
</ d e s c r i p c i ó n >

84

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Declaraciones ATTLIST:

• Especifican los atributos que pueden ir asociados a un elemento. Sintaxis:


<!ATTLIST e l e m e n t o a t r i b u t o t i p o v a l o r >

donde:
◦ elemento indica la etiqueta en la que se adjunta el atributo.
◦ atributo es el nombre del atributo.
◦ tipo indica el conjunto de valores que puede tener el atributo.
◦ valor hace referencia al valor por defecto y a la obligatoriedad de incluir el
atributo.
• tipo puede ser uno de los siguientes:
◦ CDATA: Cadena de caracteres (Tipo por defecto).
◦ Lista de valores: Valores posibles para el atributo.
◦ ID: Identificador único. Dos elementos no pueden tener el mismo valor para
este atributo.
◦ IDREF : Referencia al identificador de un elemento.
◦ IDREFS : Lista de referencias a identificadores de otros elementos, separadas
por espacios.
• valor puede ser uno de los siguientes:
◦ ”Valor”: El atributo es opcional. Si no se indica, tomará el valor indicado.
◦ #FIXED ”Valor”: El atributo es obligatorio y ha de tener el valor indicado.
◦ #REQUIRED: El atributo es obligatorio.
◦ #IMPLIED: El atributo es opcional.
• Se pueden declarar varios atributos con el mismo nombre, siempre que pertenezcan
a elementos distintos.
• En los elementos que tienen tipos ID, IDREF, IDREFS, los identificadores tienen
que adecuarse a la sintaxis XML.
• En particular, han de comenzar por carácter alfabético.
• Ejemplos:
<!ATTLIST t e l e f o n o t i p o
( c a s a | t r a b a j o | m o v i l ) ” c a s a”>
< t e l e f o n o t i p o =”m o v i l”> . . . </ t e l e f o n o >
<!ATTLIST c o n t a c t o d n i CDATA #REQUIRED>
<c o n t a c t o d n i =”51233412H”>
...
</c o n t a c t o >

contactos.xml:

85

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<?xml v e r s i o n =”1.0” e n c o d i n g=”UTF−8”?>
<agenda>
<c o n t a c t o d n i =”51233412H”>
<nombre>David</nombre>
<a p e l l i d o s >Álvez Campos</ a p e l l i d o s >
<d i r e c c i ó n >
<c a l l e >Paseo de Ondarreta </ c a l l e >
<numero>5</numero>
<c ó d i g o −p o s t a l >20018</ c ó d i g o −p o s t a l >
<l o c a l i d a d >San S e b a s t i á n </ l o c a l i d a d >
</ d i r e c c i ó n >
< t e l e f o n o t i p o =”c a s a ”>943102321</ t e l e f o n o >
< t e l e f o n o t i p o =”m o v i l ”>617702341</ t e l e f o n o >
</c o n t a c t o >
<c o n t a c t o d n i =”46821354T”>
<nombre>Vı́ c t o r </nombre>
<a p e l l i d o s >Martı́n Moreno</ a p e l l i d o s >
< t e l e f o n o t i p o =”c a s a ”>914621100</ t e l e f o n o >
</c o n t a c t o >
</agenda>

contactos.dtd:
<!ELEMENT agenda ( c o n t a c t o)+>
<!ELEMENT c o n t a c t o ( nombre , a p e l l i d o s , d i r e c c i ó n ? , t e l e f o n o ∗)>
<!ELEMENT nombre (#PCDATA)>
<!ELEMENT a p e l l i d o s (#PCDATA)>
<!ELEMENT d i r e c c i ó n ( c a l l e , numero , c ó d i g o −p o s t a l , l o c a l i d a d )>
<!ELEMENT c a l l e (#PCDATA)>
<!ELEMENT numero (#PCDATA)>
<!ELEMENT c ó d i g o −p o s t a l (#PCDATA)>
<!ELEMENT l o c a l i d a d (#PCDATA)>
<!ELEMENT t e l e f o n o (#PCDATA)>
<!ATTLIST t e l e f o n o t i p o ( c a s a | t r a b a j o | m o v i l ) #REQUIRED>
<!ATTLIST c o n t a c t o d n i CDATA #REQUIRED>

Asociar una DTD a un documento:

• Se utiliza la declaración <!DOCTYPE> en la cabecera del documento XML.


• Para DTDs incluidas en el documento XML:
<!DOCTYPE elem−r aı́ z [
. . . d e c l a r a c i o n e s DTD . . .
] >

• Para DTDs separadas en un archivo externo:


<!DOCTYPE elem−r aı́ z SYSTEM ” a r c h i v o . dtd”>

• Ejemplo de DTD interna:


<?xml v e r s i o n =”1.0” e n c o d i n g=”UTF−8”?>
<!DOCTYPE agenda [
<!ELEMENT agenda ( c o n t a c t o)+>
<!ELEMENT c o n t a c t o ( nombre , a p e l l i d o s , d i r e c c i ó n ? , t e l e f o n o ∗)>
<!ELEMENT nombre (#PCDATA)>
<!ELEMENT a p e l l i d o s (#PCDATA)>
<!ELEMENT d i r e c c i ó n ( c a l l e , numero , c ó d i g o −p o s t a l , l o c a l i d a d )>
<!ELEMENT c a l l e (#PCDATA)>

86

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<!ELEMENT numero (#PCDATA)>
<!ELEMENT c ó d i g o −p o s t a l (#PCDATA)>
<!ELEMENT l o c a l i d a d (#PCDATA)>
<!ELEMENT t e l e f o n o (#PCDATA)>
<!ATTLIST t e l e f o n o t i p o ( c a s a | t r a b a j o | m o v i l ) #REQUIRED>
<!ATTLIST c o n t a c t o d n i CDATA #REQUIRED>
]>
<agenda>
<c o n t a c t o d n i =”51233412H”>
...
</c o n t a c t o >
<c o n t a c t o d n i =”46821354T”>
...
</c o n t a c t o >
</agenda>

• Ejemplo de DTD externa:


<?xml v e r s i o n =”1.0” e n c o d i n g=”UTF−8”?>
<!DOCTYPE agenda SYSTEM ” c o n t a c t o s . dtd”>
<agenda>
<c o n t a c t o d n i =”51233412H”>
<nombre>David</nombre>
<a p e l l i d o s >Álvez Campos</ a p e l l i d o s >
<d i r e c c i ó n >
<c a l l e >Paseo de Ondarreta </ c a l l e >
<numero>5</numero>
<c ó d i g o −p o s t a l >20018</ c ó d i g o −p o s t a l >
<l o c a l i d a d >San S e b a s t i á n </ l o c a l i d a d >
</ d i r e c c i ó n >
< t e l e f o n o t i p o =”c a s a ”>943102321</ t e l e f o n o >
< t e l e f o n o t i p o =”m o v i l ”>617702341</ t e l e f o n o >
</c o n t a c t o >
<c o n t a c t o d n i =”46821354T”>
<nombre>Vı́ c t o r </nombre>
<a p e l l i d o s >Martı́n Moreno</ a p e l l i d o s >
< t e l e f o n o t i p o =”c a s a ”>914621100</ t e l e f o n o >
</c o n t a c t o >
</agenda>

• Ejemplo de una tienda: tienda.xml:


<?xml v e r s i o n =”1.0” e n c o d i n g=”UTF−8”?>
<!DOCTYPE t i e n d a SYSTEM ” Tienda . dtd”>
<t i e n d a >
< a r t i c u l o i d = ” c01”>
<nombre>A c e i t e V i r g e n Extra S e r i e ORO</nombre>
<c a n t i d a d >500 ml</c a n t i d a d >
<p r e c i o d i v i s a = ”EUR”>8.36</ p r e c i o >
<p r e c i o d i v i s a = ”GBP”>7.17</ p r e c i o >
</ a r t i c u l o >
< a r t i c u l o i d = ” c02”>
<nombre>A c e i t e V i r g e n Extra </nombre>
<c a n t i d a d >1000 ml</c a n t i d a d >
<p r e c i o d i v i s a = ”EUR”>7.50</ p r e c i o >
<p r e c i o d i v i s a = ”GBP”>6.43</ p r e c i o >
</ a r t i c u l o >
< l i s t a −compra>
<a r t i c u l o s >
<p r o d u c t o i d =”c01 ” c a n t i d a d =”2” d e s c u e n t o = ”15”/>
<p r o d u c t o i d =”c02 ” c a n t i d a d =”1”/>
</ a r t i c u l o s >
< t o t a l d i v i s a = ”EUR” >21.71</ t o t a l >

87

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
</ l i s t a −compra>
</t i e n d a >

tienda.dtd:
<!ELEMENT t i e n d a ( a r t i c u l o ∗ , l i s t a −compra)>
<!ELEMENT a r t i c u l o ( nombre , c a n t i d a d , p r e c i o +)>
<!ELEMENT l i s t a −compra ( a r t i c u l o s , t o t a l )>
<!ELEMENT a r t i c u l o s ( p r o d u c t o )∗>
<!ELEMENT p r o d u c t o EMPTY>
<!ELEMENT nombre (#PCDATA)>
<!ELEMENT c a n t i d a d (#PCDATA)>
<!ELEMENT p r e c i o (#PCDATA)>
<!ELEMENT t o t a l (#PCDATA)>
<!ATTLIST a r t i c u l o i d ID #REQUIRED>
<!ATTLIST p r e c i o d i v i s a CDATA ”EUR”>
<!ATTLIST t o t a l d i v i s a CDATA ”EUR”>
<!ATTLIST p r o d u c t o i d IDREF #REQUIRED>
<!ATTLIST p r o d u c t o c a n t i d a d CDATA ”1”>
<!ATTLIST p r o d u c t o d e s c u e n t o CDATA ”0”>

3.4. Lenguajes para bases de datos de XML


3.4.1. XPath
Definido por el W3C en 1999.

Se utiliza para seleccionar determinados nodos de un documento XML.

Una expresión especifica una ruta (o rutas) donde se encuentra la información buscada.

Estos nodos son relativos a un documento XML determinado, que se obtiene mediante
la función doc, que recibe un nombre de archivo almacenado en la base de datos.

Sintaxis
Elementos sintácticos:

Expresión Significado
nodo Selecciona todos los nodos cuyo nombre es nodo
/ Selecciona nodos desde la raı́z
// Selecciona nodos
@ Selecciona atributos
* representa cualquier nodo
@* representa cualquier atributo
| permite sentencias compuestas (relacionadas por un AND)

Ejemplos:

• Consideremos un documento XML con las etiquetas: bookstore, book, title y price.

88

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• /bookstore: selecciona el elemento raı́z bookstore.
• bookstore/book: selecciona todos los elementos book hijos de bookstore.
• //book: selecciona todos los elementos book en cualquier parte del documento.
• bookstore//book: selecciona todos los elementos book descendientes de bookstore.
• //@lang: selecciona todos los atributos lang.
• /bookstore/book[last()]: selecciona el último elemento book hijo de bookstore.
• /bookstore/book[last()-1]: selecciona el penúltimo elemento book hijo de books-
tore.
• /bookstore/book[position()<3]: selecciona los dos primeros hijos de book hijo
de bookstore.
• //title[@lang]: selecciona todos los elementos title que tengan el atributo lang.
• //title[@lang=’en’]: selecciona todos los elementos title que tengan el atributo
lang y su valor sea en.
• /bookstore/book[price>35.00]: selecciona los hijos de book cuyo texto para
price sea mayor que 35.
• /bookstore/book[price>35.00]/title: selecciona title de los hijos de book cuyo
texto para price sea mayor que 35.
• /bookstore/*: todos los hijos de bookstore.
• //*: todos los elementos.
• //title[@*]: todos los elementos title que tengan un atributo (de cualquier ti-
po!!!).
• //book/title | //book/price: todos los tı́tulos y precios de cualquier elemento
book.
• //title | //price: todos los tı́tulos y precios.
• /bookstore/book/title |//price: todos los tı́tulos de cualquier elemento book
y todos los precios.

Ejemplos
Tomamos como referencia el siguiente documento XML:
<?xml v e r s i o n =”1.0” e n c o d i n g=”UTF−8”?>

<b o o k s t o r e >

<book c a t e g o r y =”COOKING”>
< t i t l e l a n g=”en”>Everyday I t a l i a n </ t i t l e >
<author>Giada De L a u r e n t i i s </author>
<year >2005</ year>
<p r i c e >30.00</ p r i c e >
</book>

89

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
<book c a t e g o r y =”CHILDREN”>
< t i t l e l a n g=”en”>Harry P o t t e r </ t i t l e >
<author>J K. Rowling </author>
<year >2005</ year>
<p r i c e >29.99</ p r i c e >
</book>

<book c a t e g o r y =”WEB”>
< t i t l e l a n g=”en”>XQuery Kick S t a r t </ t i t l e >
<author>James McGovern</author>
<author>Per Bothner </author>
<author>Kurt Cagle </author>
<author>James Linn </author>
<author>Vaidyanathan Nagarajan </author>
<year >2003</ year>
<p r i c e >49.99</ p r i c e >
</book>

<book c a t e g o r y =”WEB”>
< t i t l e l a n g=”en”>L e a r n i n g XML</ t i t l e >
<author>E r i k T . Ray</author>
<year >2003</ year>
<p r i c e >39.95</ p r i c e >
</book>

</b o o k s t o r e >

Ejemplos:

doc(/db/prueba")/bookstore: devuelve el elemento raı́z del documento, que tiene


como etiqueta <bookstore>.

doc(/db/prueba")/bookstore/book/title: devuelve la lista de elementos <title>


que sean hijos de <book>, que a su vez sean hijos de <bookstore>.

doc(/db/prueba")/bookstore/*/title: devuelve la lista de elementos <title> que


sean hijos de hijos de <bookstore> (nietos).

doc(/db/prueba")/bookstore//title: devuelve la lista de elementos <title> que


sean descendientes de <bookstore>.

doc(/db/prueba")/bookstore/book[3]/price: muestra el contenido de <price> pa-


ra <book[3]>.

3.4.2. XQuery
Estándar del W3C que se apoya en XPath para realizar consultas más complejas en
bases de datos XML.

Es (o pretende ser) para XML lo que es SQL para bases datos relacionales.

Se construye sobre expresiones del lenguaje XPath.

Utiliza expresiones FLWOR (For, Let, Where, Order by, Return):

90

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• For: selecciona una secuencia de nodos.
• Let: enlaza los elementos de esta secuencia a una variable.
• Where: filtra los elementos de la secuencia.
• Order by: ordena los elementos de la secuencia.
• Return: construye la respuesta.

Ejemplo:
f o r $x i n doc ( ” books . xml ” ) / b o o k s t o r e / book
where $x / p r i c e >30
o r d e r by $x / t i t l e
r e t u r n $x / t i t l e

• doc("books.xml")/bookstore/book: contiene un conjunto de etiquetas.


• where $x/price>30: permite obtener un subconjunto del anterior.
• x: representa cada uno de los elementos de este conjunto. En este caso de etiquetas
book.
• x/title: representa cada una de las etiquetas hijas de x.

Para nuestro fichero anterior devolverá:


< t i t l e l a n g=”en”>XQuery Kick S t a r t </ t i t l e >
< t i t l e l a n g=”en”>L e a r n i n g XML</ t i t l e >

Expresiones FLWOR
for:

• Los nombres de variables comienzan por el sı́mbolo $.


• En una cláusula de la forma for $x in seq la variable $x toma cada uno de los
valores de la secuencia seq.
• Podemos acceder a los elementos contenidos en una variable mediante expresiones
XPath.
• Ejemplos:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n $b / t i t u l o

f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n $b / a u t o r e s / a u t o r

let: sirve para introducir definiciones auxiliares y hacer más legible el código. Ejemplo:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
l e t $ t i t u l o := $b / t i t u l o
return $ t i t u l o

91

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
order by: ordena los resultados. Ejemplo:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
l e t $ t i t u l o := $b / t i t u l o
o r d e r by $ t i t u l o
return $ t i t u l o

f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
l e t $ t i t u l o := $b / t i t u l o d e s c e n d i n g
o r d e r by $ t i t u l o
return $ t i t u l o

where: filtra los resultados. Ejemplo:


f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
l e t $ t i t u l o := $b / t i t u l o
where $b / p r e c i o <= 50
o r d e r by $ t i t u l o
return $ t i t u l o

Cuantificadores some y every


Determinan si una condición se cumple en alguno o todos los elementos de una secuencia
(permiten condiciones complejas).

Sintaxis:

• some $var in secuencia satisfies condición: algún valor de $var satisface


la condición.
• every $var in secuencia satisfies condición: todos los valores de $var sa-
tisfacen la condición.

Ejemplo:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
where some $x i n $b // a u t o r
s a t i s f i e s $x / @id = ” a01 ”
r e t u r n $b

• $b: es un elemento.
• $b//autor: es un conjunto o secuencia.

Constructoras de elementos
Es posible integrar los resultados de una consulta en otros elementos XML.

Las expresiones XQuery contenidas dentro de un elemento han de estar delimitadas


por llaves.

Ejemplo:

92

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n <r e s u l t a d o >
{ $b / t i t u l o }
{ $b / p r e c i o }
</ r e s u l t a d o >

Acceso al contenido de un elemento


Supongamos que queremos devolver el tı́tulo y el precio contenido dentro de las eti-
quetas <t> y <p>, respectivamente. Ejemplo:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n <r e s u l t a d o >
<t >{$b / t i t u l o }</t>
<p>{$b / p r e c i o }</p>
</ r e s u l t a d o >

La función data() permite acceder al contenido de un elemento. También funciona con


secuencias de elementos. Ejemplo:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n <r e s u l t a d o >
<t >{data ( $b / t i t u l o )}</ t>
<p>{data ( $b / p r e c i o )}</p>
</ r e s u l t a d o >

Otra posibilidad es hacer referencia al contenido de un elemento mediante text().


Ejemplo:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n <r e s u l t a d o >
<t >{$b / t i t u l o / t e x t ()} </ t>
<p>{$b / p r e c i o / t e x t ()} </p>
</ r e s u l t a d o >

Más ejemplos:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
l e t $numautores := count ( $b / a u t o r e s / a u t o r )
r e t u r n <r e s u l t a d o >
<t >{data ( $b / t i t u l o )}</ t>
<p>{data ( $b / p r e c i o )}</p>
<na>{$numautores}</na>
</ r e s u l t a d o >

f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
l e t $numautores := count ( $b / a u t o r e s / a u t o r )
r e t u r n <r e s u l t a d o numautores=”{$numautores}”>
<t >{data ( $b / t i t u l o )}</ t>
<p>{data ( $b / p r e c i o )}</p>
</ r e s u l t a d o >

93

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
l e t $numautores := count ( $b / a u t o r e s / a u t o r )
where $numautores = 1
r e t u r n <r e s u l t a d o >
<t >{data ( $b / t i t u l o )}</ t>
<p>{data ( $b / p r e c i o )}</p>
</ r e s u l t a d o >

Restringimos la búsqueda a aquellos casos en los que solo haya un autor.

Consultas anidadas
Es posible anidar expresiones FLWOR en los resultados de una consulta.

Ejemplos:
f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n <r e s u l t a d o >
<t >{data ( $b / t i t u l o )}</ t>
<p>{data ( $b / p r e c i o )}</p>
{ f o r $a i n $b // a u t o r
r e t u r n $a
}
</ r e s u l t a d o >

En este ejemplo relacionamos dos archivos XML distintos: libros.xml y autores.xml :


f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n <r e s u l t a d o >
<t >{data ( $b / t i t u l o )}</ t>
<p>{data ( $b / p r e c i o )}</p>
{ f o r $a i n $b // a u t o r
l e t $ i n f o a u t o r := doc ( ” a u t o r e s . xml ” ) / / a u t o r [ @id = $a / @id ]
return $infoautor
}
</ r e s u l t a d o >

f o r $b i n doc ( ” l i b r o s . xml ” ) / l i b r o s / l i b r o
r e t u r n <r e s u l t a d o >
<t >{data ( $b / t i t u l o )}</ t>
<p>{data ( $b / p r e c i o )}</p>
{ f o r $a i n $b // a u t o r
l e t $ i n f o a u t o r := doc ( ” a u t o r e s . xml ” ) / / a u t o r [ @id = $a / @id ]
r e t u r n <a u t o r >{data ( $ i n f o a u t o r / nombre ) }
{ data ( $ i n f o a u t o r / a p e l l i d o s ) }
</a u t o r >
}
</ r e s u l t a d o >

XQuery y HTML
Es posible construir una página HTML con el resultado de una consulta XQuery. Ejemplo:
l e t $my−doc := doc ( ” books . xml ” )
return
<html>

94

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Función Descripción
count(seq) Devuelve el número de elementos de la secuencia dada
sum(seq) Devuelve la suma de los elementos de la secuencia dada
avg(seq) Devuelve la media de los elementos de seq
max(seq), min(seq) Devuelve el máximo/mı́nimo de los elementos de seq
distinct-values(seq) Elimina los duplicados de seq
contains(cadena,subcadena) Busca una subcadena de caracteres en una cadena
except Permite ”quitar” código XML

Cuadro 3.1: Algunas funciones de agregación sobre conjuntos secuencias

<head>
< t i t l e >C u rr e n t Rates </ t i t l e >
</head>
<body>
<ul >
{
f o r $x i n $my−doc / b o o k s t o r e / book
o r d e r by $x / t i t l e
r e t u r n < l i >{data ( $x / t i t l e ) } . Category : { data ( $x / @category )}</ l i >
}</ ul>
</body>
</html>

3.4.3. SGBD eXist


Gestor de bases de datos XML que utiliza XQuery como lenguaje de acceso a los datos.

Se distribuye bajo la licencia GNU LGPL.

Puede obtenerse en la dirección: http://exist-db.org/

Durante la instalación se solicita el nombre de usuario y contraseña del administrador.

En los laboratorios:

• Nombre de usuario: admin


• Contraseña: ninguna

Tras arrancar el gestor de bases de datos, abrir un navegador web e introducir la


dirección: http://localhost:8080/exist

En la esquina superior izquierda seleccionar el botón Not logged in

Introducir login y contraseña de administrador.

En la ventana principal seleccionar el icono Collections.

95

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 3.1: Ventana principal de eXist-db

Figura 3.2: Ventana de administración de eXist-db

96

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 3.3: Adición de documentos en eXist-db

Figura 3.4: Ventana de edición en eXist-db

Los documentos XML se almacenan en carpetas que pueden ser anidadas. Similar a
un sistema de archivos.

3.4.4. xQuery tester


Es una herramienta on-line que permite realizar consultas con XQuery. Es estremada-
mente sencilla.

Está disponible en http://www.xpathtester.com/xquery

Mini-tutorial en: https://www.youtube.com/watch?v=Gs_WGF-m958

Nota: durante el examen de la asignatura, al no tener conexión a Internet, no estará dispo-


nible.

97

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 3.5: Ventana de xQuery tester

98

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 4

MongoDB

4.1. Conceptos básicos


4.1.1. ¿Qué es MongoDB?
MongoDB es un sistema de base de datos NoSQL multiplataforma de licencia libre
(código abierto).

MongoDB es un sistema de base de datos orientada a documentos: en lugar de guardar


los datos en tablas, guarda los datos en documentos.

Estos documentos son almacenados en BSON, que es una representación binaria de


JSON:

• Para el intercambio de datos para almacenamiento y transferencia de documentos


en MongoDB usamos el formato BSON, (Binary JavaScript Object Notation). Se
trata de una representación binaria de estructuras de datos y mapas, diseñada
para ser más ligero y eficiente que JSON, (JavaScript Object Notation).

Está orientado a documentos de esquema libre: cada registro puede tener un esquema
de datos distinto. Los atributos no tienen que ser iguales en diferentes registros.

MongoDB está pensado para mejorar la escalabilidad horizontal :

• Es una propiedad deseable de un sistema que indica la capacidad para adaptarse


sin perder calidad con el crecimiento del tamaño de la base de datos.

Cada registro o conjunto de datos se denomina documento, que pueden ser agrupados
en colecciones, (equivalente a las tablas de las bases de datos relacionales pero sin estar
sometidos a un esquema fijo).

99

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Servidor
Base de datos
Colección
Documentos
Campos
Estructura de la información en MongoDB.

Principales herramientas para trabajar con MongoDB:

1. Mongod : Servidor de bases de datos de MongoDB.


2. Mongo: Cliente para la interacción con la base de datos MongoDB.

Caracterı́sticas:

• Replicación:
◦ MongoDB, es más flexible que las bases de datos relacionales, y por ello menos
restrictivo, lo que puede presentar en ocasiones problemas de volatilidad.
◦ MongoDB manda los documentos escritos a un servidor maestro, que sincro-
nizado a otro u otros servidores mandará esta misma información replicada,
a estos ”esclavos”.
• Indexación: Cualquier campo en un documento de MongoDB puede ser indexa-
do. Es posible hacer ı́ndices secundarios. El concepto de ı́ndices en MongoDB es
similar al de datos relacionales.
• Escalabilidad horizontal:
◦ Capacidad de trabajar con varias máquinas de manera distribuida, almace-
nando en cada uno de los nodos cierta información que de una forma u otra
debe estar comunicada con el resto de nodos que forman nuestro sistema.
◦ Esto dota de mayor flexibilidad al sistema, ya que facilita la agregación de
equipos en función de las necesidades.
• Sharding:
◦ MongoDB utiliza el Sharding como método para dividir los datos a lo largo
de los múltiples servidores de nuestra solución.
◦ Las bases de datos relacionales también hacen tareas similares a ésta, si bien
de forma diferente.
◦ Tal vez el rasgo más destacable en MongoDB porque realiza estas tareas de
manera automática.
• Balanceo:
◦ El balanceador es un proceso de MongoDB para equilibrar los datos en nuestro
sistema.
◦ Mueve porciones de datos de un shard a otro, de manera automática.

100

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Algunos inconvenientes:

• No implementa las propiedades ACID (atomicidad, consistencia, aislamiento y


durabilidad): El no implementar las propiedades ACID genera que la base de
datos no asegure la durabilidad, la integridad, la consistencia y el aislamiento
requeridos obligatoriamente en las transacciones (”superables” en el futuro).
• Tiene problemas de rendimiento cuando el volumen de datos supera los 100GB.
• Otros problemas ”menores” achacables a versiones concretas (”superables” en el
futuro).

Relación entre los conceptos de las bases de datos relacionales y MongoDB:

Bases de datos relacionales MongoDB


Base de datos Base de datos
Tabla Colección
Tupla o fila Documento
Columna Campo

4.1.2. Documentos
Los documentos son la unidad básica de organización de la información en MongoDB,
y desempeñan el papel equivalente a una fila en las bases de datos relacionales.

Un documento es un conjunto ordenado de claves que tienen asociados valores, y que se


corresponden con algunas estructuras de datos tı́picas de los lenguajes de programación
tales como tablas hash o diccionarios. En general los documentos contendrán múltiples
pares clave-valor como: {’’Nombre’’:’’Juan’’,’’Pais’’:’’Espa~ na’’}

Caracterı́sticas:

• Las claves son cadenas, permitiéndose cualquier carácter.


• Excepciones:
◦ La clave no pueden contener el carácter nulo \0.
◦ El punto (.) y el ($) están prohibidos.
• MongoDB es sensitivo tanto a las mayúsculas/minúsculas como a los tipos de
datos. Ası́ por ejemplo los siguientes documentos se consideran distintos:
{’’Edad’’:3} ,{’’Edad’’: ’’3’’},{’’Edad’’:3},{’’edad’’:3}
• Los documentos no pueden tener claves duplicadas. Ası́ por ejemplo el siguiente
documento es incorrecto: {’’edad’’:3,’’edad’’:56}
• Los pares clave-valor están ordenados en los documentos. Por ejemplo el docu-
mento {”x”:3,”y”:5} no es lo mismo que {”y”:5,”x”:3}.

101

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Los pares clave-valor están ordenados en los documentos. Por ejemplo el docu-
mento {”x”:3,”y”:5} no es lo mismo que {”y”:5,”x”:3}.
• Los valores de un documento pueden ser de diferentes tipos.

4.1.3. Tipos de datos


• Principales tipos de datos soportados por los documentos en MongoDB:
◦ Nulo: Representa el valor nulo o bien un campo que no existe. Por ejemplo
{”x”:null}. Nota: El uso de este valor va en contra de las ideas fundamentales
de bases de datos semiestructuradas. Sólo utilizarlo en casos excepcionales.
◦ Booleanos: Representa el tipo booleano, el cual puede tomar los valores de
true o false. Por ejemplo {”x”:true}
◦ Números: Distingue entre números reales como por ejemplo {”x”:3.14} y
números enteros como por ejemplo {”x”:45}
◦ Cadenas: Cualquier cadena de caracteres como por ejemplo {”x”:”Ejemplo”}
◦ Fechas: Almacena la fecha en milisegundos. Por ejemplo: {”x”:new Date()}
◦ Expresiones regulares: Se pueden usar expresiones regulares para realizar con-
sultas.
◦ Arrays: Se representa como un conjunto o lista de valores. Por ejemplo {”x”:[”a”,”b”,”c”]}
◦ Documentos embebidos: Los documentos pueden contener documentos em-
bebidos como valores de un documento padre. Por ejemplo {”x”:{”y”:45}}
◦ Identificadores de objetos: Es un identificador de 12 bytes para un documento.
Por ejemplo {”x”: OjectId()}
◦ Datos binarios: Es una cadena de bytes arbitraria que no puede ser mani-
pulada directamente desde el Shell y que sirve para representar cadenas de
caracteres no UTF8.
◦ Código Javascript: Los documentos y las consultas pueden contener código
JavaScript. Por ejemplo {”x”: function () { ...}}
◦ Fechas:
 Para crear un objeto de tipo fecha se usa el comando new Date(). Sin
embargo si se llama sin new(solo Date()) entonces se retorna una cadena
que representa la fecha. Y por tanto se trata de diferentes tipos de datos.
 Las fechas en el Shell son mostradas usando la configuración local de la
zona horaria, sin embargo la base de datos las almacena como un valor
en milisegundos sin referencia a la zona horaria.
◦ Sobre los arrays:
 Pueden ser usados tanto en operaciones en las que el orden es importante
tales como listas, pilas o colas como en operaciones en las que el orden
no es importante tales como conjuntos.

102

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
 Los arrays pueden contener diferentes tipos de valores como por ejemplo
{”Cosas”:[”edad”,45]}(de hecho soporta cualquiera de los tipos de valores
soportados para los documentos, pudiéndose crear arrays anidados).
◦ Una propiedad importante en MongoDB es que es reconoce la estructura
de los arrays y permite navegar por el interior de los arrays para realizar
operaciones sobre sus contenidos como consultas o crear ı́ndices sobre sus
contenidos.
◦ En el ejemplo anterior se podrı́a crear una consulta para recuperar todos
aquellos documentos donde 3.14 es un elemento del array ”Cosas”, y si por
ejemplo esta fuera una consulta habitual entonces incluso se podrı́a crear un
ı́ndice sobre la clave ”Cosas” y mejorar el rendimiento de la consulta.
◦ Ası́ mismo MongoDB permite realizar actualizaciones que modifican los con-
tenidos de los arrays tales como cambiar un valor del array por otro.
• Documentos embebidos:
◦ Los documentos pueden ser usados como valores de una clave, y en este caso
se denominan ”documentos embebidos”. Se suelen usar para organizar los
datos de una manera lo más natural posible.
◦ Por ejemplo si se tiene un documento que representa a una persona y se quiere
almacenar su dirección podrı́a crearse anidando un documento ”dirección” al
documento asociado a una persona como por ejemplo:
{
” nombre ” : ” Juan ” ,
” d i r e c c i ó n ” : {
” c a l l e ” : ” Mayor , 3 ” ,
” c i u d a d ” : ” Madrid ” ,
” P a i s ” : ” España ”
}}

◦ MongoDB es capaz de navegar por la estructura de los documentos embe-


bidos y realizar operaciones con sus valores como por ejemplo crear ı́ndices,
consultas o actualizaciones.
• Identificador de objetos:
◦ Cada documento tiene que tener un clave denominada ” id”.
◦ El valor de esta clave puede ser de cualquier tipo pero por defecto será de
tipo ObjectId.
◦ En una colección cada documento debe tener un valor único y no repetido
para la clave ” id”, lo que asegura que cada documento en la colección pueda
ser identificado de manera única.
◦ Ası́ por ejemplo dos colecciones podrı́an tener un documento con ” id” con el
valor 123, pero en una misma colección no podrı́a haber dos documentos con
valor de ” id” de 123.

103

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
◦ El tipo ObjectID es el tipo por defecto para los valores asociados a la clave
” id”. Es un tipo de datos diseñado para ser usado en ambientes distribuidos
de manera que permita disponer de valores que sean únicos globalmente.
◦ Cada valor usa 12 bytes lo que permite representar una cadena de 24 dı́gitos
hexadecimales(2 digitos por cada byte). Si se crean múltiples valores del tipo
ObjectID sucesivamente sólo cambian unos pocos dı́gitos del final y una pareja
de dı́gitos de la mitad. Esto se debe a la forma en la que se crean los valores
del tipo ObjectIDs.
◦ Observaciones:
 Los primeros 4 bytes son un marca de tiempo en segundos que combinados
con los siguientes 4 bytes proporciona unicidad a nivel de segundo y que
identifican de manera implı́cita cuando el documento fue creado.
 Por otro lado, a causa de que la marca de tiempo aparece en primer lugar,
entonces los ObjectIDs se ordenan obligatoriamente en orden de inserción
lo que hace que la indexación sobre ObjectIDs sea eficiente.
 Los siguientes 3 bytes son un identificador único de la máquina que lo
genera, lo que garantiza que diferentes máquinas no generan colisiones.
 Para conseguir unicidad entre diferentes procesos que generan ObjectIDs
concurrentemente en una misma máquina se usan los siguientes 2 bytes
que son tomados del identificador del proceso que genera un ObjectID.
◦ Cuando un documento se va a insertar si no tiene un valor para la clave ” id”
entonces es generado automáticamente por MongoDB.

4.1.4. Colecciones
Una colección es un grupo de documentos, y desempeña el papel análogo a las tablas
en las bases de datos relacionales.

Las colecciones tienen esquemas dinámicos lo que significa que dentro de una colección
puede haber cualquier número de documentos con diferentes estructuras.

Por ejemplo en una misma colección podrı́an estar los siguientes documentos diferentes:
{”edad”:34},{”x”:”casa”} que tienen diferentes claves y diferentes tipos de valores.

Dado que cualquier documento se puede poner en cualquier colección y dado que no
es necesario disponer de esquemas distintos para los diferentes tipos de documentos,
entonces surge la pregunta de por qué se necesita usar más de una colección y tener
que separar los documentos mediante colecciones separadas:

• Cuando se crean ı́ndices se impone cierta estructura a los documentos (espe-


cialmente en los ı́ndices únicos). Estos ı́ndices están definidos por colección de
forma que poniendo documentos de un solo tipo en la misma colección entonces
se podrán indexar las colecciones de una forma más eficiente.

104

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• En general, por eficiencia, es razonable crear un esquema y agrupar los tipos
relacionados de documentos juntos aunque MongoDB no lo imponga como obli-
gatorio.

Una colección se identifica por su nombre, el cual es una cadena con las siguientes
restricciones:

• La cadena vacı́a no es un nombre válido para una colección.


• Los nombres de las colecciones no pueden contener el carácter nulo \0 pues este
sı́mbolo se usa para indicar el fin del nombre de una colección.
• No se debe crear ninguna colección que empiece con ”system” dado que es un pre-
fijjo reservado para las colecciones internas. Por ejemplo la colección system.users
contiene los usuarios de la base de datos, la colección system.namespaces contiene
información acerca de todas las colecciones de la base de datos.
• Las colecciones creadas por los usuarios no deben contener el carácter reservado
$ en su nombre.

Una convención para organizar las colecciones consiste en definir subcolecciones usando
espacios de nombres separados por el carácter ”.”.

Por ejemplo una aplicación que contuviese un blog podrı́a tener una colección denomi-
nada blog.posts y otra colección denominada blog.autores con un propósito organizativo
y que sin embargo ni exista la colección blog y en caso de existir no exista una relación
entre la colección padre blog y las subcolecciones.

Las colecciones se agrupan en bases de datos, de manera que una única instancia
de MongoDB puede gestionar varias bases de datos cada una agrupando cero o más
colecciones.

Bases de datos. Algunas observaciones:

• Cada base de datos tiene sus propios permisos y se almacena en ficheros del disco
separados.
• Una buena regla general consiste en almacenar todos los datos de una aplicación
en la misma base de datos.
• Las bases de datos separadas son útiles cuando se almacenan datos para aplica-
ciones o usuarios diferentes que usan el mismo servidor de MongoDB.

Las bases de datos se identifican mediante nombres que son cadenas con las siguientes
restricciones:

• La cadena vacı́a no es un nombre válido para una base de datos.

105

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• El nombre de una base de datos no puede contener ninguno de los siguientes
caracteres:
\,/,.,’’,*,<,>,:,|,?,$,espacio o \0(valor nulo)
• Los nombres de las bases de datos son sensitivos a mayúsculas y minúsculas
incluso sobre sistemas de archivos que no lo sean. Una regla práctica es usar
siempre nombres en minúscula.
• Los nombres están limitados a un máximo de 64 bytes.

Existen nombres que no pueden usarse para las bases de datos por estar reservados:

• admin. Es el nombre de la base de datos ”root” en términos de autenticación. Si


un usuario es añadido a esta base de datos entonces el usuario hereda los permisos
para todas las bases de datos. Existen determinados comandos que solo pueden
ser ejecutados desde esta base de datos tales como listar todas las bases de datos
o apagar el servidor.
• local. Esta base de datos nunca será replicada y sirve para almacenar cualquier
colección que deberı́a ser local a un servidor.
• config. Cuando en MongoDB se usa una configuración con sharding se usa esta
base de datos para almacenar información acerca de los fragmentos o shards que
se crean.

Mediante la concatenación del nombre de una base de datos con una colección de la
base de datos se consigue un cualificación entera del nombre de la colección denominado
espacio de nombres.

Por ejemplo si se usa la colección blog.posts en la base de datos cms, el espacio de


nombres de esa colección serı́a cms.blog.posts. Los espacios de nombres están limitados
a 121 bytes de longitud, aunque en la práctica es mejor que sean menores de 100 bytes.

Observaciones:

• Cuando se consulta una colección con el comando db.Nombre colección siempre


funciona salvo que el nombre de la colección sea un nombre reservado o es un
nombre inválido de propiedad de JavaScript.
• Por ejemplo si se intenta acceder a una colección denominada ”version” no po-
dremos usar ”db.version” dado que se trata de un método sobre db que retorna
la versión del servidor de MongoDB que se está usando:
> db . v e r s i o n
function (){
return this . serverBuidInfo ( ) . version ;
}

• Ası́ para acceder a la colección de nombre ”version” se usa la función getCollection:

106

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
> db . g e t C o l l e c t i o n ( ” v e r s i o n ” )
test . version
}

4.2. Inserción y borrado


Creación de bases de datos y colecciones:

• use mi bbdd: crea o abre mi bbdd.


• db.createCollection(’’mi colección’’): crea mi colección.

Para insertar un documento en una colección se usa el método insert. Ejemplo:


db . prueba . i n s e r t ( { ” T i t u l o ” : ” El Q u i j o t e ” } )

Esta acción añadirá al documento el campos id en caso de no existir en el documento,


y almacenará el mismo en MongoDB.

Cuando es necesario insertar un conjunto de documentos, se puede pasar como paráme-


tro un array con el conjunto de documentos que deben ser insertados. Ejemplo:
db . prueba . i n s e r t ( [ { ” T i t u l o ” : ” Otro ” } , { ” T i t u l o ” : ” Otro mas ” } ] )

Se pueden insertar mediante un array múltiples documentos siempre que se vayan


almacenar en una única colección, en caso de varias colecciones no es posible.

Observación: Cuando se inserta usando un array, si se produce algún fallo en algún


documento, se insertan todos los documentos anteriores al que tuvo el fallo, y los que
hay a continuación no se insertan. Este comportamiento se puede cambiar usando la
opción continueOnError que en caso de encontrarse un error en un documento lo
salta, y continua insertando el resto de documentos. Esta opción no está disponible
directamente en la shell, pero si en los drivers de los lenguajes de programación.

Actualmente existe un lı́mite de longitud de 48 MB para las inserciones realizadas


usando un array de documentos.

Cuando se inserta un documento MongoDB realizan una serie de operaciones con el


objetivo de evitar inconsistencias tales como:

• Se añade el campo id en caso de no tenerlo.


• Se comprueba la estructura básica. En particular se comprueba el tamaño del do-
cumento(debe ser más pequeño de 16 Mb). Para saber el tamaño de un documento
se puede usar el comando Object.bsonsize(doc).
• Existencia de caracteres no válidos.

107

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
El método remove elimina todos los documentos de una colección, pero no elimina la
colección ni la metainformación acerca de la colección.

El método permite opcionalmente tomar una condición de búsqueda, de forma que


eliminará solo aquellos documentos que encajen con la condición dada.

Por ejemplo si se quisiera eliminar todos los documentos de la colección correo.lista


dónde el valor para el campo salida es cierto entonces se usarı́a el siguiente comando:
db . c o r r e o . l i s t a . remove ( { ” s a l i d a ” : t r u e } )

Una vez que se ha realizado el borrado no se puede dar revertir y se pierden todos los
documentos borrados.

A veces si se van a borrar todos los documentos es más rápido eliminar toda la colección
en vez los documentos. Para ello se usa el método drop:
db . prueba . drop ( )

4.3. Consultas
El método find se utiliza para realizar consultas en MongoDB, las cuales retornan un
subconjunto de documentos de una colección (desde ningún documento hasta todos los
documentos de la colección).

El primer argumento especifica las condiciones que deben cumplir los documentos que
se quieren recuperar.

Una condición de búsqueda vacı́a ({}) encaja con todos los documentos de la colección.

En caso de no especificar ninguna condición entonces se toma por defecto la condición


vacı́a({}). Por ejemplo la consulta db.c.find() recupera todos los documentos de la
colección c.

Cuando se añaden pares clave/valor a las condiciones de búsqueda se restringe la


búsqueda. Esto funciona directamente para la mayorı́a de los tipos: números coinciden
con números, booleanos con booleanos, cadenas con cadenas...

Si se quiere consulta un tipo simple entonces basta especificar el valor que se está
buscando.

Por ejemplo si se quiere encontrar todos los documentos dónde el valor de la edad es
27, entonces se añade como condición de búsqueda el par clave/valor {”edad”:27} a la
condición de búsqueda:
db . u s e r s . f i n d ( { ” edad ” : 2 7 } )

108

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Cuando se quieren usar múltiples condiciones juntas se añaden los pares clave/valor que
sean necesarias, las cuales serán interpretadas como ’’Condición1 AND Condición2
AND...AND Condición N’’.

Por ejemplo si se quieren recuperar todos los usuarios con 27 años y que se llamen
”Isabel” se realizarı́a la siguiente consulta:
db . u s e r s . f i n d ( { ” nombre ” : ” I s a b e l ” , ” edad ” : 2 7 } )

A veces cuando se recupera un documento no es necesario recuperar todos los campos


del documento, para ello se puede pasar un segundo argumento al método find para
especificar que campos se quieren recuperar.

Por ejemplo si se tiene una colección de usuarios y solo se quiere recuperar el nombre
del usuario y el email entonces se podrı́a realizar la siguiente consulta:
db . u s e r s . f i n d ( { ” nombre ” : 1 , ” e m a i l ” : 1 } )

Siempre que se recupera un documento, por defecto se recupera el campo id.

También es posible especificar explı́citamente que pares clave/valor no se quieren re-


cuperar en la consulta.

Por ejemplo se puede tener una colección que tenga documentos con diferentes claves
pero en todos ellos no se quiere recuperar la clave ”teléfono”, entonces se podrı́a realizar
la siguiente consulta:
db . u s e r s . f i n d ( { ” t e l e f o n o ” : 0 } )

También podrı́a usarse para evitar recuperar la clave id:


db . u s e r s . f i n d ( { ” nombre ” : 1 , ” i d ” : 0 } )

Existe un método similar a find() que es findOne() que permite recuperar un único
documento cumpliendo las condiciones especificadas.

Operadores condicionales:

• Los operadores ”$lt”, ”$lte”,”$gt” y ”$gte” corresponden a los operadores de com-


paración <,<=,> y >= respectivamente, y pueden combinarse para buscar rangos
de valores.
• Por ejemplo si se quiere buscar los usuarios que tienen una edad entre 18 y 30
años se puede hacer de la siguiente manera:
db . u s u a r i o s . f i n d ( { ” edad ” : {” $ g t e ” : 1 8 , ” $ l t e ” : 3 0 } } )

109

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Este tipo de consultas son muy útiles para realizar consultas sobre las fechas. Por
ejemplo para encontrar las personas que se registraron antes del 1 de Enero de
2007 se puede hacer de la siguiente manera:
> f e c h a = new Date ( ” 0 1 / 0 1 / 2 0 0 7 ” )
> db . u s u a r i o s . f i n d ( { ” r e g i s t r a d o s ” : {” $ l t ” : f e c h a } } )

• Una coincidencia exacta sobre la fecha es menos útil puesto que las fechas son
sólo almacenadas con precisión de milisegundos, y con frecuencia lo que se busca
es comparar un dı́a, semana o mes entero haciendo necesario una consulta sobre
rangos.
• También puede ser útil consultar los documentos en los que el valor de una clave
no es igual a cierto valor, para lo cual se usa el operador ”$ne” que representa ”no
igual”. Por ejemplo si se quieren recuperar los usuarios que no tienen por nombre
”Pablo”, se podrı́an consultar de la siguiente manera:
db . u s e r s . f i n d ( { ” username ” : {” $ne ” : ” j o e ” } } )

• Observar que el operador ”$ne” puede ser usado con cualquier tipo.

Consultas de tipo OR:

• Existen dos posibilidades para realizar una consulta ”OR”:


◦ El operador ”$in” que puede ser usado para consultar sobre una variedad de
valores para una clave dada.
◦ El operador ”$or” que puede ser usado para consultar sobre un conjunto de
valores dados sobre múltiples claves dadas.
• Si se tiene más de un posible valor a encajar sobre una clave dada, es mejor usar
un ”$in” sobre un array con los valores.
• Por ejemplo si se quieren recuperar los documentos de personas que tienen un dni
autorizado(sean 725,542 y 390 los dnis autorizados) se podrı́a hacer de la siguiente
manera:
db . a u t o r i z a d o s . f i n d ( { ” d n i ” : {” $ i n ” : [ 7 2 5 , 5 4 2 , 3 9 0 ] } } )

• ”$in” es más flexible y permite especificar criterios sobre diferentes tipos y valores.
• Por ejemplo supóngase una base de datos donde se pueden usar tanto nombres
de usuario como identificadores numéricos de usuario, entonces se podrı́a realizar
una consulta de la siguiente manera:
db . u s u a r i o s . f i n d ( { ” u s e r i d ” : {” $ i n ” : [ 1 2 3 4 5 , ” Pablo ” ] } )

• Si el operador ”$in” aparece con un array con un único valor, entonces se comporta
intentado hacer coincidir el valor. Por ejemplo {”dni” : {$in : [725]}} es equivalente
a {”dni” : 725}.

110

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• El operador opuesto a ”$in” es ”$nin”, el cual retorna documentos que no coinci-
den con ninguno de los criterios dados en el array de valores.
• Por ejemplo si se quieren recuperar todos los documentos de personas que tienen
un dni que no está autorizado se podrı́a hacer de la siguiente manera:
db . a u t o r i z a d o s . f i n d ( { ” d n i ” : {” $ n i n ” : [ 7 2 5 , 5 4 2 , 3 9 0 ] } } )

• ”$or” toma un array con posibles criterios de coincidencia. Por ejemplo si se


quieren recuperar los documentos de personas que tiene 30 años de edad ó que no
son fumadores se podrı́a hacer de la siguiente manera:
db . p e r s o n a s . f i n d ( { ” $ o r ” : [ { ” edad ” : 3 0 } , { ” fumador ” : f a l s e } ] } )

• ”$or” también puede contener otros condicionales. Por ejemplo si se quieren re-
cuperar los documentos de personas que tienen 30, 34 y 35 años o que no son
fumadores se podrı́a hacer de la siguiente manera:
db . p e r s o n a s . f i n d ( { ” $ o r ” : [ { ” edad ” : { ” $ i n ” : [ 3 0 , 3 4 , 3 5 ] } } , { ” fumador ” : f a l s e } ] } )

• El operador ”$or” siempre funciona pero siempre que se pueda es mejor usar ”$in”
dado que es más eficiente.

El ”$not”:

• El operador ”$not” es un metacondicional, es decir que puede ser aplicado sobre


otros criterios.
• ”$not”puede ser útil en conjunción con expresiones regulares para encontrar todos
los documentos que no encajan con un determinado patrón.

Observar que no existe el operador ”$eq” pero se puede simular con el operador ”$in”
con un único valor.

Observaciones:

• Se pueden expresar múltiples condiciones sobre una clave dada. Por ejemplo si se
quieren encontrar todos los usuarios que tienen una edad entre 20 y 30, se podrı́an
usar los operadores $gt y $lt sobre la clave ”edad” de la siguiente manera:
db . u s u a r i o s . f i n d ( { ” edad ” : {” $ l t ” : 3 0 , ” $ g t ” : 2 0 } } )

Consultas sobre arrays:

• Las consultas sobre elementos de un array están diseñada para comportarse de la


misma forma que sobre valores escalares.
• Por ejemplo si se tiene un array que representa una lista de frutas como por
ejemplo:
db . comida . i n s e r t ( { ” f r u t a ” : [ ” manzana ” , ” p l a t a n o ” , ” m e l o c o t o n ” ] } )

111

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
entonces la consulta:
db . comida . f i n d ( { ” f r u t a ” : ” p l a t a n o ” } )

entonces tendrá éxito sobre el documento que se ha insertado.


• La consulta es equivalente a si se tuviera un documento de la forma:
{” f r u t a ” : ”manzana ” , ” f r u t a ” : ” p l a t a n o ” , ” f r u t a ” : ” m e l o c o t o n ”}

El operador $all:

• Cuando se quieren encajar todos los valores de un array se puede usar el operador
$all.
• Por ejemplo si se tuviera la siguiente colección de documentos:
> db . comida . i n s e r t ( { ” i d ” : 1 , ” f r u t a ” : [ ” manzana ” , ” p l a t a n o ” , ” m e l o c o t o n ” ] } )
> db . comida . i n s e r t ( { ” i d ” : 2 , ” f r u t a ” : [ ” manzana ” , ” p e r a ” , ” n a r a n j a ” ] } )
> db . comida . i n s e r t ( { ” i d ” : 3 , ” f r u t a ” : [ ” c e r e z a ” , ” p l a t a n o ” , ”manzana ” ] } )

• Se podrı́a buscar todos los documentos que tienen a la vez ”manzana” y ”platano”
mediante una consulta de la forma:
db . comida . f i n d ( { f r u t a : { $ a l l : [ ” manzana ” , ” p l a t a n o ” ] } } )

• El orden no importa en la consulta. Ası́ por ejemplo en el segundo resultado


aparece ”platano” antes de ”manzana”.
• Cuando se usa $all con un array de un solo elemento entonces es equivalente a
no usar $all. Por ejemplo:
{ f r u t a : { $ a l l : [ ’ manzana ’ ] }

es equivalente a:
{ fruta : ’ manzana ’ }

• Se puede realizar una consulta para buscar una coincidencia exacta usando el
array entero. Sin embargo, la coincidencia exacta no encajará un documento si
alguno de los elementos no se encuentran o sobran.
• Por ejemplo, la siguiente consulta recupera el primer documento:
db . comida . f i n d ( { ” f r u t a ” : [ ” manzana ” , ” p l a t a n o ” , ” m e l o c o t o n ” ] } )

sin embargo las consultas:


db . comida . f i n d ( { ” f r u t a ” : [ ” manzana ” , ” p l a t a n o ” ] } )
db . comida . f i n d ( { ” f r u t a ” : [ ” p l a t a n o ” , ”manzana ” , ” m e l o c o t o n ” ] } )

no recuperan el primer documento.


• También es posible consultar un elemento especı́fico de un array usando la nota-
ción indexada clave.ı́ndice como por ejemplo con:

112

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
db . comida . f i n d ( { ” f r u t a . 2 ” : ” m e l o c o t o n ” } )

• Observar que los ı́ndices empiezan a contar desde cero por lo que la consulta
anterior buscarı́a que el tercer elemento del array tome el valor de ”melocoton”.

El operador $size:

• Este operador permite consultar arrays de un tamaño dado. Por ejemplo, la si-
guiente consulta:
db . comida . f i n d ( { ” f r u t a ” : {” $ s i z e ” : 3 } } )

• Un uso normal consiste en recuperar un rango de tamaños. El operador $size no


puede ser combinado con otro operador condicional pero se puede añadir un clave
”size” al documento, de manera que cada vez que se añade un elemento al array,
entonces se incrementa el valor de la clave ”size”.

El operador $slice:

• Este operador puede ser usado para retornar un subconjunto de elementos de un


clave que tiene por valor un array.
• Por ejemplo, supóngase que se tiene un documento sobre un post de un blog y
se quiere recuperar los 10 primeros comentarios entonces se podrı́a hacer de la
siguiente manera :
db . b l o g . p o s t s . findOne ( c r i t e r i o , {” c o m e n t a r i o s ” : {” $ s l i c e ” : 1 0 } } )

• Y si se quieren recuperar los 10 últimos comentarios entonces se podrı́a hacer de


la siguiente manera:
db . b l o g . p o s t s . findOne ( c r i t e r i a , {” c o m e n t a r i o s ” : {” $ s l i c e ” : −10}})

• El operador $slice también puede retornar elementos concretos de los resultados


para lo cual es necesario especificar un valor que indica desde que elemento se
empieza a recuperar y otro valor que indica cuántos se van a considerar a partir
del indicado.
• Ası́ por ejemplo, la consulta:
db . b l o g . p o s t s . findOne ( c r i t e r i a , {” c o m e n t a r i o s ” : {” $ s l i c e ” : [ 2 3 , 1 0 ] } } )

se salta los 23 primeros elementos y recupera del 24 al 33. Si hubiera menos de


33 elementos en el array entonces se recuperan tantos como sea posible.
• Observar que a menos que se indique lo contrario, todas las claves de un docu-
mento son recuperadas cuando se usa el operador $slice. Este comportamiento
es diferente con respecto a otros especificadores de clave que suprimen las claves
que no se mencionan que deban recuperarse.

113

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Por ejemplo, si se tuviera el siguiente documento:
>db . b l o g . p o s t s . findOne ( )
{
” i d : ObjectId ( . . . ) ,
” t i t u l o : ”un buen p o s t ” ,
” contenido : ” . . . ” ,
” comentarios = [
{
” nombre ” : ” j u a n ” ,
” e m a i l ” : ” juan@ejemplo . com ” ,
” contenido ” : ” estupendo ”
},
{
” nombre ” : ” i s a b e l ” ,
” e m a i l ” : ” i s a b e l @ e j e m p l o . com ” ,
” contenido ” : ” excelente ”
}
]
}
>

• Si se usa el operador $slice para conseguir el último comentario se harı́a de la


siguiente manera:
>db . b l o g . p o s t s . findOne ( { } , { ” c o m e n t a r i o s ” : {” $ s l i c e ” : −1}})
{
” i d : ObjectId ( . . . ) ,
” t i t u l o : ”un buen p o s t ” ,
” contenido : ” . . . ” ,
” comentarios = [
{
” nombre ” : ” i s a b e l ” ,
” e m a i l ” : ” i s a b e l @ e j e m p l o . com ” ,
” contenido ” : ” excelente ”
}
]
}
>

• Se puede observar que se recupera además del comentario, también la clave ”ti-
tulo” y ”contenido” aunque no se haya especificado.
• A veces se desconoce el ı́ndice del elemento que se quiere recuperar. En estos casos
se puede utilizar el operador $.
• En el ejemplo anterior si se quiere recuperar el comentario que ha realizado ”juan”
se podrı́a hacer de la siguiente manera:
db . b l o g . p o s t s . f i n d ( { ” c o m e n t a r i o s . nombre ” : ” j u a n ” } , {” c o m e n t a r i o s . $ ” : 1 } )

• La única limitación de esta técnica es que solo recupera la primera coinciden-


cia, de manera que si hubiera más comentarios de ”juan” en este post no serı́an
retornados.
Arrays y rangos:
• Los valores escalares(no arrays) en los documentos deben coincidir con cada
claúsula que aparece en la consulta. Ası́ por ejemplo en la consulta:

114

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
{” x” : {” $ g t ” : 1 0 , ” $ l t ” : 20}}

deberı́a cumplirse a la vez que x es más grande que 10 y más pequeño que 20.
• Sin embargo, si en un documento x fuera un array entonces el documento encajarı́a
si existe un elemento de x que encaja con cada uno de los criterios que aparecen en
la consulta pero cada criterio puede encajar con un elemento diferente del array.
Ası́ por ejemplo si se tuvieran los siguientes documentos en una colección:
{” x” : 5}
{” x” : 15}
{” x” : 25}
{” x” : [5 , 25]}

Si se quieren encontrar todos los documentos donde x está entre 10 y 20, entonces
se podrı́a construir la siguiente consulta:
db . t e s t . f i n d ( { ” x” : {” $ g t ” : 1 0 , ” $ l t ” : 2 0 } } )

esperando que recuperase como resultado el documento {"x": 15} sin embargo
recupera dos documentos:
{” x” : 15}
{” x” : [ 5 , 2 5 ] }

Aunque ni 5 ni 25 están entre 10 y 20, sin embargo 25 encaja con la condición de


ser más grande que 10 y 5 encaja con la condición de ser más pequeño que 20. Es
por ello que las consultas sobre rangos en arrays no son muy útiles pues un rango
encajará con cualquier array multielemento.
Sin embargo existen varias formas de conseguir el comportamiento esperado:
◦ Se puede usar el operador $elemenMatch para forzar que MongoDB compare
cada condición con cada elemento del array. Sin embargo este operador no
encajará con elementos que no sean arrays.
◦ Ası́ por ejemplo
db . t e s t . f i n d ( { ” x” : {” $elemMatch ” : {” $ g t ” : 1 0 , ” $ l t ” : 2 0 } } )

no devuelve ningún resultado cuando el documento {"x": 15} cumplirı́a el


criterio. El problema es que en ese documento x no es un array.

4.3.1. Consultas sobre documentos embebidos


Existen dos caminos para consultar sobre documentos embebidos:

1. Consultar sobre el documento entero.


2. Consultar sobre pares individuales clave-valor.

La consulta sobre un documento embebido entero funciona de la misma forma que una
consulta normal.

115

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Sin embargo, una consulta sobre un subdocumento entero debe encajar exactamente
con el subdocumento. Por ejemplo si se añadiera un nuevo campo en el subdocumento
o se cambiara el orden de los campos entonces ya no coincidirı́an con la búsqueda.

En general es mejor realizar consultas sobre claves especı́ficas de un documento em-


bebido de manera que si se producen cambios en la estructura de los documentos, las
consultas no se vean afectadas por los cambios.

Para consultar sobre claves especı́ficas de documentos embebidos se usa la notación ”.”
como por ejemplo:
db . prueba . f i n d ( { ” nombre . nombre ” : ” J a v i e r ” , ” nombre . a p e l l i d o ” : ” Sanz ” } )

Observar que en los documentos de consultas el uso de la notación dot tiene como
significado alcanzar el interior del documento embebido. Es por ello que no se permita
usar el caracter ”.” dentro de los documentos que se van a insertar(por ejemplo existen
problemas cuando se quieren almacenar URLS).

Una forma de resolverlo consiste en realizar un reemplazamiento global antes de insertar


o después de recuperar, sustituyendo un carácter que no es legal en una URL por el
carácter ”.”

Las coincidencias con documentos embebidos puede complicarse cuando la estructura


del documento se hace compleja.

Por ejemplo supóngase que se están almacenando posts de un blog y se quieren recupe-
rar comentarios de Javier que fueron puntuados con al menos un 5, entonces se podrı́a
modelar el post de la siguiente manera:
>db . b l o g . p o s t s . findOne ( )
{
” i d : ObjectId ( . . . ) ,
” contenido : ” . . . ” ,
” comentarios = [
{
” autor ” : ” j a v i e r ” ,
” puntuacion ” : 3 ,
” comentario ” : ” bonito post ”
},
{
” a u t o r ” : ” maria ” ,
” puntuacion ” : 6 ,
” comentario ” : ” t e r r i b l e post ”
}
]
}
>

Ahora bien con esta estructura no se puede consultar usando la expresión:


db . b l o g . f i n d ( { ” c o m e n t a r i o s ” : {” a u t o r ” : ” j a v i e r ” , ” p u n t u a c i o n ” : {” $ g t e ” : 5 } } } )

116

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
dado que los documentos embebidos deben encajar el documento entero y éste no
encaja con la clave ”comentario”.

Tampoco funcionarı́a la expresión:


db . b l o g . f i n d ( { ” c o m e n t a r i o s . a u t o r ” : ” j a v i e r ” , ” c o m e n t a r i o s . p u n t u a c i o n ” : {” $ g t e ” : 5 } } )

dado que la condición del autor podrı́a encajar con un comentario diferente al comen-
tario que encajarı́a con la condición de la puntuación.

Ası́ esta consulta devolverı́a el documento anterior dado que encajarı́a autor ”Javier”
con el primer comentario y puntuación ”6” con el segundo comentario.

Para agrupar correctamente los criterios de búsqueda sin necesidad de especificar ca-
da clave se puede usar $elemMatch. Este operador permite especificar parcialmente
criterios para encajar con un único document embebido en un array. Ası́ la consulta
correcta serı́a:
db . b l o g . f i n d ( { ” c o m e n t a r i o s ” : { ” $elemMatch ” : { ” a u t o r ” : ” j a v i e r ” , ” p u n t u a c i o n ” : { ” $ g t e ” : 5 } } } } )

Por tanto, $elemMatch será til cuando exista más de una clave que se quiere encajar
en un documento embebido.

4.3.2. Lı́mites, saltos y ordenaciones


Las opciones más comunes sobre las consultas es limitar el número de resultados re-
cuperados, saltarse un número de resultados o bien la ordenacion de los resultados.
Todas estas opciones deben ser añadidas antes de que la consulta sea enviada a la base
de datos.

Para conseguir limitar los resultados se encadena la función limit() sobre la llamada
a find(). Por ejemplo, para retornar solo 3 resultados se harı́a de la siguiente manera:
db . c . f i n d ( ) . l i m i t ( 3 )

Si existen menos de 3 documentos que encajan con la consulta entonces solo se retornan
los documentos que encajan (limit solo establece un lı́mite superior pero no un lı́mite
inferior).

La función skip() funciona de una manera similar db.c.find().skip(3). Este ejem-


plo se saltará los 3 primeros documentos que encajen y retornará el resto de resultados.
Si existen menos de 3 documentos , no retornará ningún documento.

La función sort() toma un objeto formado por pares clave-valor donde las claves son
nombres de claves y los valores indican un sentido de la ordenación: 1(ascendente) o
-1(descendente). Cuando existen más de una clave, los resultados son ordenados en ese
orden.

117

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Por ejemplo, para ordenar los resultados en orden ascendente de ”nombre” y en orden
descendente de ”edad” se harı́a de la siguiente manera:
db . c . f i n d ( ) . s o r t ( { username : 1 , age : −1})

Estos métodos se pueden combinar. Por ejemplo, es útil en la paginación de resultados.


Supóngase que se está consultando una base de datos de libros y se van a mostrar los
resultados por páginas con un máximo de 50 resultados por página ordenados por
precio de mayor a menor, entonces se puede hacer lo siguiente:
db . s t o c k . f i n d ( { ” d e s c ” : ” l i b r o s ” } ) . l i m i t ( 5 0 ) . s o r t ( { ” p r i c e ” : −1})

Cuando la persona hace click sobre la página siguiente para ver más resultados entonces
se puede añadir un skip() a la consulta de manera que se salten los primeros 50
resultados:
db . s t o c k . f i n d ( { ” d e s c ” : ” l i b r o s ” } ) . l i m i t ( 5 0 ) . s k i p ( 5 0 ) . s o r t ( { ” p r i c e ” : −1})

A veces se puede dar el caso de tener un clave con multiples tipos, de manera que si
se aplica la función sort() entonces se ordenarán de acuerdo a un orden predefinido.
Los valores del más pequeño al más grande son:

1. Minimum value
2. Null
3. Numbers: integers, floats, doubles
4. Strings
5. Objects/document
6. Array
7. Binary data
8. Object ID
9. Boolean
10. Date
11. Timestamp
12. Regular expression
13. Maximum value

4.4. Actualizaciones
Para modificar un documento almacenado se usa el método update que toma 2 paráme-
tros:

118

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Una condición de búsqueda que localiza el documento a actualizar.
• Un conjunto de cambios a realizar sobre el documento.

Las actualizaciones son atómicas de manera que si se quieren realizar dos a la vez, la
primera que llegue es la primera en realizarse y a continuación se hará la siguiente.

El tipo de actualización más simple consiste en reemplazar un documento por otro.


Por ejemplo, que se quiere cambiar el siguiente documento:
{
” nombre ” : ” Juan ” ,
” amigos ” : 3 2 ,
” e ne mi g o s ” : 2
}

Y se quiere crear un campo ”relaciones” que englobe a los campos ”amigos” y ”enemi-
gos” como subdocumentos. Esta operación se puede llevar a cabo con un update:
> v a r j u a n=db . prueba . findOne ( { ” nombre ” : ” Juan ” } ) ;
> j u a n . r e l a c i o n e s = {” amigos ” : j u a n . amigos , ” e ne mig os ” : j u a n . e n em igo s } ;
{” amigos ” : 3 2 , ” en e mi g o s ” : 2 }
> j u a n . PrimerNombre=j u a n . nombre
Juan
> d e l e t e j u a n . amigos
true
> d e l e t e j u a n . en e m i g o s
true
> d e l e t e j u a n . nombre
true
> db . prueba . update ( { ” nombre ” : ” j u a n ” } , j u a n ) ;
W r i t e R e s u l t ( { ” nMachted ” : 1 , ” nUpersted ” : 0 , ” n M o d i f i e d ” : 1 } )
>db . prueba . findOne ( )
{
” i d ” : ObjectId ( ” . . . ” ) ,
” relaciones ”: {
” amigos ” : 3 2 ,
” e n e mi g o s ” : 2
},
” PrimerNombre ” : ” Juan ”
}
>

Reemplazamiento:

• Un error común es cuando encaja más de un documento con el criterio de búsqueda


y se crea un campo duplicado id con el segundo parámetro. En este caso la base
de dato genera un error y ningún documento es actualizado.
• Por ejemplo supóngase que se crean varios documentos con el mismo valor para
el campo ”nombre”, sea ”Juan”, y se quiere actualizar el valor del campo edad
de uno de ellos(se quiere aumentar el valor de la edad del segundo ”Juan”):
> db . prueba . f i n d ( )
{ ” i d ” : O b j e c t I d ( ” . . . 5 4 ” ) , ” nombre ” : ” Juan ” , ” edad ” : 3 2 }
{ ” i d ” : O b j e c t I d ( ” . . . 5 5 ” ) , ” nombre ” : ” Juan ” , ” edad ” : 3 3 }
{ ” i d ” : O b j e c t I d ( ” . . . 5 6 ” ) , ” nombre ” : ” Juan ” , ” edad ” : 4 5 }

119

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
> j u a n=db . prueba . findOne ( { ” nombre ” : ” Juan ” , ” edad ” : 3 3 } )
{
” i d ” : ObjectId ( ” . . . 5 5 ” ) ,
” nombre ” : ” Juan ” ,
” edad ” : 33
}
> j u a n . edad++;
33
> db . prueba . update ( { ” nombre ” : ” j u a n ” } , j u a n ) ;
WriteResult ({
” nMachted ” : 0 ,
” nUpersted ” : 0 ,
” nModified ” : 0 ,
” w r i t e E r r o r : {}
” code ” : 1 6 8 3 7 ,
” errmsg ” : ”The i d f i e l d cannot be changed from { O b j e c t I d ( ” . . . 5 4 ” ) } t o O b j e c t I d ( ” . . . 5 5 ” ) }
}
})

• Se produce un error dado que el método update busca un documento que encaje
con la condición de búsqueda y el primero que encuentra es el referido al ”Juan”
que tiene 32 años. Intenta cambiar ese documento por el actualizado, y se encuen-
tra que si hace el cambio habrı́a dos documentos con el mismo id, y eso no es
posible(el id debe ser único).
• Para evitar estas situaciones lo mejor es usar el método update con el campo id
que es único. En el ejemplo anterior se podrı́a hacer la actualización si se hiciera
de la siguiente forma:
> db . prueba . update ( { ” i d ” : O b j e c t I d ( ” . . . 5 5 ” ) } , j u a n ) ;
W r i t e R e s u l t ( { ” nMachted ” : 1 , ” nUpersted ” : 0 , ” n M o d i f i e d ” : 1 } )
>

• Otra ventaja de usar el campo id es que el documento está indexado por este
campo.

Modificadores:

• En muchas ocasiones el tipo de actualización que se quiere realizar consiste en


añadir, modificar o eliminar claves, manipular arrays y documentos embebidos,...
Para estos casos se van a usar un conjunto de operadores de modificación.
• $inc:
◦ Este operador permite cambiar el valor numérico de una clave que ya existe
incrementando su valor por el especificado junto al operador, o bien puede
crear una clave que no existı́a inicializándola al valor dado.
◦ Por ejemplo supóngase que se mantienen los datos estadı́sticos de un sitio web
en una colección de manera que se incrementa un contador cada vez alguien
visita una página. Para ello se tiene un documento que almacena la URL y
el número de visitas de la página:
> db . prueba . findOne ( )
{

120

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
” i d ” : ObjectId ( ” . . . ” ) ,
”URL” : ”www. e j e m p l o . e s ” ,
” v i s i t a s ” : 34
}
>

• Cada vez que alguien visita una página se busca la página a partir de su URL y
se incrementa el campo de ”visitas” con el modificador $inc que incrementa el
campo dado en el valor descrito:
> db . prueba . update ( { ”URL” : ”www. e j e m p l o . e s ” } , { ” $ i n c ” : { ” v i s i t a s ” : 1 } } )
W r i t e R e s u l t ( { ” nMachted ” : 1 , ” nUpersted ” : 0 , ” n M o d i f i e d ” : 1 } )
> db . prueba . findOne ( )
{
” i d ” : ObjectId ( ” . . . ” ) ,
”URL” : ”www. e j e m p l o . e s ” ,
” v i s i t a s ” : 35
}
>

• También serı́a posible incrementar el valor por un valor mayor que 1:


> db . prueba . update ( { ”URL” : ”www. e j e m p l o . e s ” } , { ” $ i n c ” : { ” v i s i t a s ” : 3 0 } } )
W r i t e R e s u l t ( { ” nMachted ” : 1 , ” nUpersted ” : 0 , ” n M o d i f i e d ” : 1 } )
> db . prueba . findOne ( )
{
” i d ” : ObjectId ( ” . . . ” ) ,
”URL” : ”www. e j e m p l o . e s ” ,
” v i s i t a s ” : 64
}
>

• De la misma forma se podrı́a decrementar usando números negativos:


> db . prueba . update ( { ”URL” : ”www. e j e m p l o . e s ” } , { ” $ i n c ” : { ” v i s i t a s ” : −37}})
W r i t e R e s u l t ( { ” nMachted ” : 1 , ” nUpersted ” : 0 , ” n M o d i f i e d ” : 1 } )
> db . prueba . findOne ( )
{
” i d ” : ObjectId ( ” . . . ” ) ,
”URL” : ”www. e j e m p l o . e s ” ,
” v i s i t a s ” : 27
}
>

• Por ejemplo, se podrı́a añadir un nuevo campo para indicar el número de enlaces
de la página:
> db . prueba . update ( { ”URL” : ”www. e j e m p l o . e s ” } , { ” $ i n c ” : { ” e n l a c e s ” : 2 0 } } )
W r i t e R e s u l t ( { ” nMachted ” : 1 , ” nUpersted ” : 0 , ” n M o d i f i e d ” : 1 } )
> db . prueba . findOne ( )
{
” i d ” : ObjectId ( ” . . . ” ) ,
”URL” : ”www. e j e m p l o . e s ” ,
” v i s i t a s ” : 27 ,
” e n l a c e s ” : 20
}
>

121

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Cuando se usan operadores de modificación el valor del campo id no puede ser
cambiado (en cambio cuando se reemplaza un documento entero si es posible cam-
biar el campo id). Sin embargo los valores para cualquier otra clave incluyendo
claves indexadas únicas si pueden ser modificadas.
• Este operador solo puede ser usado con números enteros, enteros largos o double,
de manera que si se usa con otro tipo de valores (incluido los tipos que algu-
nos lenguajes tratan como números tales como booleanos, cadenas de números,
nulos,...) producirá un fallo.

$set y $set:

• Este operador establece un valor para un campo dado, y si el campo dado no


existe entonces lo crea. En este sentido es útil para modificar el esquema de un
documento o añadir claves definidas por el usuario.
• Por ejemplo supóngase que se tiene el perfil de usuario almacenado en un docu-
mento:
> db . prueba . findOne ( )
{
” i d ” : ObjectId ( ” . . . ” ) ,
” nombre ” : ” Juan ” ,
” edad ” : 3 4 ,
” s e x o ” : ” Varon ” ,
” l o c a l i z a c i ó n ” : ” Madrid ”
}
>

• Si el usuario desea añadir un campo sobre su libro favorito, se podrı́a hacer usando
el modificador $set:
> db . prueba . update ( { . . . { ” $ s e t : { ” l i b r o F a v o r i t o ” : ” Guerra y Paz ” } } )

• También con el modificador $set es posible cambiar el tipo de un campo que se


modifica. Por ejemplo si se quiere que el campo ”libroFavorito”ea un array en vez
de un valor único también puede usarse el modificador $set:
> db . prueba . update ( { . . . { ” $ s e t : { ” l i b r o F a v o r i t o ” : [ ” Guerra y Paz ” , ” El Q u i j o t e ” ] } } )

• Mediante el operador $set es posible realizar cambios en documentos embebidos,


para lo cual sólo es necesario indicar el campo en el que se encuentran.
• Existe un operador denominado $unset que permite eliminar campos de un do-
cumento. En el ejemplo anterior si se quiere eliminar el campo ”libroFavorito”:
> db . prueba . update ( { . . . { ” $ u n s e t : { ” l i b r o F a v o r i t o ” : 1 } } )

Para añadir, modificar o eliminar claves se debe usar siempre los modificadores $. En
este sentido observar que si intentara hacer un cambio en las claves con un comando
como el siguiente:

122

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
db . prueba . update ( c r i t e r i o , {” edad ” : ” p aı́ s ” } )

tendrı́a como efecto reemplazar el documento que encaje con el criterio de búsqueda
por el documento ”edad”: ”paı́s”
{ ’ ’ edad ’ ’ : ’ ’ p aı́ s ’ ’ }

Modificadores de los arrays:

• Adición de elementos:
◦ El modificador $push añade elementos al final del array si existe o bien crea
uno nuevo si no existe.
◦ Por ejemplo, supóngase que se almacenan posts de un blog y se quiere añadir
una clave ”comentarios” que contenga un array de comentarios. Esto puede
hacerse usando el modificador $push que en el ejemplo crea una nueva clave
denominada ”comentarios”.
◦ Si se especifica un array con un único elemento, su comportamiento es similar
a un $push sin $each.
◦ También es posible limitar la longitud hasta la que puede crecer un array
usando el operador $lice junto al operador $push.
◦ Por último el operador $sort permite ordenar los elementos indicando el cam-
po de ordenación y el criterio en forma de 1(ascendente) o -1(descendente).
◦ Tanto $lice como $sort deben ir junto a un operador $each y no pueden
aparecer solos con un $push.
◦ $sort también puede ser usado para ordenar elementos que no son documen-
tos, en cuyo caso no hay que indicar ningún campo. En el siguiente ejemplo
se insertan dos elementos y se ordena el conjunto de manera ascendente.

Usando arrays como conjuntos:

• Los arrays se pueden tratar como un conjunto añadiendo valores solo si no estaban
ya. Para ello se usa el operador $ne junto al operador $push.
• Por ejemplo si se quiere añadir un autor a una lista de citas pero solo en el caso
de que no estuviera, entonces se podrı́a hacer de la siguiente manera:
db . a r t i c u l o s . update ( { ” a u t o r e s c i t a d o s ” : {” $ne ” : ” Pepe ” } } ,
{” $push ” : { ” a u t o r e s c i t a d o s ” : {” $ne ” : ” Pepe ” } } )

• Alternativamente también es posible hacer la misma operación mediante el ope-


rador $addToSet.

Borrado de elementos:

123

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Existen varias formas de eliminar elementos de un array dependiendo de la forma
en la que se quieran gestionar. Si se quiere gestionar como si fuera una pila o una
cola entonces se puede usar el operador $pop que permite eliminar elementos del
final del array(si toma el valor 1) o bien del principio del array (si toma el valor
-1):
• Otra forma alternativa de eliminar elementos es especificando un criterio en vez
de una posición en el array usando el operador $pull. El operador $pull eli-
mina todas las coincidencias que encuentre en los documento no solo la primera
coincidencia.

Modificaciones posicionales en un array:

• Las manipulaciones de un array se convierten en algo complejo cuando se tienen


múltiples valores y se quieren modificar solo algunos de ellos. En este sentido
existen dos caminos para manipular valores de un array.
• Mediante su posición. En este caso sus elementos son seleccionados como si se
indexaran las claves de un documento.

4.5. Indexación
Son una estructura de datos que mantiene información acerca de los valores de campos
especı́ficos en los documentos de una colección, y se utiliza para ordenar y clasificar
rápidamente los documentos de una colección.

De esta forma asegura una búsqueda y recuperación rápida de datos de los documentos.

Esencialmente se puede pensar que un ı́ndice es una consulta predefinida que fue eje-
cutada y los resultados de la misma se almacenan, de esta forma la consulta de in-
formación se hace rápida al no tener que recorrer la base de datos para recopilar esta
información.

Cuando se crea un ı́ndice aumenta la velocidad de las consultas, pero se reduce la


velocidad de las inserciones y las eliminaciones debido a que el sistema debe mantener
y actualizar el ı́ndice cada vez que se realiza una operación de escritura (inserción,
actualización o borrado).

Es por ello que generalmente es mejor añadir ı́ndices en las colecciones cuando el
número de lecturas es mayor que el número de escrituras, de hecho si hay más
escrituras que lecturas entonces los ı́ndices pueden ser contraproducentes.

Por otra parte, cuando se tienen ı́ndices de vez en cuando hay que borrar algunos
ı́ndices o reconstruirlos debido a varias razones:

• Limpiar algunas irregularidades que aparecen en los ı́ndices.

124

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Aumento del tamaño de la base de datos.
• Espacio excesivo ocupado por los ı́ndices. Solo se pueden definir como máximo 40
ı́ndices por colección.

Toda la información sobre los ı́ndices se encuentra almacenada en la colección system.indexes.

Ası́ por ejemplo si se ejecuta el comando indexes.find() permite ver los ı́ndices que
se han creado hasta el momento.

En general los ı́ndices se usan con las consultas (find, findOne) y en las ordenaciones.

Si se intentan realizar muchas ordenaciones sobre la información de una colección,


entonces se deberı́an añadir ı́ndices que correspondan con la especificación de la orde-
nación.

Ası́ si se usa el comando sort() sobre una colección donde no existen ı́ndices sobre
los campos que aparecen especificados en la ordenación, entonces puede dar lugar a un
error si se excede el tamaño máximo del buffer interno de ordenación.

Listado de ı́ndices:

• Toda la información sobre los ı́ndices se encuentra almacenada en la colección


system.indexes.
• Esta colección gestiona todos los ı́ndices que han sido creados en todas las colec-
ciones ası́ como los campos o elementos a los que hacen referencia.
• Se trata de una colección normal por lo que pueden operarse con ella con los
comandos habituales.
• Por ejemplo si se quieren listar los ı́ndices definidos sobre una base de datos
determinada:
> db . system . i n d e x e s . f i n d ( )
{” v ” : 1 , ” key ” : { ” i d ” : 1 } , ” name ” : ” i d ” , ” ns ” : ” b l o g . p o s t s ”}
{” v ” : 1 , ” key ” : { ” i d ” : 1 } , ” name ” : ” i d ” , ” ns ” : ” b l o g . a u t o r e s ”}
>

En el ejemplo se tiene una base de datos denominada blog que tiene dos coleccio-
nes: posts y autores.
• Aunque sobre las colecciones no se han definido ı́ndices por el usuario, sin embargo
si existen dos ı́ndices que se han creado de forma automática sobre los campos
id de cada colección.
• Los ı́ndices sobre el campo id son creados y borrados automáticamente por el
sistema cada vez que se crea o se borra una colección.

Cuando se crear un ı́ndice sobre un elemento, entonces el sistema construye un ı́ndice


en forma de árbol b, que es usado para localizar eficientemente los documentos.

125

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Si no existe ningún ı́ndice adecuado, entonces se recorren todos los documentos de la
colección para encontrar los registros que satisfacen la consulta.
Creación de un ı́ndice simple:
• Para añadir nuevos ı́ndices a una colección se usa la función createIndex().
• Esta función primero chequea si ya se ha definido un ı́ndice con la misma especi-
ficación, en cuyo caso devuelve el ı́ndice, y en caso contrario lo crea.
• La función toma como parámetros el nombre de una clave de uno de los docu-
mentos que se usará para crear el ı́ndice, y un número que indica la dirección de
ordenación del ı́ndice: 1 almacena los ı́tems en orden ascendente y -1 almacena
los ı́tems en orden descendente.
• El comando asegura que el ı́ndice se creará para todos los valores de la clave
indicada para todos los documentos de la colección.
• Por ejemplo si se quiere crear un ı́ndice ascendente sobre el campo ”Etiquetas” se
harı́a de la siguiente manera:
> db . p o s t . c r e a t e I n d e x ( { ” E t i q u e t a s ” : 1 } )
{
” createdCollectionAutomatically ”: false ,
” numIndexesBefore ” : 1 ,
” n umI nde xesAft er ” : 2 ,
” ok ” : 1
}
>db . system . i n d e x e s . f i n d ( )
{” v ” : 1 , ” key ” : { ” i d ” : 1 } , ” name ” : ” i d ” , ” ns ” : ” b l o g . p o s t s ”}
{” v ” : 1 , ” key ” : { ” i d ” : 1 } , ” name ” : ” i d ” , ” ns ” : ” b l o g . a u t o r e s ”}
{” v ” : 1 , ” key ” : { ” E t i q u e t a s ” : } , ” name ” : ” E t i q u e t a s 1 ” , ” ns ” : ” b l o g . p o s t s ”}
>

• Para indexar un campo de un documento embebido se usa la notación dot.


• Ası́ por ejemplo si se tiene un campo contador dentro de un subdocumento ”co-
mentarios” sobre el que se quiere definir un ı́ndice entonces se harı́a de la siguiente
manera:
> db . p o s t . c r e a t e I n d e x ( { ” c o m e n t a r i o s . c o n t a d o r ” : 1 } )
{
” createdCollectionAutomatically ”: false ,
” numIndexesBefore ” : 2 ,
” n umI nde xesAft er ” : 3 ,
” ok ” : 1
}
>db . system . i n d e x e s . f i n d ( )
{” v ” : 1 , ” key ” : { ” i d ” : 1 } , ” name ” : ” i d ” , ” ns ” : ” b l o g . p o s t s ”}
{” v ” : 1 , ” key ” : { ” i d ” : 1 } , ” name ” : ” i d ” , ” ns ” : ” b l o g . a u t o r e s ”}
{” v ” : 1 , ” key ” : { ” E t i q u e t a s ” : } , ” name ” : ” E t i q u e t a s 1 ” , ” ns ” : ” b l o g . p o s t s ”}
{” v ” : 1 , ” key ” : { ” c o m e n t a r i o s . c o n t a d o r ” : } , ” name ” : ” c o m e n t a r i o s . c o n t a d o r 1 ” , ” ns ” : ” b l o g . p o s t s ”}
>

• Si se especifica un campo de un documento que es de tipo array entonces el ı́ndice


incluirá todos los elementos del array como términos separados del ı́ndice, es decir
un ı́ndice multiclave.

126

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• De esta forma cada documento es enlazado a múltiples valores del ı́ndice.
• Observar que existe un operador especial para realizar consultas donde se selec-
cionan solos aquellos documentos que tienen todos los términos que se especifican.
• Ası́ por ejemplo en la base de datos blog, se tiene la colección posts con un campo
denominado ”Etiquetas” que representa las etiquetas que se le asocian a un post
determinado.
• En este sentido se podrı́a definir una consulta que recuperase todos los artı́culos
que tienen unas determinadas etiquetas como ”teléfono”, ”app”:
> db . p o s t . f i n d ( ) ( { ” E t i q u e t a s ” : { ” $ a l l ” : [ ” t e l e f o n o ” , ” app ” ] } } )

• Sin un ı́ndice multiclave sobre el campo ”Etiquetas”, se habrı́a tenido que consul-
tar cada documento de la colección para ver si existe un término, y en tal caso a
continuación chequear si ambos términos están presentes.
Creación de un ı́ndice compuesto:
• A primera vista parece que crear un ı́ndice separado para cada campo que aparece
en las consultas serı́a una buena idea para hacerlas más eficientes, sin embargo
los ı́ndices tienen un impacto significativo sobre la adición y eliminación de datos
de la base de datos puesto que es necesario actualizarlos cada vez que se realiza
una de estas operaciones.
• Los ı́ndices compuestos son una buena forma de mantener bajo el número de
ı́ndices que se tienen sobre una colección, permitiendo combinar múltiples campos
en un único ı́ndice.
• Es por ello que se deben usar este tipo de ı́ndices siempre que sea posible.
• Para crear un ı́ndice compuesto se especifican varias claves en vez de una.
• Existen dos tipos de ı́ndices compuestos: ı́ndices de subdocumentos e ı́ndices com-
puestos definidos por el usuario.
• Se han definido algunas reglas para usar los ı́ndices compuestos en consultas que
no usan todos los claves que componen el ı́ndice.
• La comprensión de estas reglas, permite construir un conjunto de ı́ndices com-
puesto que cubren todas las consultas que se desean realizar sobre una colección
sin tener un ı́ndice individual sobre cada elemento (evitando ası́ el impacto sobre
el rendimiento en las actualizaciones e inserciones).
• Un contexto donde puede que los ı́ndices compuestos no sean una buena elección
es cuando se usa el ı́ndice en una ordenación.
• En la ordenación no es bueno usar ı́ndices compuestos salvo que la lista de términos
y las direcciones de ordenación encajen exactamente con la estructura del ı́ndice.
• En estos casos, una elección mejor es usar ı́ndices simples individuales sobre cada
campo.

127

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Se puede crear un ı́ndice compuesto usando un subdocumento entero, de manera
que cada elemento del documento embebido se convierte en parte del ı́ndice.
• Por ejemplo supóngase que se tiene un subdocumento autor con el nombre y el
email en su interior, entonces se puede crear un ı́ndice compuesto con los términos
autor.nombre y autor.email.
• El único problema que existe con esta forma de crear ı́ndices compuestos es que
se pierde la posibilidad de configurar el orden de las claves en el ı́ndice puesto que
no se puede configurar la dirección de cada uno de ellos.
• Además observar que cuando se realizan consultas exactas sobre documentos em-
bebidos, el orden en que ocurren los campos debe encajar exactamente.
• Índices creados manualmente:
◦ Tal como se ha visto antes, cuando se usa un subdocumento como clave
del ı́ndice, entonces el orden de los elementos usados para construir el ı́ndice
multiclave encaja con el orden en el cual aparecen en la representación interna
del subdocumento.
◦ En muchos casos esto no da el suficiente control sobre el proceso de creación
de un ı́ndice.
◦ Para evitar esto y garantizar que la consulta usa un ı́ndice construido de
la forma deseada, se necesita asegurar que se usa la misma estructura del
subdocumento para crear el ı́ndice que la usada para realizar la consulta.
◦ Para ello se crea un ı́ndice compuesto nombrando explı́citamente todos los
campos por los que se desea combinar el ı́ndice, y el orden de combinación.
◦ Ası́ por ejemplo si se quiere crear un ı́ndice compuesto en el que los documen-
tos primero se ordena con respecto al campo ”Email” y a continuación para
cada valor del campo ”Email” se ordena con respecto al campo ”Nombre”:
> db . p o s t . f i n d ( ) ( { ” Autor ” : { ” Email ” : ” p e p i t o @ g m a i l . com ” , ” Nombre ” : ” P e p i t o ” } } )

Entonces el ı́ndice se define de la siguiente manera:


> db . p o s t . c r e a t e I n d e x ( { ” Autor . Email ” : 1 , ” Autor . Nombre ” : 1 } )
{
” createdCollectionAutomatically ”: false ,
” numIndexesBefore ” : 1 ,
” numI ndexesAft er ” : 2 ,
” ok ” : 1
}

◦ El beneficio de esta aproximación es que se puede crear un ı́ndice sobre múlti-


ples claves, pero con la ventaja de poder especificar cómo se quiere que sean
indexados cada uno de los campos de forma descendente o ascendente.
◦ Sin embargo en el caso de los subdocumentos se está limitado a que se ordenen
sólo de manera ascendente o descendente.
◦ Se pueden especificar diversas opciones cuando se crea un ı́ndice tales como
la creación de ı́ndices únicos o permitir la indexación en segundo plano.

128

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Opciones sobre ı́ndices:
◦ Para ello se especifican las opciones como parámetros de la función createIndex()
de la siguiente manera:
db . C o l e c c i o n . c r e a t e I n d e x ( ) ( { Campo : 1 } , { o p c i o n 1 : t r u e , o p c i o n 2 : t r u e , . . . } )

◦ Cuando se especifica la opción unique, entonces se crea un ı́ndice donde todas


las claves deben ser diferentes.
◦ De manera que el sistema retornará un error si se intenta insertar un docu-
mento donde clave del ı́ndice coincide con la clave de un documento existente.
◦ Es útil para campos donde se quiere asegurar que no se repiten valores.
◦ Sin embargo si se quiere añadir un ı́ndice único a una colección ya existente
con datos, hay que asegurarse de que no existen duplicaciones en las claves,
pues de lo contrario fallará si cualquiera de las claves no son únicas.
◦ Funciona con ı́ndices simples y compuestos, pero no ası́ con ı́ndices para
valores multiclave.
◦ En el caso de los ı́ndices compuestos, el sistema fuerza a la unicidad sobre la
combinación de los valores en vez del valor individual para alguno o todos los
valores de la clave.
◦ Si un documento es insertado con un campo que falta y especificado como
una clave única, entonces automáticamente se inserta el campo con el valor
a null.
◦ Esto significa que solo se puede insertar un documento en el que falte un
campo pues nuevos valores nulos harán que se considere que la clave no es
única.
◦ En el siguiente ejemplo se crea un ı́ndice único sobre el campo ”tı́tulo” de la
colección ”posts”:
> db . p o s t . i n s e r t ( { ” t i t u l o ” : 1 , ” Prueba ” } )
> db . p o s t . c r e a t e I n d e x ( { ” t i t u l o ” : 1 } , { ” u n i q u e ” : t r u e } )
{
” createdCollectionAutomatically ”: false ,
” numIndexesBefore ” : 1 ,
” numI ndexesAft er ” : 2 ,
” ok ” : 1
}

◦ Si se quiere crear un ı́ndice único para un campo donde se conoce que existe
valores duplicados, entonces se puede usar la opción dropdups, que elimina
los documentos que causan que falle la creación de un ı́ndice único.
◦ Se mantendrá el primer documento que se encuentre en la ordenación natural
de la colección y se eliminará cualquier otro documento que se encuentre y
viole la condición de creación del ı́ndice.
◦ En el siguiente ejemplo se crea un ı́ndice único sobre el campo ”titulo” de la
colección ”posts” y además se indica que se borren todos los documentos que

129

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
pudieran hacer fallar la creación del ı́ndice debido a la duplicidad de valores
del campo ”titulo”:
> db . p o s t . c r e a t e I n d e x ( { ” t i t u l o ” : 1 } , { ” u n i q u e ” : t r u e , ” dropdups ” : t r u e } )
{
” createdCollectionAutomatically ”: false ,
” numIndexesBefore ” : 1 ,
” numI ndexesAft er ” : 2 ,
” ok ” : 1
}

• Eliminación de ı́ndices:
◦ Se puede elegir entre eliminar un ı́ndice concreto de una colección usando la
función dropIndex(Especificación del ı́ndice):
◦ También es posible eliminar todos los ı́ndices de una colección usando la
función dropIndexes():
◦ Cuando se sospecha que un ı́ndice está dañado, entonces se puede forzar la
reindexación de la colección.
• Selección de ı́ndices:
◦ Cuando se quiere ejecutar una consulta, entonces el sistema crea un plan de
ejecución que es una lista de los pasos que debe ejecutar para llevar a cabo
la consulta.
◦ Cada consulta tiene múltiples planes que producirán el mismo resultado.
◦ Sin embargo cada plan puede tener elementos que son más costosos de ejecutar
que otros.
◦ Por ejemplo un recorrido de todas los registros de una colección es una ope-
ración costosa y cualquier plan que lo incorpore será lenta.
◦ Estos planes pueden incluir alternativamente listas de ı́ndices a usar para las
operaciones de consulta y ordenación.
◦ MongoBD usa un componente denominado ”analizador de consultas, el cual
toma una consulta y los objetivos de la misma, y produce un conjunto de
planes de ejecución.
◦ La función explain() lista tanto el plan que se usará para la consulta ası́
como los planes alternativos.
◦ Ası́ mismo existe otro componente denominado ”optimizador de consultas”
que tiene como función seleccionar qué plan de ejecución es el más adecuado
para una consulta particular.
◦ Este componente no usa un método basado en costes para seleccionar el plan
de ejecución, sino que ejecuta todos en paralelo y usa el que retorna los
resultados más rápidamente, finalizando el resto una vez el plan ganador
ejecuta la última lı́nea.
• El comando hint():

130

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
◦ Sin embargo puede haber casos en los que el optimizador no haga la mejor
elección.
◦ En estos casos es posibles forzar al optimizador de consultas a que use un
ı́ndice dado usando el operador hint().
◦ Por ejemplo supóngase que se tiene un ı́ndice sobre un subdocumento llamado
”Autor” que tiene como campos ”nombre” e ”email”:
> db . p o s t . c r e a t e I n d e x ( { ” Autor ” : 1 } )
{
” createdCollectionAutomatically ”: false ,
” numIndexesBefore ” : 1 ,
” numI ndexesAft er ” : 2 ,
” ok ” : 1
}

◦ Se podrı́a usar hint() para forzar a que el optimizador de consultas use el


anterior ı́ndice:
> db . p o s t . f i n d ( { ” Autor ” : { ” Nombre ” : ” Juan ” , ” Email ” : ” juan@gmail . com } } ) . h i n t ( { ” Autor ” : 1 } )

◦ También es posible usar hint() para forzar a que una consulta no use ı́ndices,
es decir que se use el escaneo de la colección de documentos como medio para
seleccionar los registros entonces se harı́a de la siguientes manera:
> db . p o s t . f i n d ( { ” Autor ” : { ” Nombre ” : ” Juan ” , ” Email ” : ” juan@gmail . com } } ) . h i n t ( { ” $ n a t u r a l ” : 1 } )

4.6. MapReduce
¿Qué es MapReduce?:

• Map-reduce es un paradigma de programación orientado al procesamiento paralelo


de grandes volúmenes de información.
• No todos los procesos pueden ser abordados mediante MapReduce. Concretamente
son abordables sólo aquellos que se pueden separar en operaciones de map y de
reduce.
• La función map se ejecuta de forma distribuida a lo largo de varias máquinas. Los
datos de entrada, procedentes por regla general de un gran archivo, se dividen
en un conjunto de m particiones de entrada (generalmente 16 a 64 megabytes).
Estas particiones pueden ser procesadas en diversas máquinas.
• La función reduce se aplica en paralelo para cada grupo creado por la función
map.

MongoDB puede utilizar comandos mapReduce.

Sintaxis de los comandos básicos de mapReduce en MongoDB:

131

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 4.1: Esquema de MapReduce

>db . c o l l e c t i o n . mapReduce (
f u n c t i o n ( ) { emit ( key , v a l u e ) ; } , //map f u n c t i o n
f u n c t i o n ( key , v a l u e s ) { r e t u r n r e d u c e F u n c t i o n } , { // r e d u c e f u n c t i o n
out : c o l l e c t i o n ,
query : document ,
s o r t : document ,
l i m i t : number
}
)

Comentarios:
• map es una función javascript función que asigna un valor a una clave.
• reduce es una función javascript función que agrupa todos los documentos por su
clave. Algunos parámetros:
◦ out especifica la colección que almacena el resultado de la consulta map-
reduce.
◦ query especifica el criterio, opcional, para la selección de documentos.
◦ sort especifica el criterio, opcional, para la ordenación del resultado
◦ limit especifica el máximo número de elementos devuleltos.
Consideremos la siguiente estructura de documento que almacena usuarios. El docu-
mento almacena user id de un usuario y el estatus de ”post”.

132

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
{
” p o s t t e x t ” : ” t u t o r i a l s p o i n t i s an awesome w e b s i t e f o r t u t o r i a l s ” ,
” u s e r i d ” : ”mark ” ,
” status ”:” active ”
}

La función map asigna a cada clave (autor en este caso) el valor 1.

La función reduce relaciona todos los valores generados para una clave (en este caso
los suma todos).

Usamos una función mapReduce en nuestra colección de ”posts” para seleccionar todos
los ”posts” activos, agruparlos por user name y contar el número de ”posts” por cada
usuario:
>db . p o s t s . mapReduce (
f u n c t i o n ( ) { emit ( t h i s . u s e r i d , 1 ) ; } ,
f u n c t i o n ( key , v a l u e s ) {
v a r count = 0 ;
f o r ( v a r i = 0 ; i < v a l u e s . l e n g t h ; ++){
count += v a l u e s [ i ] ;
}
r e t u r n count } , {
query : { s t a t u s : ” a c t i v e ” } ,
out : ” p o s t t o t a l ”
}
)

Para ver el resultado de esta consulta mapReduce se utiliza el operador find:


>db . p o s t t o t a l . f i n d ( )

La consulta da el siguiente resultado que indica que ambos usuarios tienen dos ”posts”
activos:
{ ” i d ” : ”tom ” , ” v a l u e ” : 2 }
{ ” i d ” : ”mark ” , ” v a l u e ” : 2 }

De forma similar, las consultas MapReduce se pueden utilizar para construir consultas
de agregación complejas. El uso de funciones Javascript functions hace que el uso de
MapReduce sea muy flexible.

4.7. MongoDB y PHP


La interfaz de PHP a MongoDB es muy sencilla. La veremos a través de ejemplos.
Conexión a una base de datos:
<?php
// c o n n e c t t o mongodb
$m = new MongoClient ( ) ;
echo ” C o n n e c t i o n t o d a t a b a s e s u c c e s s f u l l y ” ;

133

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
// s e l e c t a d a t a b a s e
$db = $m−>mydb ;
echo ” Database mydb s e l e c t e d ” ;
?>

Si no existe la base de datos se crea.


Creando una colección:
<?php
// c o n n e c t t o mongodb
$m = new MongoClient ( ) ;
echo ” C o n n e c t i o n t o d a t a b a s e s u c c e s s f u l l y ” ;
// s e l e c t a d a t a b a s e
$db = $m−>mydb ;
echo ” Database mydb s e l e c t e d ” ;
$ c o l l e c t i o n = $db−>c r e a t e C o l l e c t i o n ( ” mycol ” ) ;
echo ” C o l l e c t i o n c r e a t e d s u c c e s s f u l l y ” ;
?>

Insertando un documento:
<?php
// c o n n e c t t o mongodb
$m = new MongoClient ( ) ;
echo ” C o n n e c t i o n t o d a t a b a s e s u c c e s s f u l l y ” ;
// s e l e c t a d a t a b a s e
$db = $m−>mydb ;
echo ” Database mydb s e l e c t e d ” ;
$ c o l l e c t i o n = $db−>mycol ;
echo ” C o l l e c t i o n s e l e c t e d s u c c e s s f u l l y ” ;
$document = a r r a y (
” t i t l e ” => ”MongoDB” ,
” d e s c r i p t i o n ” => ” d a t a b a s e ” ,
” l i k e s ” => 1 0 0 ,
” u r l ” => ” h t t p : / /www. t u t o r i a l s p o i n t . com/mongodb / ” ,
”by ” , ” t u t o r i a l s p o i n t ”
);
$ c o l l e c t i o n −>i n s e r t ( $document ) ;
echo ”Document i n s e r t e d s u c c e s s f u l l y ” ;
?>

Seleccionando todos los documentos de una colección:


<?php
// c o n n e c t t o mongodb
$m = new MongoClient ( ) ;
echo ” C o n n e c t i o n t o d a t a b a s e s u c c e s s f u l l y ” ;
// s e l e c t a d a t a b a s e
$db = $m−>mydb ;
echo ” Database mydb s e l e c t e d ” ;
$ c o l l e c t i o n = $db−>mycol ;
echo ” C o l l e c t i o n s e l e c t e d s u c c e s s f u l l y ” ;
$ c u r s o r = $ c o l l e c t i o n −>f i n d ( ) ;
// i t e r a t e c u r s o r t o d i s p l a y t i t l e o f documents
f o r e a c h ( $ c u r s o r a s $document ) {
echo $document [ ” t i t l e ” ] . ”\n ” ;
}
?>

Actualizando un documento:
<?php
// c o n n e c t t o mongodb

134

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
$m = new MongoClient ( ) ;
echo ” C o n n e c t i o n t o d a t a b a s e s u c c e s s f u l l y ” ;
// s e l e c t a d a t a b a s e
$db = $m−>mydb ;
echo ” Database mydb s e l e c t e d ” ;
$ c o l l e c t i o n = $db−>mycol ;
echo ” C o l l e c t i o n s e l e c t e d s u c c s e s s f u l l y ” ;
// now update t h e document
$ c o l l e c t i o n −>update ( a r r a y ( ” t i t l e ”=>”MongoDB ” ) ,
a r r a y ( ’ $ s e t ’=> a r r a y ( ” t i t l e ”=>”MongoDB T u t o r i a l ” ) ) ) ;
echo ”Document updated s u c c e s s f u l l y ” ;
// now d i s p l a y t h e updated document
$ c u r s o r = $ c o l l e c t i o n −>f i n d ( ) ;
// i t e r a t e c u r s o r t o d i s p l a y t i t l e o f documents
echo ” Updated document ” ;
f o r e a c h ( $ c u r s o r a s $document ) {
echo $document [ ” t i t l e ” ] . ”\n ” ;
}
?>

Borrando datos de un documento:


<?php
// c o n n e c t t o mongodb
$m = new MongoClient ( ) ;
echo ” C o n n e c t i o n t o d a t a b a s e s u c c e s s f u l l y ” ;
// s e l e c t a d a t a b a s e
$db = $m−>mydb ;
echo ” Database mydb s e l e c t e d ” ;
$ c o l l e c t i o n = $db−>mycol ;
echo ” C o l l e c t i o n s e l e c t e d s u c c s e s s f u l l y ” ;
// now remove t h e document
$ c o l l e c t i o n −>remove ( a r r a y ( ” t i t l e ”=>”MongoDB T u t o r i a l ” ) , f a l s e ) ;
echo ” Documents d e l e t e d s u c c e s s f u l l y ” ;
// now d i s p l a y t h e a v a i l a b l e documents
$ c u r s o r = $ c o l l e c t i o n −>f i n d ( ) ;
// i t e r a t e c u r s o r t o d i s p l a y t i t l e o f documents
echo ” Updated document ” ;
f o r e a c h ( $ c u r s o r a s $document ) {
echo $document [ ” t i t l e ” ] . ”\n ” ; }
?>

Nota: el segundo argumento de remove (justOne=false) representa la especificación de borrar


solo un elmento. El valor false del segundo argumento especifica que se borrarán todos los
documentos que satisfagan el criterio. Si fuera true solo se eliminarı́a undo de ellos.

135

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
136

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 5

Datalog

5.1. Bases de datos deductivas


Una base de datos deductiva proporciona mecanismos para que el gestor de bases de
datos pueda inferir información a partir de la ya existente.
Las deducciones se realizan a través de inferencias a partir de reglas y hechos.
Una BD deductiva se compone de:
• Base de datos extensional: información almacenada explı́citamente.
• Base de datos intensional: reglas que permiten inferir nueva información.
El modelo de datos deductivo combina el modelo relacional con la programación lógica.
Caracterı́sticas fundamentales:
• Consultas recursivas.
• Negación estratificada.
El modelo es más expresivo que las BDs relacionales, pero menos que los lenguajes de
programación lógica.
Aplicaciones: minerı́a de datos, inteligencia artificial: sistemas expertos, sistemas ba-
sados en conocimiento, etc.

5.2. Datalog
Datalog es un lenguaje utilizado para representar bases de datos deductivas y realizar
consultas sobre las mismas.
Se puede considerar como una versión restringida (y menos expresiva) que el lenguaje
de programación lógica Prolog.

137

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Existen muchas implementaciones:
• IRIS (http://iris-reasoner.org/)
• 4QL (http://4ql.org/)
• XSB (http://xsb.sourceforge.net/)
Sistema DES (Datalog Educational System):
• Desarrollado por Fernando Sáenz Perez (FdI - UCM), se distribuye bajo la licencia
GNU GPL 3.
• Disponible en http://www.fdi.ucm.es/profesor/fernan/des/
• Soporta tres lenguajes de consulta:
◦ Datalog.
◦ Álgebra relacional.
◦ SQL (con vistas recursivas).
• Es un sistema enfocado en la simplicidad y facilidad de uso, no en la eficiencia.
Comandos básicos del modo consola:

Comando Significado
/consult nombre fichero Carga un fichero (Datalog, RA, o SQL) en la base de datos
/assert hecho o regla Añade un hecho o regla a la base de datos.
/save ddb nombre fichero Exporta la base de datos actual a un fichero Datalog
/cd nombre dir Cambia el directorio actual.
/quit Abandona la consola DES

Nosotros utilizaremos el entorno de programación ACIDE.

5.3. El lenguaje Datalog


Constantes:
• Numéricas: 0, 1.6, 23, -24.5
• Secuencias de caracteres alfanuméricos: pueden contener un carácter de subraya-
do, deben empezar por una letra minúscula. Ejemplos: ejemplo, pepe, esto_es_un_ejemplo
• Es posible utilizar cadenas de caracteres que no cumplan con estas condiciones es-
cribiéndolas entre comillas. Ejemplos: ’David Fernández’, ’_vale’, ’2pasos’
Variables: secuencias de caracteres alfanuméricos que comienzan por letra mayúscula
o carácter de subrayado. Ejemplos: X, Nombre, Apellidos, _Edad, _ (esta última
es una variable anónima).

138

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Átomos:

• Permiten expresar hechos.


• Sı́mbolo de relación aplicado a una serie de variables o constantes. Ejemplo:
cliente(1, ’Javier’, Edad, 2)
• Sintaxis: deben empezar por una letra minúscula.
• Es posible utilizar en los átomos los siguientes operadores: is, =<, <, >, >=, =, \=.
• Se pueden negar átomos: not padre(javier, pepe)

Comentarios:

• De una lı́nea: % Esto es un comentario de una lı́nea


• De varias lı́neas: /* Este puede ocupar varias lı́neas */

Valores null.

Funciones de relación, condiciones, etc.

5.3.1. Hechos y objetivos


La base de datos extensional comprende la información almacenada fı́sicamente en la
base de datos.

Los hechos constituyen la base sobre la cual el sistema realiza inferencias.

Un hecho es un átomo seguido de un punto.

Ejemplo:
c u e n t a ( ’ J a v i e r ’ , ’ Herranz ’ , nomina , 1 2 0 0 0 ) .
c u e n t a ( ’ Ana ’ , ’ Martin ’ , a h o r r o , 2 1 5 0 0 ) .
c u e n t a ( ’ Gerardo ’ , ’ de l a I g l e s i a ’ , a h o r r o , 1 2 0 0 ) .
c u e n t a ( ’ Manuel ’ , ’ Moreno ’ , nomina , 5 0 0 0 ) .
c u e n t a ( ’ Lucia ’ , ’ Rodriguez ’ , a h o r r o , 5 0 0 0 ) .
c u e n t a ( ’ Raquel ’ , ’ V e l a s c o ’ , n u l l , 1 0 0 ) .

El orden de escritura de los hechos no es relevante.

Las consultas sobre la base de datos se realizan mediante objetivos.

Un objetivo es el nombre de una relación aplicado a una serie de argumentos. Cada


uno de ellos puede ser una constante o una variable.

Existen dos ”modos de uso” de los objetivos:

1. Preguntar si un hecho es cierto.


2. Preguntar qué hechos son ciertos.

139

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Ejemplos:
Cuenta de las personas cuyo apellido es ’Moreno’:
DES> c u e n t a ( Nombre , ’ Moreno ’ , Tipo , S a l d o )

Cuentas de tipo nomina:


DES> c u e n t a ( Nombre , A p e l l i d o s , nomina , S a l d o )

Cuentas de tipo nómina con 12000 euros de saldo:


DES> c u e n t a ( Nombre , A p e l l i d o s , nomina , 1 2 0 0 0 )

Cuentas de aquellas personas cuyo nombre y apellidos coincidan:


DES> c u e n t a (X, X, Tipo , S a l d o )

Este conjunto de hechos es lo análogo a una tabla cuenta en base de datos relacionales.

Podemos ver los hechos y los objetivos como funciones booleanas.

5.3.2. Ajustes
Una sustitución es una función que asocia a variables con constantes u otras variables.
Ejemplo:
θ = [X 7→ 2, Y 7→ pepe, Z 7→ V ]

Aplicar una sustitución θ a un literal p consiste en reemplazar todas las variables de p


por los valores especificados por la sustitución.

Si una variable de p no esta vinculada a ningún valor en θ, no se reemplaza.

La aplicación de θ a p se denota por θ(p). Ejemplo: θ(p(X, 13, Z)) = p(2, 13, V ).

Decimos que un átomo q se ajusta a p si existe una sustitución θ tal que θ(p) = q.
Ejemplo: p(lucia, 23, Z) se ajusta a p(X, 23, Y ).

El mecanismo de Datalog busca en la base de datos hechos (que pueden ser inferidos
a partir de otros) de tal forma que se ajusten al objetivo.

5.3.3. Reglas
La base de datos intensional contiene la información que se infiere de la ya existente
en la base de datos.

Se representa mediante un conjunto de reglas.

| {z } : − literal
Sintaxis de las reglas: atomo
| 1 , literal2 , . . . , literaln
{z }
cabeza cuerpo

140

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Si se satisface cada uno de los términos del cuerpo se considera que se satisface la
cabeza. O lo que es lo mismo:

| {z } : − literal
Dada la regla: atomo
| 1 , literal2 , . . . , literaln , si existe una sustitu-
{z }
cabeza cuerpo
ción θ de tal forma que θ(literal1 ), . . . , θ(literaln ) sean conocidos (o deduci-
bles) entonces se puede inferir θ(atomo).
Este es el mecanismo de inferencia de la información.
Ejemplo:
• En una BB.DD. de un banco podemos saber si un cliente es ”preferente” si sabe-
mos cuánto dinero tiene en el banco y le decimos al sistema el valor a partir del
cual ya se considera preferente.
• Base de datos extensional:
c u e n t a ( ’ Pepe ’ , ’ Lopez ’ , a h o r r o , 1 0 0 0 ) .
c u e n t a ( ’ Juan ’ , ’ Lopez ’ , a h o r r o , 5 0 0 ) .
c u e n t a ( ’ Lucas ’ , ’ Lopez ’ , a h o r r o , 4 0 0 0 ) .
c u e n t a ( ’ Antonio ’ , ’ Lopez ’ , a h o r r o , 5 0 0 0 ) .
c u e n t a ( ’ Manuel ’ , ’ Moreno ’ , nomina , 5 0 0 0 ) .

• Base de datos intensional:


c l i e n t e s g o l d (N, A) :− c u e n t a (N, A, a h o r r o , S ) , S > 2 0 0 0 0 .

• Mediante el siguiente objetivo podemos obtener los clientes preferentes:


c l i e n t e s g o l d (X, Y ) .

o consultar si un cliente concreto es preferente o no:


c l i e n t e s g o l d ( ’ Juan ’ , ’ Lopez ’ ) .

Interpretación lógica de las reglas:


Una regla
atomo : −literal1 , literal2 , . . . , literaln
se interpreta como

literal1 ∧ literal2 ∧ . . . ∧ literaln ⇒ atomo

Básicamente representa algo deducible a través de una conjunción de hechos.


Si la regla lógica no posee esta estructura se representa de otra forma equivalente.
Ejemplo:
a ∧ (b ∨ c) ⇒ p
se representa como (calculado a ”ojo”):

141

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
p :− a , b .
p :− a , c .

Para programar cualquier inferencia que podamos pensar es necesario que tenga cierta
forma.

Forma normal conjuntiva:

• Una fórmula está en forma normal conjuntiva (FNC) si es una conjunción de


disyunción de literales:
• Ejemplos:
◦ (¬p ∨ q) ∧ (¬q ∨ p) está en FNC.
◦ (¬p ∨ q) ∧ (¬q → p) no está en FNC.
• G es una forma normal conjuntiva de F si está en forma norma conjuntiva y es
equivalente a F .
◦ Ejemplo: ¬(p ∧ (q → r)) y (¬p ∨ q) ∧ (¬p ∨ ¬r) son equivalentes.

Si cada cláusula tiene exactamente un literal positivo (un literal no negado) puede
transformarse en una regla.

Ejemplo:

• La expresión a ∧ (b ∨ c) → p no está en FNC.


• Transformada a FNC queda: (p ∨ ¬a ∨ ¬b) ∧ (p ∨ ¬a ∨ ¬c)
• Reglas obtenidas:
p :− a , b .
p :− a , c .

Las variables que aparecen en una regla se consideran universalmente cuantificadas en


toda la regla:

• La regla p(X,Y) :- q(X), r(Y) se interpreta como ∀X∀Y (q(X) ∧ r(Y )) ⇒


pp(X, Y ).
• En el ejemplo anterior:
c l i e n t e s g o l d (N, A) :− c u e n t a (N, A, a h o r r o , S ) , S > 2 0 0 0 0 .

se entiende que vale para cualquier N, A y S.


• Además, las variables que aparezcan únicamente en el cuerpo de una regla se
pueden considerar también existencialmente cuantificadas en dicho cuerpo.

Ejemplo:

142

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Decimos que dos clientes son ”amiguetes” si los saldos de su cuenta coinciden.
• Es decir, el cliente con nombre N1 y apellidos A1 es amiguete del cliente con
nombre N2 y apellidos A2 si existe un número S y sendos tipos T1 y T2, tales
que:
◦ La cuenta de N1, A1 tiene tipo T1 y saldo S.
◦ La cuenta de N2, A2 tiene tipo T2 y saldo S.
• Su expresión lógica es:

∀N 1∀A1∀N 2∀A2(∃S∃T 1∃T 2cuenta(N 1, A1, T 1, S)∧cuenta(N 2, A2, T 2, S)) ⇒ amiguetes(N 1, A

es equivalente a:

∀N 1∀A1∀N 2∀A2∀S∀T 1∀T 2cuenta(N 1, A1, T 1, S)∧cuenta(N 2, A2, T 2, S)) ⇒ amiguetes(N 1, A1

que es equivalente a:
% a m i g u e t e s ( Nombre1 , A p e l l i d o s 1 , Nombre2 , A p e l l i d o s 2 )
a m i g u e t e s (N1 , A1 , N2 , A2) :− c u e n t a (N1 , A1 , T1 , S ) , c u e n t a (N2 , A2 , T2 , S ) .

En el ejemplo anterior si hacemos una consulta podemos encontrar algo con el siguiente
aspecto:
DES> a m i g u e t e s (N1 , A1 , N2 , A2)
{
a m i g u e t e s ( ’ Ana ’ , ’ Martin ’ , ’ Ana ’ , ’ Martin ’ ) ,
a m i g u e t e s ( ’ Gerardo ’ , ’ de l a I g l e s i a ’ , ’ Gerardo ’ , ’ de l a I g l e s i a ’ ) ,
a m i g u e t e s ( ’ J a v i e r ’ , ’ Herranz ’ , ’ J a v i e r ’ , ’ Herranz ’ ) ,
a m i g u e t e s ( ’ Lucia ’ , ’ Rodriguez ’ , ’ Lucia ’ , ’ Rodriguez ’ ) ,
a m i g u e t e s ( ’ Lucia ’ , ’ Rodriguez ’ , ’ Manuel ’ , ’ Moreno ’ ) ,
a m i g u e t e s ( ’ Manuel ’ , ’ Moreno ’ , ’ Lucia ’ , ’ Rodriguez ’ ) ,
a m i g u e t e s ( ’ Manuel ’ , ’ Moreno ’ , ’ Manuel ’ , ’ Moreno ’ ) ,
a m i g u e t e s ( ’ Raquel ’ , ’ V e l a s c o ’ , ’ Raquel ’ , ’ V e l a s c o ’ )
}

Cada cliente es amigo de sı́ mismo.

Hemos de completar nuestra regla con restricciones de desigualdad:


% a m i g u e t e s ( Nombre1 , A p e l l i d o s 1 , Nombre2 , A p e l l i d o s 2 )
a m i g u e t e s (N1 , A1 , N2 , A2) :−
c u e n t a (N1 , A1 , T1 , S ) ,
c u e n t a (N2 , A2 , T2 , S ) , N1 \= N2 .
a m i g u e t e s (N1 , A1 , N2 , A2) :−
c u e n t a (N1 , A1 , T1 , S ) ,
c u e n t a (N2 , A2 , T2 , S ) , A1 \= A2 .

Cuestión: ¿por qué dos desigualdades y no una?.

En el ejemplo anterior observamos que T1 y T2 no juegan ningún papel. Es preciso


incluirlas exclusivamente por sintaxis: cuenta debe tener cuatro argumentos.

Es posible sustituir estas variables (cuyo nombre es irrelevante) por variables anónimas.

143

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Las variables anónimas ajustan con cualquier elemento.
El código anterior queda con la siguiente forma:
% a m i g u e t e s ( Nombre1 , A p e l l i d o s 1 , Nombre2 , A p e l l i d o s 2 )
a m i g u e t e s (N1 , A1 , N2 , A2) :−
c u e n t a (N1 , A1 , , S) ,
c u e n t a (N2 , A2 , , S ) , N1 \= N2 .
a m i g u e t e s (N1 , A1 , N2 , A2) :−
c u e n t a (N1 , A1 , , S) ,
c u e n t a (N2 , A2 , , S ) , A1 \= A2 .

Este código es más claro. Permite centrar la atención en las variables relevantes.
El operador punto y coma permite expresar la disyunción entre dos literales. Esto
permite expresar las dos reglas en una:
% a m i g u e t e s ( Nombre1 , A p e l l i d o s 1 , Nombre2 , A p e l l i d o s 2 )
a m i g u e t e s (N1 , A1 , N2 , A2) :−
c u e n t a (N1 , A1 , , S) ,
c u e n t a (N2 , A2 , , S ) , (N1 \= N2 ; A1 \= A2 ) .

Es innecesario pero aporta legibilidad.

5.3.4. Reglas automáticas


Es posible expresar una consulta mediante una conjunción de literales.
Ejemplo:
c u e n t a ( , A, nomina , S ) , S > 10000

con este objetivo crea la siguiente regla (y el objetivo correspondiente):


answer (A, S ) :− c u e n t a ( , A, nomina , S ) , S > 10000

El aspecto de la consulta podrı́a ser el siguiente:


DES> c u e n t a ( , A, nomina , S ) , S > 10000
Info : Processing :
answer (A, S ) :−
c u e n t a ( , A, nomina , S ) ,
S >10000.
{
answer ( ’ Herranz ’ , 1 2 0 0 0 ) ,
answer ( ’ Martin ’ , 2 1 5 0 0 )
}

Si sólo se desean conocer los apellidos, pueden utilizarse variables anónimas:


DES> c u e n t a ( , A, nomina , S ) , S > 10000
Info : Processing :
answer (A) :−
c u e n t a ( , A, nomina , S ) ,
S >10000.
{
answer ( ’ Herranz ’ ) ,
answer ( ’ Martin ’ )
}

144

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
5.3.5. Mecanismo de cómputo de Datalog
Consideremos el siguiente código en Datalog:
edad ( pepe , 1 3 ) .
edad ( juan , 1 8 ) .
edad ( maria , 2 3 ) .
edad ( ana , 1 9 ) .
amigos ( pepe , j u a n ) .
amigos ( juan , ana ) .
amigos ( maria , j u a n ) .
a m i g o s a d u l t o s (X,Y): − amigos (X,Y) , edad (X,V) ,V>17 , edad (Y,W) ,W>17.

El usuario lanza un objetivo. Consideremos los siguientes casos:

1. edad(pepe,13): el sistema no puede buscar ningún ajuste porque el objetivo no tiene


variables. Solo mira si ese hecho está o no.

2. edad(pepe,X): el sistema busca ajustes que hagan al objetivo cierto. En este caso
θ = [X 7→ 13]. También podı́amos haber enviado el objetivo edad(X,13). En tal caso
θ = [X 7→ pepe].

3. edad(X,Y): el sistema busca ajustes que lo hagan cierto. Hay varios, por ejemplo:
θ = [X 7→ maria, Y 7→ 23].

4. amigos adultos(juan,ana): el sistema plantea el ajuste θ = [X 7→ juan, Y 7→ ana] a


cada uno de los literales de la regla:

Como es cierto amigos(juan,ana) intenta comprobar el siguiente literal. Si hubie-


ra sido falso habrı́a interrumpido el procesamiento y determinado que amigos adultos(juan,ana)
es falso.
A continuación se lanza edad(juan,V). El objetivo busca ajustes que hagan el
literal cierto (en este caso θ = [V 7→ 18]).
Pasa al siguiente literal V>17 (con V=18 ).
El proceso continua hasta que todos los literales de la regla sean ciertos o alguno
sea falso.

5. amigos adultos(W,Z): serı́a similar con la diferencia de que se plantean más ajustes.

5.3.6. Programando con Datalog


Programar con Datalog consiste en representar la información con funciones booleanas.

Hechos y reglas son funciones booleanas. Un hecho es una función booleana constante.

• Dado un conjunto de objetos, si queremos representar una propiedad booleana de


estos objetos utilizamos un función lógica con un argumento.

145

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Si se trata de propiedades no booleanas (existe más de un valor), utilizamos una
función lógica de dos argumentos (en el que el segundo argumento será el valor
de la propiedad). Por ejemplo: edad(juan,18).
• Si estamos representando representaciones entre objetos utilizaremos funciones
booleanas con dos argumentos (si la relación es booleana, o más si no lo es).

5.3.7. Reglas recursivas


Las reglas recursivas es uno de los mecanismos más expresivos de Datalog.

Es la caracterı́stica que hace que Datalog sea muy potente en términos de expresividad.

Permiten la especificación de relaciones complejas.

Árboles genealógicos

Consideremos la genealogı́a de la figura 5.1.

Podemos establecer los siguientes hechos básicos:


hombre ( abraham ) .
hombre ( c l a n c y ) .
hombre ( h e r b e r t ) .
hombre ( homer ) .
hombre ( b a r t ) .
mujer ( mona ) .
mujer ( j a c k i e ) .
mujer ( marge ) .
mujer ( p a t t y ) .
mujer ( selma ) .
mujer ( l i s a ) .
mujer ( maggie ) .
mujer ( l i n g ) .

p r o g e n i t o r ( abraham , h e r b e r t ) .
p r o g e n i t o r ( abraham , homer ) .
p r o g e n i t o r ( mona , homer ) .
p r o g e n i t o r ( c l a n c y , marge ) .
p r o g e n i t o r ( j a c k i e , marge ) .
progenitor ( clancy , patty ) .
progenitor ( jackie , patty ) .
p r o g e n i t o r ( c l a n c y , selma ) .
p r o g e n i t o r ( j a c k i e , selma ) .
p r o g e n i t o r ( homer , b a r t ) .
p r o g e n i t o r ( marge , b a r t ) .
p r o g e n i t o r ( homer , l i s a ) .
p r o g e n i t o r ( marge , l i s a ) .
p r o g e n i t o r ( homer , maggie ) .
p r o g e n i t o r ( marge , maggie ) .
p r o g e n i t o r ( selma , l i n g ) .

Podemos expresar las siguientes reglas no recursivas:

146

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 5.1: Datalog y los Simpson

147

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Reglas sobre la paternidad:
◦ Semántica de padre(X,Y): X es padre de Y. Semántica de madre(X,Y): X es
madre de Y.
◦ En Datalog se expresa:
padre (X,Y) :− p r o g e n i t o r (X,Y) , hombre (X ) .
madre (X,Y) :− p r o g e n i t o r (X,Y) , mujer (X ) .

• Relaciones de hermandad:
◦ Semántica de hermanos(X,Y): X es hermano o hermana de Y.
◦ En Datalog se expresa:
hermanos (X,Y) :− p r o g e n i t o r ( Z ,X) , p r o g e n i t o r ( Z ,Y) , X \= Y.

• De modo similar podemos indicarle al sistema en qué consiste ser ”tı́a” y ”abuela”:
t i a (X,Y) :− mujer (X) , hermanos (X, Z ) , p r o g e n i t o r ( Z ,Y ) .
a b u e l a (X,Y) :− madre (X, Z ) , p r o g e n i t o r ( Z ,Y ) .

Regla recursiva:

• Una regla remite a otra del mismo tipo.


• Ejemplo:
a n t e c e s o r (X,Y) :− p r o g e n i t o r (X,Y ) .
a n t e c e s o r (X,Y) :− p r o g e n i t o r (X, Z ) , a n t e c e s o r ( Z ,Y ) .

produce el siguiente resultado:


DES> a n t e c e s o r (X,Y)
{
a n t e c e s o r ( abraham , b a r t ) ,
a n t e c e s o r ( abraham , h e r b e r t ) ,
a n t e c e s o r ( abraham , homer ) ,
a n t e c e s o r ( abraham , l i s a ) ,
a n t e c e s o r ( abraham , maggie ) ,
antecesor ( clancy , bart ) ,
antecesor ( clancy , l i n g ) ,
...
}

• Otro ejemplo:
◦ Ser de la misma generación mg(X,Y).
◦ Se codifica:
mg(X,Y) :− hermanos (X,Y ) .
mg(X,Y) :− p r o g e n i t o r ( Z ,X) , p r o g e n i t o r (V,Y) , mg( Z ,V ) .

produce el siguiente resultado:

148

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
DES> mg(X,Y)
{
mg( ba r t , l i n g ) ,
mg( ba r t , l i s a ) ,
mg( ba r t , maggie ) ,
mg( h e r b e r t , homer ) ,
mg( homer , h e r b e r t ) ,
mg( l i n g , b a r t ) ,
...
}

Caminos en un grafo
Representar un grafo es muy sencillo con Datalog: simplemente hay que especificar las
aristas.

Ejemplo:
arista (a , b ).
a r i s t a (b , d ) .
a r i s t a (d , c ) .
a r i s t a (d , e ) .
arista (e , f ).
arista (f , c ).
arista (f ,g ).
arista (g , d ).

Podemos introducir la idea de ”alcanzabilidad ” a través del predicado camino(X,Y)


(existe camino entre X e Y).

Codificado:
camino (X,Y) :− a r i s t a (X,Y ) .
camino (X,Y) :− a r i s t a (X, Z ) , camino ( Z ,Y ) .

puede producir el siguiente resultado:


DES> camino ( e , Z ) .
{
camino ( e , c ) ,
camino ( e , d ) ,
camino ( e , e ) ,
camino ( e , f ) ,
camino ( e , g )
}

Podemos especificar la idea de ciclo:


c i c l o ( Z ) :− camino ( Z , Z ) .

puede producir el siguiente resultado:


DES> c i c l o ( Z )
{
c i c l o (d) ,
ciclo (e) ,

149

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
ciclo ( f ) ,
ciclo (g)
}

5.4. Datalog avanzado


5.4.1. Reuniones
Datalog posee mecanismos para combinar tablas de forma similar al del esquema rela-
cional.

Permite los siguientes tipos de reuniones:

• Natural join.
• Left outer join.
• Right outer join.
• Full outer join.

Reuniones internas
No existe ninguna función especial para representar reuniones internas.

Pueden expresarse de manera sencilla mediante reglas.

Ejemplo:
% c l i e n t e ( Id , Nombre , A p e l l i d o s )
c l i e n t e ( 1 , ’ J a v i e r ’ , ’ Herranz ’ ) .
c l i e n t e ( 2 , ’ Ana ’ , ’ Martin ’ ) .
c l i e n t e ( 3 , ’ Gerardo ’ , ’ de l a I g l e s i a ’ ) .
c l i e n t e ( 4 , ’ Manuel ’ , ’ Moreno ’ ) .
c l i e n t e ( 5 , ’ Lucia ’ , ’ Rodriguez ’ ) .
c l i e n t e ( 6 , ’ Raquel ’ , ’ V e l a s c o ’ ) .
c l i e n t e ( 7 , ’ I g n a c i o ’ , ’ Martin ’ ) .

% c u e n t a ( Id , Tipo , Saldo , I d T i t u l a r )
c u e n t a ( 1 , nomina , 1 2 0 0 0 , 1 ) .
cuenta ( 2 , ahorro , 15000 , 1 ) .
c u e n t a ( 3 , nomina , 2 1 5 0 0 , 2 ) .
cuenta ( 4 , ahorro , 1200 , 3 ) .
c u e n t a ( 5 , nomina , 5 0 0 0 , 4 ) .
cuenta ( 6 , ahorro , 5000 , 5 ) .
cuenta (7 , null , 100 , 6 ) .
c u e n t a ( 8 , nomina , 1 5 0 0 , 6 ) .

La siguiente regla implementa una reunión interna de ambas ”tablas”:


% c u e n t a ( Id , Tipo , Saldo , I d T i t u l a r )
c l i e n t e s c u e n t a s (N, A, T, S ) :−
c l i e n t e ( I d C l i e n t e , N, A) , c u e n t a ( , T, S , I d C l i e n t e ) .

150

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Un posible resultado:
% c u e n t a ( Id , Tipo , Saldo , I d T i t u l a r )
DES> c l i e n t e s c u e n t a s (N, A, T, S )
{
c l i e n t e s c u e n t a s ( ’ Ana ’ , ’ Martin ’ , nomina , 2 1 5 0 0 ) ,
c l i e n t e s c u e n t a s ( ’ Gerardo ’ , ’ de l a I g l e s i a ’ , a h o r r o , 1 2 0 0 ) ,
c l i e n t e s c u e n t a s ( ’ J a v i e r ’ , ’ Herranz ’ , a h o r r o , 1 5 0 0 0 ) ,
c l i e n t e s c u e n t a s ( ’ J a v i e r ’ , ’ Herranz ’ , nomina , 1 2 0 0 0 ) ,
c l i e n t e s c u e n t a s ( ’ Lucia ’ , ’ Rodriguez ’ , a h o r r o , 5 0 0 0 ) ,
c l i e n t e s c u e n t a s ( ’ Manuel ’ , ’ Moreno ’ , nomina , 5 0 0 0 ) ,
c l i e n t e s c u e n t a s ( ’ Raquel ’ , ’ V e l a s c o ’ , nomina , 1 5 0 0 ) ,
c l i e n t e s c u e n t a s ( ’ Raquel ’ , ’ V e l a s c o ’ , n u l l , 1 0 0 )
}

Las funciones is null e is not null permiten comprobar si un atributo es nulo (no
utilizar en comparaciones):
DES> c l i e n t e s c u e n t a s (N, A, T, S ) , i s n o t n u l l (T)
Info : Processing :
answer (N, A, T, S ) :−
c l i e n t e s c u e n t a s (N, A, T, S ) ,
i s n o t n u l l (T ) .
{
answer ( ’ Ana ’ , ’ Martin ’ , nomina , 2 1 5 0 0 ) ,
answer ( ’ Gerardo ’ , ’ de l a I g l e s i a ’ , a h o r r o , 1 2 0 0 ) ,
answer ( ’ J a v i e r ’ , ’ Herranz ’ , a h o r r o , 1 5 0 0 0 ) ,
answer ( ’ J a v i e r ’ , ’ Herranz ’ , nomina , 1 2 0 0 0 ) ,
answer ( ’ Lucia ’ , ’ Rodriguez ’ , a h o r r o , 5 0 0 0 ) ,
answer ( ’ Manuel ’ , ’ Moreno ’ , nomina , 5 0 0 0 ) ,
answer ( ’ Raquel ’ , ’ V e l a s c o ’ , nomina , 1 5 0 0 )
}

Reuniones externas
Reunión externa por la izquierda (left outer join): lj(RelIzqda, RelDcha, CondicionJoin)

Reunión externa por la derecha (right outer join): rj(RelIzqda, RelDcha, CondicionJoin)

Reunión externa completa (full outer outer join): fj(RelIzqda, RelDcha, CondicionJoin)

Ejemplo:
c l i e n t e s c u e n t a s l e f t (N, A, T, S ) :−
l j ( c l i e n t e ( Id , N, A) , c u e n t a ( , T, S , IdC ) , I d = IdC ) .

DES> c l i e n t e s c u e n t a s l e f t (N, A, T, S )
{
c l i e n t e s c u e n t a s l e f t ( ’ Ana ’ , ’ Martin ’ , nomina , 2 1 5 0 0 ) ,
c l i e n t e s c u e n t a s l e f t ( ’ Gerardo ’ , ’ de l a I g l e s i a ’ , a h o r r o , 1 2 0 0 ) ,
c l i e n t e s c u e n t a s l e f t ( ’ I g n a c i o ’ , ’ Martin ’ , n u l l , n u l l ) ,
c l i e n t e s c u e n t a s l e f t ( ’ J a v i e r ’ , ’ Herranz ’ , a h o r r o , 1 5 0 0 0 ) ,
c l i e n t e s c u e n t a s l e f t ( ’ J a v i e r ’ , ’ Herranz ’ , nomina , 1 2 0 0 0 ) ,
c l i e n t e s c u e n t a s l e f t ( ’ Lucia ’ , ’ Rodriguez ’ , a h o r r o , 5 0 0 0 ) ,
c l i e n t e s c u e n t a s l e f t ( ’ Manuel ’ , ’ Moreno ’ , nomina , 5 0 0 0 ) ,
c l i e n t e s c u e n t a s l e f t ( ’ Raquel ’ , ’ V e l a s c o ’ , nomina , 1 5 0 0 ) ,
c l i e n t e s c u e n t a s l e f t ( ’ Raquel ’ , ’ V e l a s c o ’ , n u l l , 1 0 0 )
}

151

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
5.4.2. Agrupaciones
Instrucción básica: group by(Rel, VarsGroup, CondsGroup)
Realiza un agrupamiento de los resultados de la relación Rel, reuniendo aquellos que
coincidan en las variables indicadas en VarsGroup. En CondsGroup podemos utilizar
funciones de agregación y ligar sus valores a variables externas.
VarsGroup es una lista [Var-1, ..., Var-n]
CondsGroup se compone de una o varias condiciones Var = Exp, donde Exp puede
contener funciones de agregación.
Ejemplo:
c l i e n t e s c u e n t a s l e f t ( Id , N, A, T, S ) :−
l j ( c l i e n t e ( Id , N, A) , c u e n t a ( , T, S , IdC ) , I d = IdC ) .

c l i e n t e s n u m c u e n t a s (N, A, NumCuentas ) :−
group by (
c l i e n t e s c u e n t a s l e f t ( Id , N, A, T, S ) ,
[ Id , N, A] ,
NumCuentas = count ( S )
).

DES> c l i e n t e s n u m c u e n t a s (N, A, NumCuentas )


{
c l i e n t e s n u m c u e n t a s ( ’ Ana ’ , ’ Martin ’ , 1 ) ,
c l i e n t e s n u m c u e n t a s ( ’ Gerardo ’ , ’ de l a I g l e s i a ’ , 1 ) ,
c l i e n t e s n u m c u e n t a s ( ’ I g n a c i o ’ , ’ Martin ’ , 0 ) ,
c l i e n t e s n u m c u e n t a s ( ’ J a v i e r ’ , ’ Herranz ’ , 2 ) ,
c l i e n t e s n u m c u e n t a s ( ’ Lucia ’ , ’ Rodriguez ’ , 1 ) ,
c l i e n t e s n u m c u e n t a s ( ’ Manuel ’ , ’ Moreno ’ , 1 ) ,
c l i e n t e s n u m c u e n t a s ( ’ Raquel ’ , ’ V e l a s c o ’ , 1 )
}

5.4.3. Funciones de agregación


count(X): Número de resultados en las que X es distinto de null.
count: Número de resultados.
sum(X) / times(X): Suma/multiplicación de los valores de X.
avg(X): Media aritmética de los valores de X.
min(X) / max(X): Mı́nimo/máximo de los valores de X.
Ejemplo:
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l (N, A, NumCuentas , S a l d o T o t a l ) :−
group by (
c l i e n t e s c u e n t a s l e f t ( Id , N, A, T, S ) ,
[ Id , N, A] ,
( NumCuentas = count (T) , S a l d o T o t a l = sum ( S ) )
).

152

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
DES> c l i e n t e s n u m c u e n t a s y s a l d o t o t a l (N, A, NumCuentas , S a l d o T o t a l )
{
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ Ana ’ , ’ Martin ’ , 1 , 2 1 5 0 0 ) ,
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ Gerardo ’ , ’ de l a I g l e s i a ’ , 1 , 1 2 0 0 ) ,
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ I g n a c i o ’ , ’ Martin ’ , 0 , n u l l ) ,
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ J a v i e r ’ , ’ Herranz ’ , 2 , 2 7 0 0 0 ) ,
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ Lucia ’ , ’ Rodriguez ’ , 1 , 5 0 0 0 ) ,
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ Manuel ’ , ’ Moreno ’ , 1 , 5 0 0 0 ) ,
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ Raquel ’ , ’ V e l a s c o ’ , 1 , 1 6 0 0 )
}

Se pueden añadir condiciones sobre la agregación:


c l i e n t e s n u m c u e n t a s y s a l d o t o t a l (N, A, NumCuentas , S a l d o T o t a l ) :−
group by (
c l i e n t e s c u e n t a s l e f t ( Id , N, A, T, S ) ,
[ Id , N, A] ,
( NumCuentas = count (T) , S a l d o T o t a l = sum ( S ) )
) , SaldoTotal > 10000.

DES> c l i e n t e s n u m c u e n t a s y s a l d o t o t a l (N, A, NumCuentas , S a l d o T o t a l )


{
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ Ana ’ , ’ Martin ’ , 1 , 2 1 5 0 0 ) ,
c l i e n t e s n u m c u e n t a s y s a l d o t o t a l ( ’ J a v i e r ’ , ’ Herranz ’ , 2 , 2 7 0 0 0 )
}

5.4.4. Aritmética
Pueden realizarse operaciones aritméticas mediante el operador is.
Sintaxis: X is Expresión
Ejemplo:
% a r t i c u l o ( Nombre , Cantidad , P r e c i o U n i d a d )
a r t i c u l o ( ’ Aceite ’ , 2 , 6 . 9 5 ) .
a r t i c u l o ( ’ Azúcar ’ , 1 , 1 . 2 0 ) .
a r t i c u l o ( ’ Leche ’ , 3 , 0 . 9 0 ) .

t o t a l a r t i c u l o ( Nombre , T o t a l ) :−
a r t i c u l o ( Nombre , Cantidad , P r e c i o U n i d a d ) ,
T o t a l i s Cantidad ∗ P r e c i o U n i d a d .
t o t a l c o m p r a ( Suma ) :−
g r o u p b y ( t o t a l a r t i c u l o ( , T o t a l ) , [ ] , Suma = sum ( T o t a l ) ) .

Algunos resultados posibles:


DES> t o t a l a r t i c u l o (N, T)
{
t o t a l a r t i c u l o ( ’ Aceite ’ , 1 3 . 9 ) ,
t o t a l a r t i c u l o ( ’ Azúcar ’ , 1 . 2 ) ,
t o t a l a r t i c u l o ( ’ Leche ’ , 2 . 7 )
}

DES> t o t a l c o m p r a (T ) .
{
total compra (17.8)
}

153

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Los argumentos de las relaciones sólo pueden ser constantes o variables; nunca expre-
siones. El siguiente código es incorrecto:
t o t a l a r t i c u l o ( Nombre , Cantidad ∗ P r e c i o U n i d a d ) :−
a r t i c u l o ( Nombre , Cantidad , P r e c i o U n i d a d ) .

Las variables que aparezcan en las expresiones aritméticas han de estar ligadas a algún
valor. El siguiente código es incorrecto:
p (X, Z ) :− Z i s X+Y.

La Y no está ligada.

El siguiente código mira la distancia entre dos nodos de un grafo:

• No puede tener ciclos.


• Es preciso indicar la lista de los nodos.
camino (X, X, 0 ) .
camino (X, Y, D i s t ) :−
camino (X, Z , D i s t 1 ) ,
a r i s t a ( Z ,Y) , D i s t i s D i s t 1 + 1 .

Se puede calcular la distancia de un nodo a los demás:


DES> camino ( a , Y,D)
{
camino ( a , a , 0 ) ,
camino ( a , b , 1 ) ,
camino ( a , c , 3 ) ,
camino ( a , c , 5 ) ,
camino ( a , d , 2 ) ,
camino ( a , e , 3 ) ,
camino ( a , f , 4 ) ,
camino ( a , g , 5 )
}

Cuestión: ¿qué pasarı́a si tuviera ciclos?.

Otro ejemplo. El factorial:


% f a c t o r i a l (N, Fact )
factorial (0 ,1).
f a c t o r i a l (N, F) :−
N > 0 , NAux i s N−1 , f a c t o r i a l (NAux , FAux ) , F i s N ∗ FAux .

5.4.5. Negación
En Datalog se considera que una relación no se cumple si no se puede deducir de la ya
existente en la base de datos.

La negación se identifica con la ausencia de información.

154

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
El operador not permite comprobar si una determinada relación no es cierta.

Ejemplo:
c l i e n t e s s i n c u e n t a a h o r r o (N, A) :−
c l i e n t e s c u e n t a s (N, A, , ),
not c l i e n t e s c u e n t a s (N, A, a h o r r o , ).

DES> c l i e n t e s s i n c u e n t a a h o r r o (N,A ) .
{
c l i e n t e s s i n c u e n t a a h o r r o ( ’ Ana ’ , ’ Martin ’ ) ,
c l i e n t e s s i n c u e n t a a h o r r o ( ’ Manuel ’ , ’ Moreno ’ ) ,
c l i e n t e s s i n c u e n t a a h o r r o ( ’ Raquel ’ , ’ V e l a s c o ’ )
}

Negación estratificada
La negación puede ser problemática cuando aparece en relaciones definidas recursiva-
mente.

Las mayorı́a de implementaciones Datalog utiliza una versión restringida de la negación,


conocida como negación estratificada.

Una definición de relación, que contenga una negación, se considera segura si no aparece
en un camino de cómputo recursivo.

Comprobación de la corrección:

• A partir del conjunciones de las reglas construimos un grafo de dependencias.


• Si un predicado q forma parte de la definición de otro p trazaremos una arista de
q a p.
• Si el predicado q aparece sin negar le asignaremos a la arista de q a p una etiqueta
+.
• Si el predicado q aparece negado le asignaremos a la arista de q a p una etiqueta
−.
• El código se considera seguro si no existe ningún ciclo con una etiqueta −.

El siguiente código es seguro:


a :− b, c.
b :− not c , a .
c :− d.
d :− c.

Mediante el comando /pdg de DES se puede obtener una representación del grafo de
dependencias.

155

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
5.5. Más allá de Datalog: Prolog
5.5.1. ¿Qué es la programación declarativa?
La idea clave consiste en describir relaciones entre los datos sin concretar los algoritmos.

Se describe qué debe ser computado y no cómo debe ser computado.

En consecuencia no hay estructuras de control. Tampoco hay variables mutables, es


decir, no hay asignación:

• ¿Qué es una variable mutable?.


• Es una variable ”normal”: a lo largo de la ejecución de un programa puede tener
varios valores.
• En Prolog cuando una variable toma un valor ya no puede ser modificado.

Contrasta con la programación imperativa (o procedimental), en la que hay un flujo


de programa determinado por las acciones del mismo.

Ventajas de la programación declarativa:

• Permite al programador concentrarse en la formulación del problema y lo libera


del control del algoritmo.
• Los programas son más fáciles de manejar, transformar y verificar.

Hay multitud de lenguajes declarativos o con inspiración declarativa (como SQL).

El paradigma de programación declarativa se compone, a su vez, de dos paradigmas:

• Programación lógica.
• Programación funcional.

Existen dos grandes familias de lenguajes declarativos:

• Lenguajes lógicos, cuyo representante más conocido es Prolog.


• Lenguajes funcionales, cuyo representante más puro es Haskell.

En la práctica:

• La eficiencia también importa: la ausencia de control es relativa y el programador


no queda completamente liberado de la algoritmia.
• Pero el programador trabaja a un nivel de abstracción superior y queda liberado de
fuentes comunes de error: uso de punteros, pasos por referencia/valor, condiciones
de parada de bucles, etc.

156

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Estos estilos de programación enriquecen considerablemente las habilidades del
programador, aunque posteriormente utilice algún lenguaje imperativo.
• El diseño declarativo es normalmente más conciso, elegante, claro y fácil de man-
tener.

Algunas caracterı́sticas de Prolog:

• Sintaxis: subconjunto de la lógica de primer orden. Aunque la sintaxis de Datalog


está basada en la de Prolog, éste último es más expresivo (permite utilizar listas).
• Semántica declarativa: basada en la propia lógica.
• Semántica operacional: Unificación + resolución SLD (mecanismo de funciona-
miento).
• Enriquecido con multitud de librerı́as.

5.5.2. Prolog: listas


Existe muchos interpretes/compiladores de Prolog. Nosotros utilizaremos SWI-prolog.

Descargable en http://www.swi-prolog.org/

La complejidad de Prolog desborda los lı́mites de esta asignatura. Nos limitaremos a


estudiar las listas.

Listas en Prolog:

Una lista es una secuencia ordenada de elementos que puede tener cualquier longitud.

Los elementos de una lista pueden ser cualquier término (constantes, variables, estruc-
turas) u otras listas.

Una lista puede definirse recursivamente como:

• Una lista vacı́a: [] o


• Una lista con dos componentes:
◦ Cabeza: primer elemento de la lista.
◦ Cola: resto de la lista.
• Una lista con este formato se representa mediante [X|Y]:
◦ Donde X es un elemento e Y es una lista.
◦ Una lista con un solo elemento también se puede representar como [X|Y] (si
Y=[]) o [X].
• Ejemplos:

157

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Lista Cabeza Cola
[a,b,c] a [b,c]
[a] a []
[] no tiene no tiene
[[a,b],c] [a,b] [c]
[a,[b,c]] a [[b,c]]
[a,[b,c],d] a [[b,c],d]
[a+b,c+d] a+b [c+d]
Nota: + es el operador concatenación.

Los siguientes ejemplos muestran funciones lógicas para saber si un elemento dado está
o no en una lista:
miembro (E , L) :− L=[X|Y] , X=E .
miembro (E , L) :− L=[X|Y] , miembro (E ,Y ) .

miembro (E , [ X|Y ] ) :− X=E .


miembro (E , [ X|Y ] ) :− miembro (E ,Y ) .

miembro (X , [ X|Y ] ) .
miembro (E , [ X|Y ] ) :− miembro (E ,Y ) .

miembro (X , [ X| ] ) .
miembro (X , [ |Y ] ) :− miembro (X,Y ) .

Otras operaciones con listas:


/∗ n e l ( L i s t a ,N) <− e l numero de e l e m e n t o s de l a l i s t a L i s t a e s N ∗/
nel ( [ ] , 0 ) .
n e l ( [ X|Y] ,N) :− n e l (Y,M) ,
N i s M+1.

/∗ e s l i s t a ( L i s t a ) <− L i s t a e s una l i s t a ∗/
es lista ([]).
es lista ([ | ]).

/∗ c o n c a t e n a (A, B, C) <− c o n c a t e n a c i ó n de l a s l i s t a s A y B
dando l u g a r a l a l i s t a C ∗/
c o n c a t e n a (A, B, C) :− A= [ ] , C=B .
c o n c a t e n a (A, B, C) :− A=[X|D] , c o n c a t e n a (D, B, E) , C=[X| E ] .

o bien:
/∗ c o n c a t e n a ( L1 , L2 , L3 ) <− c o n c a t e n a c i ó n de l a s l i s t a s L1 y L2
dando l u g a r a l a l i s t a L3 ∗/
concatena ( [ ] , L , L ) .
c o n c a t e n a ( [ X| L1 ] , L2 , [ X| L3 ] ) :− c o n c a t e n a ( L1 , L2 , L3 ) .

/∗ u l t i m o ( Elem , L i s t a ) <− Elem e s e l u l t i m o e l e m e n t o de L i s t a ∗/


u l t i m o (X , [ X ] ) .
u l t i m o (X , [ |Y ] ) :− u l t i m o (X,Y ) .

158

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
/∗ i n v e r s a ( L i s t a , I n v e r ) <− I n v e r e s l a i n v e r s a de l a l i s t a L i s t a ∗/
inversa ( [ ] , [ ] ) .
i n v e r s a ( [ X|Y] , L) :− i n v e r s a (Y, Z ) ,
c o n c a t e n a ( Z , [ X] , L ) .

/∗ b o r r a r ( Elem , L1 , L2 ) <− s e b o r r a e l e l e m e n t o Elem de l a l i s t a L1


o b t e n i é n d o s e l a l i s t a L2 ∗/
b o r r a r (X , [ X|Y] ,Y ) .
b o r r a r (X , [ Z | L ] , [ Z |M] ) :− b o r r a r (X, L ,M) .

/∗ s u b c o n j u n t o ( L1 , L2 ) <− l a l i s t a L1 e s un s u b c o n j u n t o de l i s t a L2 ∗/
s u b c o n j u n t o ( [ X|Y] , Z ) :− miembro (X, Z ) ,
s u b c o n j u n t o (Y, Z ) .
subconjunto ( [ ] ,Y) .

/∗ i n s e r t a r ( Elem , L1 , L2 ) <− s e i n s e r t a e l e l e m e n t o Elem en l a l i s t a L1


o b t e n i é n d o s e l a l i s t a L2 ∗/
i n s e r t a r (E , L , [ E | L ] ) .
i n s e r t a r (E , [ X|Y ] , [ X| Z ] ) :− i n s e r t a r (E , Y, Z ) .

/∗ p e r m u t a c i o n ( L1 , L2 ) <− l a l i s t a L2 e s una p e r m u t a c i ó n de l i s t a L1 ∗/
permutacion ( [ ] , [ ] ) .
p e r m u t a c i ó n ( [ X|Y] , Z ) :− p e r m u t a c i o n (Y, L ) ,
i n s e r t a r (X, L , Z ) .

Recursión:

Veamos a través de un ejemplo cómo funcional el mecanismo de cómputo de Prolog.

Consideremos el predicado nel (calcula el número de elementos de una lista).

En la recursión encontramos dos partes:

1. Descendemos y construimos el árbol hasta encontrar el valor que unifica con la


condición de parada.
2. Ascendemos por el árbol asignando valores a las variables que tenı́amos pendientes
en las sucesivas llamadas.

Quicksort con Prolog:


partition ( , [] , [] , [ ] ) .
p a r t i t i o n (P , [X| Xs ] , [X| Ls ] , Gs ) :− X =< P , p a r t i t i o n (P , Xs , Ls , Gs ) .
p a r t i t i o n (P , [X| Xs ] , Ls , [X| Gs ] ) :− X > P , p a r t i t i o n (P , Xs , Ls , Gs ) .

qsort ( [ ] , [ ] ) .
q s o r t ( [ X| Xs ] , Zs ) :−
p a r t i t i o n (X, Xs , Ls , Gs ) ,
q s o r t ( Ls , L s S o r t ) ,
q s o r t ( Gs , GsSort ) ,
append ( LsSort , [X| GsSort ] , Zs ) .

?− q s o r t ( [ 6 , 1 , 4 , 9 , 2 ] , Xs ) .
Xs = [ 1 , 2 , 4 , 6 , 9 ] .

159

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 5.2: Secuencia de llamadas para el cómputo de la función nel

160

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Parte III

Funcionamiento interno de un SGBD

161

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 6

Almacenamiento de datos e ı́ndices

6.1. Almacenamiento
En el nivel conceptual o lógico hemos las bases de datos en el modelo relacional como
conjuntos de tablas. En realidad, el modelo lógico de las bases de datos es el nivel
adecuado en el que se deben centrar los usuarios.

El objetivo de un sistema de bases de datos es simplificar y facilitar el acceso a los


datos. Los usuarios del sistema no deben someterse sin necesidad alguna a la carga de
los detalles fı́sicos del desarrollo del sistema.

En la mayor parte de los sistemas informáticos hay varios tipos de almacenamientos


de datos. Estos medios de almacenamiento se clasifican según la velocidad con la que
se puede acceder a los datos y por la fiabilidad del medio principalmente (también por
el coste económico: adquisición del medio por unidad de dato).

Diversos tipos de medios de almacenamiento:

• Caché:
◦ Es la forma de almacenamiento más rápida y costosa. Por lo tanto, es pequeña.
◦ Su uso lo gestiona el hardware del sistema informático. No hay que preo-
cuparse sobre la gestión del almacenamiento caché del sistema de bases de
datos.
• Memoria principal: aunque la memoria principal puede contener muchos megabi-
tes de datos, suele ser demasiado pequeña (o demasiado cara) para guardar toda
la base de datos.
• Memoria flash:
◦ Memoria sólo de lectura programable y borrable eléctricamente.
◦ La memoria flash se diferencia de la memoria principal en que los datos pueden
sobrevivir a los fallos del suministro eléctrico.

163

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Almacenamiento en discos y cintas magnéticas.
• Almacenamiento óptico: CDs, DVDs.

Clasificación de los dispositivos de almacenamiento:

Tipo Permanencia Velocidad


Caché Volátil La más rápida
Memoria principal Volátil
Memoria flash No volátil
Discos magnéticos No volátil
Medios ópticos No volátil
Cintas magnéticas No volátil La más lenta

6.1.1. RAID
Los requisitos de almacenamiento de datos de algunas aplicaciones (por ejemplo, debido
a las aplicaciones web: bases de datos y multimedia) crecen muy rápidamente.

Se necesita un gran número de discos para almacenar sus datos, incluso aunque las
capacidades de los discos hayan estado creciendo muy rápidamente.

Un gran número de discos en un sistema presenta oportunidades para mejorar la velo-


cidad a la que se pueden leer o escribir los datos si los discos funcionan en paralelo:

• El paralelismo se puede usar para realizar varias lecturas o escrituras indepen-


dientes simultáneamente: rendimiento.
• Esta configuración ofrece la posibilidad de mejorar la fiabilidad del almacenamien-
to de datos, ya que se puede guardar información repetida en varios discos. Por
tanto, el fallo de un disco no provoca una pérdida de datos.

Para abordar los problemas de rendimiento y de fiabilidad se han propuesto varias


técnicas de organización de discos, denominadas colectivamente disposición redundante
de discos independientes RAIDs (Redundant Array of Independent Disks).

Mejora de la fiabilidad mediante la redundancia:

• La solución al problema de la fiabilidad consiste en introducir la redundancia.


• Se guarda información adicional que normalmente no se necesita pero que puede
utilizarse en caso de fallo de un disco para reconstruir la información perdida.
• El enfoque más sencillo (pero el más costoso) para la introducción de la redundan-
cia es duplicar todos los discos. Esta técnica se denomina creación de imágenes
(o, a veces, creación de sombras).

164

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Un disco lógico consiste en dos discos fı́sicos y cada proceso de escritura se lleva
a cabo en ambos discos. Si uno de los discos falla se pueden leer los datos del
otro. Los datos sólo se perderán si falla el segundo disco antes de que se repare el
primero que falló.

Mejora del rendimiento mediante el paralelismo:

• Con la creación de imágenes de los discos la velocidad a la que las solicitudes de


lectura pueden procesarse se duplica, dado que las solicitudes de lectura pueden
enviarse a cualquiera de los discos.
• La velocidad de transferencia de cada proceso de lectura es la misma que en los
sistemas de discos únicos, pero el número de procesos de lectura por unidad de
tiempo se ha duplicado.

La creación de imágenes proporciona gran fiabilidad pero resulta costosa. La distribu-


ción de los datos en varios discos proporciona velocidades de transferencia de datos
elevadas pero no mejora la fiabilidad.

Distribución en el nivel de bloque:

• La distribución en el nivel de bloque reparte bloques de datos entre varios discos.


• Trata la disposición de discos como un único y gran disco, y proporciona números
lógicos a los bloques. Se asume que los números de bloque comienzan en 0. En
realidad, los discos se consideran como conjuntos de bloques fı́sicos.
• Con una disposición de n discos, la distribución en el nivel de bloque asigna el
bloque lógico i usa el bloque fı́sico bi/nc-ésimo del disco para almacenar el bloque
lógico i en el disco (i mod n) + 1; .
• Por ejemplo, con ocho discos, el bloque lógico 0 se almacena el bloque fı́sico 0
del disco 1, mientras que el bloque lógico 11 se almacena en el bloque fı́sico 1 del
disco 4.
• Al leer un archivo grande, la distribución en el nivel de bloque busca n bloques en
un instante en paralelo en los n discos, dando una gran velocidad de transferencia
para grandes lecturas.
• Cuando se lee un único bloque, la velocidad de transferencia de datos es igual que
en un disco, pero los restantes n − 1 discos están libres de realizar cualquier otra
acción.

Niveles de RAID:

• Nivel 0: disposiciones de discos con distribución en el nivel de bloque pero sin


redundancia. Los datos se ”reparten”.
• Nivel 1: creación de imágenes del disco con distribución de bloques. Los datos se
”reparten” y se ”duplican”.

165

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Nivel 2:
◦ Cada byte del sistema de memoria puede tener asociado un bit de paridad
que registra el número de bits que valen 1.
◦ Si, por error, se produce un error pequeño (el cambio de un bit) habrá una
discrepancia entre el bit de paridad guardado y el bit de paridad calculado.
◦ Garantiza la fiabilidad sin la necesidad de duplicar la información.
• Cada nivel adicional, hasta el nivel 6, supone un paso adicional para lograr fiabi-
lidad y velocidad.

6.2. Índices
Motivación:

La mayor parte de las consultas hacen referencia sólo a una pequeña parte de los
registros de un archivo. Por ejemplo, buscar por DNI (solamente habrá un registro!!!).

No es eficiente para el sistema tener que leer cada registro (búsqueda secuencial) y
comprobar que cumple las condiciones de la búsqueda.

Lo más adecuado serı́a que el sistema fuese capaz de localizar directamente estos regis-
tros. Para facilitar estas formas de acceso se diseñan estructuras adicionales (ı́ndices)
que se asocian con archivos.

Conceptos básicos:

Un ı́ndice para un archivo del sistema funciona como el ı́ndice de un libro: permite
encontrar la información si necesidad de leer todo el libro.

Para recuperar un registro cuenta dado su número de cuenta, el sistema de bases de


datos buscarı́a en un ı́ndice para encontrar el bloque de disco en que se encuentra
el registro correspondiente, y entonces extraerı́a ese bloque de disco para obtener el
registro cuenta.

Hay dos tipos básicos de ı́ndices:

• Índices ordenados: basados en una disposición ordenada de los valores. Ejemplo:


árboles B + .
• Índices asociativos (hash indices): basados en una distribución uniforme de los
valores a través de una serie de cajones (buckets). El valor asignado a cada cajón
está determinado por una función, llamada función de asociación (hash function).

Existen varias técnicas de indexación. Ninguna es mejor de una forma absoluta. De-
pende de la aplicación especı́fica de la base de datos.

166

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Cada técnica debe ser valorada según los siguientes criterios:
• Tipos de acceso.
• Tiempo de acceso.
• Tiempo de inserción.
• Tiempo de borrado.
• Espacio adicional requerido.
Los atributos o conjunto de atributos usados para buscar en un archivo se llaman
claves de búsqueda. Hay que observar que esta definición de clave difiere de la usada
en clave primaria, clave candidata y superclave. Este doble significado de clave está
(por desgracia) muy extendido en la práctica.
Un ı́ndice es una estructura que permite la búsqueda eficiente de un registro a través
de un determinado campo.
Asocia los valores de un campo (claves de búsqueda) con la posición del registro co-
rrespondiente dentro de la base de datos.
Tipos:
• Índices ordenados: basados en el orden de los valores. Pueden implementarse me-
diante un árbol B + :
◦ Índices densos: el ı́ndice contiene una entrada por cada clave de búsqueda en
la tabla.
◦ Índices dispersos: el ı́ndice contiene entradas sólo para algunas claves de
búsqueda. Son una combinación de acceso directo con acceso secuencial.
• Índices hash: basados en una función que distribuye uniformemente los valores en
un conjunto de ”cajones”.
Índices en SQL:
• SQL crea automáticamente ı́ndices para las claves primarias de una relación.
• Es recomendable la creación de ı́ndices sobre otros campos si se van a realizar
búsquedas intensivas sobre los mismos.
• Instrucciones relacionadas:
◦ Creación de un nuevo ı́ndice: CREATE INDEX nombre indice ON tabla(columna);
◦ Borrado de un ı́ndice: DROP INDEX nombre indice;
Índices en MongoDB:
• Cuando se añade un documento a una colección MongoDB crea de forma au-
tomática un campo ( id) sobre el que construye un ı́ncide.
• MongoDB permite crear ı́ndices sobre cualquier clave.

167

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
6.2.1. Árboles B +
Caracterı́sticas generales:

Es la estructura más utilizada para almacenar ı́ndices ordenados.

Los árboles B + son árboles de búsqueda equilibrados.

Todos los caminos desde la raı́z hasta las hojas de un árbol son de la misma longitud.

El número de hijos para cada nodo oscila entre dn/2e y n hijos, donde n es un número
fijo para todo el árbol con la excepción de la raı́z del árbol.

Estructura de un árbol B +
En cada nodo interno se almacenan punteros y claves:

[P1 , K1 , P2 , K2 , . . . KN , PN +1 ]

donde Ki < Kj si i < j

Contiene n casillas destinadas a claves y n + 1 punteros.

La semántica de un nodo interno consiste en almacenar claves dentro del rango [K1 , KN ).

Además, en un nodo interno el puntero, Pi , que está entre las claves Ki y Ki+1 señala
a un nodo cuyas claves están entre estos valores: [Ki , Ki+1 ).

Nodos hoja:

• Al menos la mitad de las casillas, en concreto d(n − 1)/2e, deben contener claves.
• El puntero que hay a la izquierda de cada clave apunta al registro correspondiente
en la tabla.
• El puntero de más a la derecha apunta a la siguiente hoja del árbol (si existe).

Ejemplo: n = 3

[62, ∅, ∅]

[24, 39, ∅] [75, ∅, ∅]

[7, 15, 21] [24, 32, ∅] [39, 47, 59] [62, 70, ∅] [75, 78, ∅]

168

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Búsqueda en un árbol B +
Para buscar una determinada clave se recorre el árbol desde la raı́z hasta la hoja
correspondiente siguiendo un mecanismo similar al de la búsqueda en un árbol binario.

El enlazar los nodos hoja nos permite un procesamiento secuencial. Ejemplo: buscar
claves dentro de cierto rango.

Ejemplo: búsqueda de la clave 39.

• Nodos visitados: [62, ∅, ∅], [24, 39, ∅], [39, 47, 59].

Búsqueda de los elementos cuyas claves estén comprendidas entre 30 y 65.

• Nodos visitados: [62, ∅, ∅], [24, 39, ∅], [24, 32, ∅], [39, 47, 59], [62, 70, ∅].

Inserción en un árbol B +
Se desciende por el árbol hasta encontrar la posición del elemento a insertar (similar a
una consulta).

Se inserta el nuevo elemento dentro de la hoja correspondiente, desplazando el resto


de elementos si es necesario.

Si el elemento a insertar no cabe dentro de la hoja correspondiente, ésta deberá dividirse


en dos hojas:

• El nodo padre debe apuntar a estas dos hojas.


• Este último paso puede provocar la división de un nodo interno en dos.
• El proceso de división varı́a según se realice en una hoja o en un nodo interno.

Ejemplo de inserción sin división:

• Clave a insertar: 28.


• Buscamos su posición dentro del árbol. Está en el nodo [24, 32, ∅].
• Insertamos la nueva clave desplazando a las demás contenidas en el nodo (inclu-
yendo sus punteros).
• Árbol resultante:
[62, ∅, ∅]

[24, 39, ∅] [75, ∅, ∅]

[7, 15, 21] [24, 28, 32] [39, 47, 59] [62, 70, ∅] [75, 78, ∅]

169

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
División de nodos hoja:

• Si el tamaño de nodo es n las hojas del árbol B + permiten almacenar hasta n-1
claves.
• Si se pretenden almacenar n claves en una hoja hemos de dividirla en dos:
◦ Las primeras dn/2e se quedan en la hoja existente.
◦ Las restantes pasan a una nueva hoja.
• En el nodo padre deberá insertarse una referencia a la nueva hoja creada. Esto
puede provocar una división en el nodo padre!!!.

Un poco de notación:

• Se Ci una clave, entonces prev(Ci ) es el puntero de la izquierda. Apunta a un


nodo con claves menores que Ci . De forma análoga, post(Ci ) es el puntero de la
derecha. Sean dos claves consecutivas, Ci y Ci+1 , entonces: post(Ci ) = prev(Ci+1 ).

Ejemplo:

• Clave a insertar: 30
• Inserción en: [24, 28, 32]
• Nuevo nodo: [24, 28, 30, 32].
• Nodos que surgen de la división: [24, 28∅] y [30, 32, ∅].
• post(21) = 24.
• Los nodos [24, 28, ∅], [30, 32, ∅] y [39, 47, 59] se enlazan secuencialmente.
• Se añade la entrada con el nuevo nodo creado en el nodo padre.

División de nodos internos:

• Si el tamaño de nodo es n, los nodos del árbol interno pueden contener n punteros
a hojas.
• Al insertar el elemento (n+1)-ésimo debemos dividir el nodo interno y repartir
los punteros:
◦ Los primeros dn/2e) se quedan en el nodo existente.
◦ Las restantes pasan a un nuevo nodo.
• Al repartir los punteros de un nodo interno existe una clave que no está rodeada
por dos punteros.
• Esta clave será eliminada del nodo dividido y será propagada hacia el nodo padre,
que la insertará en el lugar adecuado.
• Esto puede provocar una división en el nodo padre, que se tratará de la misma
manera.

170

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Eliminación en un árbol B +
Se busca la clave a eliminar y se borra de la hoja correspondiente.

Esto puede provocar que la hoja resultante tenga un número de entradas inferior al de
la capacidad mı́nima.

En este caso se intenta fusionar la hoja con la anterior.

Si el resultado de fusionar las dos hojas diese lugar a un nodo demasiado grande, en
lugar de fusionar se realizará una redistribución de hijos desde la hoja anterior.

Las fusiones de nodos provocan la eliminación de entradas en el nodo padre, que puede
quedar por debajo de la capacidad mı́nima. Se repite el mismo proceso en el padre.

6.2.2. Tablas Hash


Estructura de datos usada como implementación alternativa de ı́ndices. No necesita
que exista una relación de orden en las claves.

Una tabla hash se compone de varios ”cajones” (buckets) con un tamaño fijo numerados
desde 0 hasta n − 1.

Se basan en la utilización de una función hash, que transforma una clave de búsqueda
en un número de 0 a n − 1 que indica el cajón donde se buscará el valor deseado.

Tipos:

• Hashing estático: la función hash es invariable, y se mantiene constante a lo largo


del tiempo de vida de la estructura.
• Hashing dinámico: la función hash varı́a según crezca el tamaño de la base de
datos. Ejemplo: hashing extensible.

Una función hash debe distribuir las claves de búsqueda uniformemente entre los dis-
tintos cajones.

h(K) = Valor hash asociado a la clave K.

Hashing estático
Búsqueda:

• Buscamos la clave K.
• Calculamos m = h(K).
• Búsqueda en el cajón m-ésimo.
• Ejemplo:

171

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
◦ Función hash: h(x) = x mod 4
◦ Si K = 25, h(25) = 1.
◦ La función resto reparte uniformemente las posiciones de las claves.
• Eficiencia:
◦ Para una tabla hash con un cajón por valor de la función hash (o pocos), las
operaciones de lectura tienen una complejidad cercana a θ(1).
◦ En cambio, para una tabla hash con listas largas de cajones por cada valor
de la función hash, las operaciones de lectura tienen una complejidad cercana
a θ(n).
Inserción:
• Buscar el cajón que le corresponde a una clave e insertarlo ahı́ (si es posible).
• ¿Qué hacer si el cajón está lleno?. Existen dos alternativas:
◦ Encadenamiento: enlazar el cajón lleno con otro (inicialmente vacı́o) e insertar
en él la nueva clave.
◦ Open hashing: colocar la clave en otro cajón distinto.
Inconvenientes del Hashing estático:
• El número de cajones iniciales es difı́cil de determinar. Un número inadecuado da
lugar a complejidades lineales (ineficiencia).
• Comenzar con un número demasiado elevado de cajones deperdicia memoria.

Hashing extensible
Idea clave: tener una función hash adaptable al tamaño de los datos que maneja.
Consiste en la combinación de una función hash con el truncamiento de su valor ex-
presado en binario. En la práctica tendremos una función hash variable.
b representa el números de bits tomados. Valores tı́picos: b = 32, b = 64.
Ejemplo: si h(x) = x mod 4, entonces h(27) = 3 = |{z}
11 .
binario

Estructura:
1. Una tabla de cajones (a modo de ı́ndice) asociada a un valor de K.
2. K: número de bits necesarios para direccionar los cajones (valor de la función
hash truncada). Este valor puede cambiar a lo largo de tiempo.
3. Un conjunto de cajones (o lista de cajones) que almacenan la información. Cada
cajón (o lista de cajones) posse un valor J que representa el número de bits reales
para su direccionamiento. Comparado con K permite insertar eficazmente.

172

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Todas las claves de un cajón poseen los últimos J bits iguales.

Búsqueda:

• Establecer una función hash, h.


• m = últimos K bits de h(clave).
• Buscar el cajón apuntado por la entrada m de la tabla de cajones.
• Buscar clave en el cajón seleccionado.

Inserción:

• m = últimos K bits de h(clave).


• Buscar el cajón apuntado por la entrada m de la tabla de cajones.
• Intentar insertar en el cajón seleccionado. Hay 4 posibles casos:
1. Hay espacio en el cajón: inserción en el cajón.
2. No hay espacio en el cajón, J < K: varias claves hash truncadas comparten
el mismo cajón. Solución: crear cajones distintos para estas claves.
3. No hay espacio en el cajón, J = K: la tabla de claves hash truncadas se ha
quedado pequeña. Solución: duplicar tabla (ahora la función hash truncada
es distinta).
4. No hay espacio en el cajón, y el valor de todas las claves contenidas en el
mismo coincide con el valor a insertar.
• Caso 1: se inserta en en cajón correspondiente.
• Caso 2:
◦ Dividimos el cajón C en dos: C1 y C2 .
◦ Cada uno de ellos tendrá asociado el valor J + 1.
◦ La primera mitad de las entradas que apuntaban a C pasan a apuntar a C1
y la segunda mitad pasan a apuntar a C2 .
◦ Redistribuimos los elementos del cajón antiguo C entre los dos cajones nuevos
utilizando el valor hash.
◦ Volver a intentar la inserción de la clave en la nueva tabla hash.
• Caso 3:
◦ En este caso es necesario duplicar el tamaño de la tabla de cajones.
◦ Para cada entrada en la tabla antigua con una cadena m de K bits, se crean
dos entradas de K + 1 bits en la tabla nueva: una de ellas con la cadena 0m y
la otra con la cadena 1m. Ambas entradas apuntarán al cajón que apuntaba
m en la tabla antigua.
◦ Incrementar el valor K asociado a la tabla de cajones en una unidad.

173

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 6.1: Estado inicial

◦ Volver a repetir la inserción. No habrá espacio en el cajón correspondiente,


pero esta vez se cumplirá la condición J < K (caso 2).
• Caso 4:
◦ Es posible que varias claves tengan exactamente el mismo valor hash. Todas
ellas estarán en el mismo cajón.
◦ Si este cajón se desborda no habrá manera de separar estas entradas en ca-
jones distintos, por mucha subdivisión de cajones que realicemos.
◦ La solución en este caso excepcional es la misma que en el hashing estático.
Utilizar un cajones extra de desbordamiento enlazados.
◦ Este caso degrada el rendimiento de la tabla hash.

174

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 6.2: Inserción del registro ”Einstein”

Figura 6.3: Inserción de los registros ”Gold” y ”El Said”

175

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 6.4: Inserción del registro ”Katz”

Figura 6.5: Inserción de once registros más

176

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 6.6: Inserción del registro ”Kim”

177

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
178

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 7

Transacciones y control de la
concurrencia

7.1. Transacciones
Una transacción es una unidad lógica (atómica, indivisible) de trabajo en una base de
datos.

Ejemplos:

• Transferencia de dinero entre dos cuentas bancarias.


• Borrado de una cuenta de usuario en un portal web.

El SGBD debe comprobar la ejecución correcta de las transacciones. En concreto:

• Ejecución todo o nada (unidad lógica).


• Consistencia de la base de datos en presencia de transacciones concurrentes.

7.1.1. Modelo simple de transacciones


Nuestro estudio de las transacciones se centrará en un simple lenguaje de bajo nivel.

El lenguaje contiene primitivas para leer desde la BBDD en disco a la memoria principal
y para escribir desde la memoria principal a la BBDD.

Los elementos leı́dos o escritos se designarán mediante variables A, B, C, etc. que


pueden denotar filas de una tabla, columnas de una fila, etc.

Operaciones principales del modelo:

• READ A: Lee el elemento A desde la BBDD a una variable temporal que también
se llamará A.

179

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• WRITE A: Escribe el valor de la variable temporal A en el elemento correspondiente
de la base de datos.
• A = expresión: Asigna a la variable que contiene el valor del elemento A el valor
indicado por la expresión.

Los elementos leı́dos y escritos son globales a todas las transacciones. Las variables
temporales son locales a cada transacción.

Ejemplo. Transferencia bancaria de 10 euros desde la cuenta A hasta la cuenta B.


READ A
A = A − 10
WRITE A
READ B
B = B + 10
WRITE B

Propiedades deseables en el manejo de transacciones. Propiedades ACID


Atomicidad (Atomicity): O se realizan todas las operaciones de la transacción correc-
tamente o no se realiza ninguna.

Consistencia (Consistency): La ejecución de una transacción individual debe preservar


la integridad de los datos.

Aislamiento (Isolation): Aunque se permita la ejecución concurrente de transacciones,


el resultado global debe ser equivalente al que se obtendrı́a si se hubiesen ejecutado las
transacciones en serie.

Durabilidad (Durability): Una vez realizada una transacción, sus cambios son perma-
nentes en la base de datos.

Observaciones:

Consideremos el ejemplo anterior:


READ A
A = A − 10
WRITE A
−−−−−Abortar
READ B
B = B + 10
WRITE B

Si la transacción se aborta en el lugar indicado se deberá restaurar el valor de A escrito


previamente a su valor antes de comenzar la transacción. A este proceso se le llama
rollback.

El proceso de restauración de los efectos de una transacción abortada se realiza me-


diante un registro (log).

180

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 7.1: Transiciones posibles entre los estados de una transacción

Una transacción que termina con éxito pasa a un estado de confirmación (commit). La
propiedad de permanencia exige que se proporcionen los mecanismos necesarios para
que los cambios sean permanentes.

Los efectos de una transacción finalizada no se pueden deshacer, salvo que se haga otra
transacción que realice el efecto contrario.

Estados de una transacción:

Activa: Transacción en ejecución.

Parcialmente confirmada: Se ha ejecutado la última operación con éxito.

Fallada: No puede continuarse la ejecución de la transacción.

Abortada: Los efectos realizados por la transacción han sido deshechos.

Confirmada: Los cambios realizados por la transacción son permanentes.

La transición entre estos estados la podemos observar en al figura 7.1


Aislamiento:

Los SGBDs permiten la ejecución simultánea de varias transacciones, lo que puede


producir efectos no deseados en la consistencia de los datos.

La propiedad de aislamiento exige que estos efectos nunca se produzcan.

El modo más seguro de evitarlos consistirı́a en prohibir la concurrencia.

Sin embargo, la concurrencia es una herramienta demasiado valiosa como para renun-
ciar a ella: aumenta la eficiencia.

Plan de transacciones:

181

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Un plan de transacciones es una elección concreta del orden en que se realizan las
operaciones de un conjunto de transacciones.
Debe contener todas las operaciones de todas las transacciones involucradas.
Se debe respetar el orden de las instrucciones de cada transacción individual.
Sin embargo, es posible entremezclar instrucciones provenientes de transacciones dis-
tintas.
Consideremos las siguientes transacciones:
T1 :
READ A
A = A − 10
WRITE A

T2 :
READ A
A = A ∗ 2
WRITE A

Son posibles los siguientes planes de transacciones:

Tiempo Acción
1 T1:READ A
2 T1:A = A - 10
3 T1:WRITE A
4 T2:READ A
5 T2:A = A * 10
6 T2:WRITE A

Tiempo Acción
1 T2:READ A
2 T2:A = A * 2
3 T2:WRITE A
4 T1:READ A
5 T1:A = A - 10
6 T1:WRITE A

Tiempo Acción
1 T2:READ A
2 T2:A = A * 2
3 T1:READ A
4 T1:A = A - 10
5 T2:WRITE A
6 T1:WRITE A

182

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Planes serializables
Un plan de transacciones se realiza en serie cuando todas las instrucciones de cada
transacción individual se ejecutan consecutivamente.

Un plan es serializable si es equivalente a un plan en serie.

Existen varias nociones de equivalencia. En el presente tema supondremos que la equi-


valencia se refiere a los resultados obtenidos tras la ejecución del plan.

Ejemplo:

• Los planes 1 y 2 son planes en serie. El plan 3 no lo es.


• Si suponemos que el valor inicial contenido en A es k, observamos el valor final
de A.
◦ Plan 1: A = 2 ∗ (k − 10)
◦ Plan 2: A = 2 ∗ k − 10
◦ Plan 3: A = k − 10
• El valor final del plan 3 no coincide, en general, ni con el valor final de 1 ni con
el de 2. Deducimos que el plan 3 no es serializable.

7.2. Control de la concurrencia


El Plan 3 del ejemplo anterior es inadmisible, en el sentido de que deja el valor del
elemento A en un estado inconsistente.

Un gestor de transacciones debe proporcionar mecanismos para el control de la concu-


rrencia. Los hay de distintos tipos:

• Basados en bloqueos.
• Basados en timestamps.
• Aislamiento de instantáneas (snapshot isolation).

7.2.1. Protocolos basados en bloqueos


Los bloqueos permiten garantizar la exclusión mutua de recursos comunes.

Existe un candado asociado a cada elemento.

Cuando una transacción accede a un elemento A ha de obtener previamente su candado


mediante la instrucción LOCK A.

183

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Si una transacción Ti quiere acceder al candado de un elemento A, pero éste está en
posesión de otra transacción Tj, la transacción Ti deberá esperar a que Tj libere el
candado.
La liberación del candado asociado a A se realiza mediante la instrucción UNLOCK A.
Ejemplo: Consideremos las siguientes transacciones:
T1 :
LOCK A
READ A
A = A − 10
WRITE A
UNLOCK A

T2 :
LOCK A
READ A
A = A + 20
WRITE A
UNLOCK A

El siguiente plan es válido:

T1 T2
LOCK A
READ A
A = A - 10
WRITE A
UNLOCK A
LOCK A
READ A
A = A + 20
WRITE A
UNLOCK A

El siguiente plan no es válido:

T1 T2
LOCK A
READ A
LOCK A (T1 tenı́a el candado sobre A)
READ A
A = A + 20
WRITE A
UNLOCK A
A = A - 20
WRITE A
UNLOCK A

184

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Bloqueos y serializabilidad
Los bloqueos realizados en T1 y T2 sólo permiten dos planes de transacciones:

• Ejecutar T1 ı́ntegramente, luego T2.


• Ejecutar T2 ı́ntegramente, luego T1.

Ambos son planes en serie, luego cualquier plan de transacciones de T1 y T2 es seria-


lizable (a cambio de prohibir la concurrencia).

No obstante, es posible intercalar transacciones cuando se bloquean recursos distintos.


La serializabilidad no está garantizada por el simple hecho de utilizar bloqueos.

Un poco de notación:

• Por simplicidad, omitiremos cualquier referencia a operaciones READ, WRITE, y


asignación.
• Para cada par LOCK X ... UNLOCK X, se supondrá que se realizan las operaciones
oportunas entre la adquisición del candado y su liberación.
• El ejemplo anterior quedarı́a ası́:
T1 :
LOCK A
UNLOCK A

T2 :
LOCK A
UNLOCK A

T1 T2
LOCK A
UNLOCK A
LOCK A
UNLOCK A

Test de serializabilidad:

• Decide si un determinado plan de transacciones es serializable.


• Consiste en la creación de un grafo dirigido que modela el orden en el que deben
ejecutarse las transacciones.
• Para cada instrucción UNLOCK A de la transacción Ti se busca la siguiente
instrucción de la forma LOCK A iniciada por una transacción distinta Tj. Se
dibuja un arco desde Ti hasta Tj.
• Si el grafo contiene ciclos, el plan analizado no es serializable.

185

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Si el grafo no contiene ciclos, el plan es serializable. Realizando un recorrido en
el orden indicado por las aristas obtendremos un plan en serie equivalente al
analizado.

Ejercicio. Comprobar si los siguientes planes son serializables:

T1 T2 T3
LOCK A
LOCK B
LOCK C
UNLOCK B
LOCK B
UNLOCK A
LOCK A
UNLOCK C
UNLOCK A
LOCK A
LOCK C
UNLOCK B
UNLOCK C
UNLOCK A

T1 T2 T3
LOCK A
UNLOCK A
LOCK A
UNLOCK A
LOCK B
UNLOCK B
LOCK B
UNLOCK B

Protocolo de bloqueo en dos fases:

• El protocolo de bloqueo en dos fases garantiza la serializabilidad de las transac-


ciones.
• Este protocolo exige que las transacciones soliciten los candados en dos fases:
◦ Fase de adquisición: La transacción puede adquirir candados, pero no libe-
rarlos.
◦ Fase de liberación: La transacción puede liberar candados, pero no puede
adquirir nuevos candados.

186

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Si todas las transacciones se adhieren a este protocolo, cualquier plan de transac-
ciones que respete los bloqueos es serializable.
• Ejemplos:
T1 :
LOCK A
READ A
A := A − 10
WRITE A
LOCK B
READ B
B := B + 10
WRITE B
UNLOCK A
UNLOCK B

Respeta el bloqueo.
T2 :
LOCK A
READ A
A := A − 10
WRITE A
UNLOCK A
LOCK B
READ B
B := B + 10
WRITE B
UNLOCK B

No respeta el bloqueo.
T3 :
LOCK A
LOCK B
READ A
A := A − 10
WRITE A
READ B
B := B + 10
WRITE B
UNLOCK A
UNLOCK B

Respeta el bloqueo.

Bloqueos de lectura y escritura:

• Anteriormente hemos supuesto que para leer un elemento A debemos obtener un


candado sobre él con el fin evitar problemas derivados del acceso concurrente.
• Esto prohı́be que dos transacciones distintas puedan leer el elemento A simultánea-
mente.
• Mediante distintos tipos de candados podemos preservar la serializabilidad de las
transacciones, a la vez que se permite el acceso concurrente a un elemento en
modo de sólo lectura.
• Nuevas instrucciones de bloqueo:

187

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
◦ RLOCK A Ejecutada por una transacción que sólo quiere leer el elemento A.
Evita que otra transacción acceda al elemento A para escribir en él, pero per-
mite que otras transacciones puedan adquirir el candado RLOCK A simultánea-
mente.
◦ WLOCK A Obtención de un candado con el fin de leer y/o escribir en el elemento
A.
◦ UNLOCK A Libera el candado obtenido, independientemente de su tipo.

Conversiones de bloqueo:

• Una transacción con un candado de lectura (RLOCK) sobre un elemento puede


solicitar un candado de lectura/escritura (WLOCK) sobre ese mismo elemento.
• Esta operación se conoce como promoción (upgrade) del candado.
• La operación contraria (adquirir un candado de lectura habiendo obtenido el
de lectura/escritura previamente) también es posible, aunque no se utiliza en
la práctica. Esta operación recibe el nombre de degradación (downgrade).
• Ejemplo:
T1 :
RLOCK A
READ A
RLOCK B
READ B
WLOCK B
B := A + 10
WRITE B
UNLOCK B
UNLOCK A

T2 :
RLOCK B
READ B
UNLOCK B

Plan válido:
T1 T1
RLOCK A
RLOCK B
RLOCK B
UNLOCK B
WLOCK B
UNLOCK B
UNLOCK A

Plan no válido:

188

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
T1 T1
RLOCK A
RLOCK B
RLOCK B
WLOCK B
UNRLOCK B
UNLOCK B
UNLOCK A

Test de serializabilidad:

• De nuevo, utiliza un grafo cuyos vértices son las transacciones.


• Para cada instruccion RLOCK A o WLOCK A de la transaccion Ti se busca la
siguiente instruccion de la forma WLOCK A iniciada por Tj (donde i 6= j), y se
dibuja un arco desde Ti hasta Tj.
• Para cada instrucción UNLOCK A de Ti que libere un candado de escritura, se
buscan todas las transacciones Tj que ejecuten un RLOCK A a continuación, pero
antes de que otra transacción obtenga el candado de escritura sobre A. Entonces,
se dibuja una arista desde Ti hasta Tj.
• Si el grafo no tiene ciclos, el plan es serializable.
• Demuestra que el siguiente plan no es serializable:
T1 T2 T3 T4
WLOCK A
RLOCK B
UNLOCK A
RLOCK A
UNLOCK B
WLOCK B
RLOCK A
UNLOCK B
WLOCK B
UNLOCK A
UNLOCK A
WLOCK A
UNLOCK B
RLOCK B
UNLOCK A
UNLOCK B

Protocolo de bloqueo en dos fases:

189

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Sirve para garantizar la serializabilidad de las transacciones que se ajusten a este
protocolo.
• Fase de adquisición: La transacción puede adquirir candados (RLOCK, WLOCK),
pero no liberarlos. También puede realizar promoción de sus candados.
• Fase de liberación: La transacción puede liberar candados, pero no puede adquirir
nuevos candados. También puede degradar los candados que ya haya obtenido.
• Una forma muy utilizada de generar las instrucciones de bloqueo automáticamente
en función de las operaciones de lectura y escritura consiste en utilizar la siguiente
estrategia:
◦ Antes de cada instrucción READ X, generar una instrucción RLOCK X.
◦ Antes de cada instrucción WRITE X, generar una instrucción WLOCK X.
◦ Al final de cada transacción se liberan todos los candados obtenidos.
• El resultado obtenido mediante esta estrategia respeta el protocolo de bloqueo en
dos fases.
• Ejemplo:
T1 :
READ A
READ B
B = 2∗A + B
WRITE B

se transforma en:
T1 :
RLOCK A
READ A
RLOCK B
READ B
B := 2∗A + B
WLOCK B
WRITE B
UNLOCK B
UNLOCK A

Modos de bloqueo:

• Añadir dos modos de bloqueo distintos nos permite que el gestor de bloqueos
permita mayor concurrencia, pero preservando la serializabilidad.
• Esto se ha conseguido relajando las reglas de bloqueo con respecto a la versión
anterior (LOCK):
◦ El gestor de bloqueos concede bloqueo de lectura de X a una transacción, si
no existe otra transacción que haya bloqueado X para escritura previamente.
◦ El gestor de bloqueos concede bloqueo de escritura de X si no existe otra
transacción que haya bloqueado X, bien para lectura, o bien para escritura.
• Esto permite ser expresado mediante la siguiente tabla de compatibilidades:

190

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Bloqueo en manos de una transacción existente
Lectura Escritura
Bloqueo solicitado Lectura Sı́ No
Escritura No No
• Esta idea puede generalizarse a otros tipos de bloqueos.
• Para poder aplicar el test de serializabilidad hemos de disponer de la tabla de
compatibilidad.
• Para generar esta tabla utilizamos el siguiente criterio:
Si una transacción T1 quiere bloquear un elemento para la operación X
y otra transacción T2 ya ha bloqueado dicho elemento para la operación
Z, el candado se concederá si las operaciones X y Z conmutan.
• Ejemplo. Supongamos que añadimos un nuevo tipo de bloqueo: ILOCK X. La
transacción que adquiera el candado correspondiente podrá incrementar el valor
de X de manera atómica.
Lectura Escritura Incrementar
Lectura Sı́ No No
Escritura No No No
Incrementar No No Sı́
Test de serializabilidad:
• En el grafo de transacciones se coloca un arco cuando T1 adquiere el bloqueo X
antes de que T2 adquiera el bloqueo Z, y la celda correspondiente de la tabla de
compatibilidad contiene el valor NO.
Concurrencia y granularidad:
• En algunos contextos es necesario bloquear varios elementos a la vez. Por ejemplo:
◦ Una consulta puede necesitar acceso a todos los registros de una tabla.
◦ Una actualización puede afectar a un subconjunto de filas de una tabla.
• El bloqueo individual de cada uno de los elementos resulta ineficiente.
• Existen mecanismos para definir múltiples niveles de granularidad en los bloqueos.
Protocolo de bloqueo en árbol:
• Sea:
A

B C

D E

191

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• El bloqueo en un elemento conlleva el bloqueo de sus descendientes.

◦ Supongamos que T1 bloquea E.

◦ T2 no podrá bloquear ni A ni B.

• Esto implica, que cada vez que se quiera bloquear un determinado elemento, se
ha de comprobar que sus descendientes no estén bloqueados por otra transacción
(ineficiente).

• El protocolo de bloqueo en árbol no permite bloquear un elemento (LOCK X) a


menos que se haya colocado un bloqueo de aviso (WARN X) sobre sus ascendien-
tes. Bloquear supone poner una marca WARN.

• Una transacción no puede adquirir un bloqueo sobre un elemento X si éste ha sido


bloqueado por otra transacción mediante LOCK o WARN.

• Matriz de compatibilidad:

Bloqueo en manos de una transacción existente


WARN LOCK
Bloqueo solicitado WARN Sı́ No
LOCK No No

• Ejemplo. Sea:

B C

D E F G

192

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
T1 T2 T3
WARN A
WARN A
WARN A
WARN B
LOCK C
LOCK D
UNLOCK C
UNLOCK D
UNLOCK A
UNLOCK B
LOCK B
WARN C
LOCK F
UNLOCK A
UNLOCK B
UNLOCK F
UNLOCK C
UNLOCK A
El plan es serializable.
Problemas con bloqueos:
• Inanición:
◦ Una transacción espera permanentemente que se libere el candado sobre un
elemento que otras transacciones ocupan.
◦ Existen varias soluciones. Una de ellas es utilizar una estrategia first-come-
first-served para la concesión de bloqueos.
• Interbloqueo:
◦ T1 y T2 esperan indefinidamente a que la otra transacción libere el bloqueo
que obtiene sobre el recurso:
T1 :
LOCK A
LOCK B
UNLOCK A
UNLOCK B
T2 :
LOCK B
LOCK A
UNLOCK B

◦ Se puede evitar mediante un protocolo de bloqueo en dos fases, obligando a


que los recursos se adquieran en un determinado orden. Por ejemplo, se debe
adquirir el bloqueo en A antes que en B.

193

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
194

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Capı́tulo 8

Procesamiento de consultas y
optimización

8.1. Introducción
Conceptos generales:

Los primeros sistemas basados en el modelo relacional tenı́an un rendimiento muy bajo
en las consultas.

En los sistemas no relacionales:

• Las consultas se expresan en un lenguaje procedural de bajo nivel.


• El usuario-programador selecciona la estrategia de ejecución.
• Optimización ”manual”.

En un sistema relacional:

• Las consultas se expresan en SQL.


• El sistema selecciona la mejor estrategia de ejecución.
• Optimización automática.

Procesamiento de consultas: actividades involucradas en la recuperación de datos de


la BB.DD.

Optimización de consultas: elección de una estrategia de ejecución eficaz para procesar


cada consulta sobre la base de datos.

Objetivos del procesamiento de consultas:

1. Transformar una consulta SQL en una estrategia de ejecución eficaz expresada en


un lenguaje de bajo nivel.

195

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
2. Ejecutar dich estrategia.

Objetivo de la optimización de consultas:

1. Elegir la estrategia de ejecución que minimiza el uso de los recursos.


2. La estrategia elegida por el sistema puede no ser óptima aunque será razonable-
mente eficiente.

8.2. Pasos del procesamiento de una consulta


1. Análisis léxico, sintáctico y validación.

2. Optimización.

3. Generación de código.

4. Ejecución.

8.2.1. Análisis léxico, sintáctico y validación


Análisis léxico: identificar los componentes (léxicos) en el texto de la consulta (SQL).

Análisis sintáctico: revisar la sintaxis de la consulta (corrección gramatical).

Validación semántica: verificar la validez de los nombres de las tablas, vistas, columnas,
etc.

Traducción de la consulta a una representación interna que la máquina manipule mejor


eliminando peculiaridades del lenguaje de alto nivel empleado (SQL).

Utilizamos el Álgebra relacional como base de un formalismo para la representación de


la consulta.

Ejemplo:
SELECT nombre FROM Empleado E , T r a b a j a e n T WHERE E . n s s = T . n s s e AND T . nump=2 ;

se traduce como: πnombrep (σnump=2 (empleado ./nss=nsse trabaja en))

Árbol algebraico o de consultas: traducción de una consulta en lenguaje SQL a una


representación interna basada en el álgebra relacional en forma de árbol.

Se obtiene mediante los siguientes pasos:

• Por cada relación base de la consulta se crea un nodo hoja.


• Por cada operación intermedia producida por una operación de álgebra relacional
se crea un nodo no hoja.

196

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Figura 8.1: Operaciones del álgebra relacional

• El resultado de la consulta se representa como la raı́z del árbol.

Observaciones:

• La secuencia de operaciones se dirige de los nodos hoja al nodo raı́z.


• Una misma consulta puede estar representada por árboles diferentes según el
orden elegido para dichas operaciones.

Ejemplo:
SELECT e . f i r s t N a m e , e . lastName , e . h i r e D a t e , d . deptName
FROM Emp e , Dept d WHERE e . d e p t I d= d . d e p t I d
AND ( e . h i r e D a t e > ’01 −SEP−07 ’ AND d . deptName LIKE ’ IT ’ ) ;

Su árbol de consultas es:


πf irstN ame,lastN ame,hireDate,deptN ame

./e.deptId=d.deptId

σhireDate>0 01−SEP −070 σd.deptN ameLIKE 0 IT 0

Emp Dept

8.2.2. Optimización
El optimizador de consultas combina varias técnicas. Destacan las siguientes:

197

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
• Optimización heurı́stica: ordenar las operaciones de la consulta para incrementar
la eficiencia de su ejecución.
• Estimación de costes:
◦ Estimar sistemáticamente el coste de cada estrategia de ejecución.
◦ Elegir la estrategia de menor coste estimada.

La optimización heurı́stica aplica reglas de transformación para modificar la represen-


tación interna de una consulta (árbol de consulta) para mejorar el rendimiento.

Varias expresiones del álgebra relacional pueden corresponder a la misma consulta.

Lenguajes como SQL permiten expresar una misma consulta de varias formas. El ren-
dimiento no deberı́a depender de cómo sea expresada la consulta.

El analizador sintáctico genera un árbol de consulta inicial: ejecución ineficiente.

El optimizador de consultas transforma dicho árbol en un árbol de consulta final equi-


valente y eficiente a través de unas reglas de transformación.

La consulta queda convertida en su forma canónica equivalente.

Algunas técnicas del optimizador para la evaluación de la operación selección σ.

• Búsqueda lineal.
• Búsqueda binaria
• Empleo del ı́ndice primario o clave de dispersión.

El optimizador elige la técnica a partir de:

• La información estadı́stica.
• Información sobre la interdependencia entre las operaciones de bajo nivel.

8.2.3. Algunas heurı́sticas para la optimización de consultas


Ejecutar las selecciones tan pronto como sean posibles.

Ejecutar primero las selecciones más restrictivas (las que producen menor número de
filas).

Ejecutar las operaciones de proyección tan pronto como sea posible.

Combinar un producto cartesiano con una selección en un join cuya condición sea la
de la selección.

198

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
8.2.4. Reglas de transformación de expresiones
Las siguientes reglas nos permiten equivalencias entre expresiones para elegir la más
eficiente:

1. Transformación de una secuencia de selecciones en una sola:

σc1 (σc2 (A)) = σc1 and c2 (A)

2. Una secuencia de proyecciones se puede reducir a una sola si ésta última aparece en
todas las demás:

πc1 (πc2 (A)) = πc2 (A) si y solo si C2 ⊆ C1

3. Una selección de una proyección puede transformarse en una proyección de una selec-
ción:

σc (πp (A)) = πp (σc (A))

Es una buena idea hacer una selección antes que una proyección pues la selección reduce
el tamaño de entrada de la selección.

4. σ es distributivo respecto de la unión, la intersección y la diferencia:


N N
σc (R S) = σc (R) σc (S)
N S T
donde ∈ { , , \}.

5. π es distributivo respecto de la unión:


S S
πp (R S) = πp (R) πp (S)

6. σ es distributivo respecto de un join si la condición de selección:

contiene columnas que sólo pertenecen a una tabla:


σc (R1 ./ R2) = R1 ./ (σc (R2))
o
puede escribirse como c1 and c2 y en c1 solo hay columnas de R1 y en c2 solo
hay columnas de R2:
σc (R1 ./ R2) = σc1 R1 ./ (σc2 (R2))

7. π es distributivo respecto del join si en la condición solo intervienen columnas incluidas


en la lista de proyección:

199

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
πp (R1 ./ R2) = πP −R1 R1 ./ (πP −R1 (R2))

8. En álgebra relacional, son conmutativas la unión, la intersección y el join y no conmu-


tativas la diferencia y la división.

9. En álgebra relacional, son asociativas la unión, la intersección y el join y no asociativas


la diferencia y la división.

10. Idempotencia (A ⊗ A = A). Son idempotentes la unión la intersección y el join.

200

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.
Índice de figuras

1.1. Ventana principal de XAMPP . . . . . . . . . . . . . . . . . . . . . . . . . . 10


1.2. Creando una base de datos con XAMPP . . . . . . . . . . . . . . . . . . . . 11
1.3. Contenido de la base de datos libreria . . . . . . . . . . . . . . . . . . . . . . 12
1.4. Definiendo la estructura de una tabla categoria . . . . . . . . . . . . . . . . 13
1.5. Definiendo relaciones entre tablas categoria . . . . . . . . . . . . . . . . . . . 14
1.6. Definiendo relaciones entre tablas categoria . . . . . . . . . . . . . . . . . . . 15
1.7. Contenido de la tabla categoria . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.8. Modo consola de XAMPP categoria . . . . . . . . . . . . . . . . . . . . . . . 17
1.9. Gestión de usuarios categoria . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.10. Transferencia de privilegios . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

2.1. Test sobre BCNF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

3.1. Ventana principal de eXist-db . . . . . . . . . . . . . . . . . . . . . . . . . . 96


3.2. Ventana de administración de eXist-db . . . . . . . . . . . . . . . . . . . . . 96
3.3. Adición de documentos en eXist-db . . . . . . . . . . . . . . . . . . . . . . . 97
3.4. Ventana de edición en eXist-db . . . . . . . . . . . . . . . . . . . . . . . . . 97
3.5. Ventana de xQuery tester . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

4.1. Esquema de MapReduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

5.1. Datalog y los Simpson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147


5.2. Secuencia de llamadas para el cómputo de la función nel . . . . . . . . . . . 160

6.1. Estado inicial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174


6.2. Inserción del registro ”Einstein” . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.3. Inserción de los registros ”Gold” y ”El Said” . . . . . . . . . . . . . . . . . . 175
6.4. Inserción del registro ”Katz” . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
6.5. Inserción de once registros más . . . . . . . . . . . . . . . . . . . . . . . . . 176
6.6. Inserción del registro ”Kim” . . . . . . . . . . . . . . . . . . . . . . . . . . . 177

7.1. Transiciones posibles entre los estados de una transacción . . . . . . . . . . . 181

8.1. Operaciones del álgebra relacional . . . . . . . . . . . . . . . . . . . . . . . . 197

201

a64b0469ff35958ef4ab887a898bd50bdfbbe91a-1600742
Reservados todos los derechos. No se permite la explotación económica ni la transformación de esta obra. Queda permitida la impresión en su totalidad.

También podría gustarte