Vous êtes sur la page 1sur 96

INSTITUTO SUPERIOR TECNOLÓGICO

PRIMERO DE MAYO
Tecnología Superior en Desarrollo de Software

Base de Datos Avanzada


III CICLO

GUÍA DE ESTUDIO

Autor:
Ing. Diego Jhonatan Chamba Saca

Yantzaza – Ecuador
Contenido
UNIDAD 1. SENTENCIAS DDL Y DML ......................................................................................... 4
1.1. CONCEPTOS BÁSICOS ............................................................................................... 8
1.2 Lenguaje de Definición de datos DDL ................................................................................. 8
1.3. Lenguaje de Manipulación de datos DML ......................................................................... 13
1.4. Sentencias select .............................................................................................................. 15
1.5. Uso de where, order by, group by, sum, limit ................................................................... 17
1.6 Funciones de agrupamiento (count - max - min - sum - avg) ............................................ 18
UNIDAD II Implementación de Índices, Trigger y vistas .............................................................. 27
2.1. Introducción, Entorno y uso de índices ............................................................................. 27
2.2. TIPOS Y ESTRUCTURA DE ÍNDICES ............................................................................. 28
2.3. CREACIÓN Y ELIMINACIÓN DE TRIGGERS ................................................................. 31
2.4. USO DE VISTAS Y SINTAXIS .......................................................................................... 41
2.5. VENTAJAS Y USO DE VISTAS........................................................................................ 43
UNIDAD III PROCEDIMIENTOS ALMACENADOS, INDICES Y CURSORES ........................... 45
3.1 Procedimientos almacenados y Tablas de Permisos ........................................................ 45
3.2 Introducción a índices ........................................................................................................ 50
3.3. Bucles y Cursores ............................................................................................................. 51
UNIDAD IV TRANSACCIONES, CONCURRENCIAS Y SEGURIDAD DE BASE DE DATOS .. 62
4.1. Introducción a transacciones ......................................................................................... 62
4.2. Lecturas Consistentes .................................................................................................... 70
4.3. Aplicación de Lecturas Consistentes ............................................................................. 72
4.4. Transacciones ................................................................................................................ 75
4.5. Control de Concurrencias .............................................................................................. 76
4.6. Control de concurrencias por DBMS ............................................................................. 77
4.7. Ejemplo de Transacciones por concurrencias ............................................................... 78
4.8. Tipos de Usuario ............................................................................................................ 92
4.9. Implementación de políticas de seguridad ..................................................................... 93
4.10. Aplicación ..................................................................................................................... 94
Bibliografía ................................................................................................................................... 95
Índice de Tablas
Tabla 1: Ejemplo de transacciones .............................................................................................. 79

3
PERFIL DE LA CARRERA

Formar profesionales con pensamiento crítico, creativo y ético; capaz de analizar,


diseñar, codificar e implementar sistemas informáticos mediante la aplicación de
metodologías de software para satisfacer las necesidades del mercado; basados
en la ciencia de la matemática aplicada, enfocada a fortalecer el sector de la
tecnología y sus usuarios, difundiendo los avances tecnológicos y resolviendo
por medio de la sistematización problemas presentados en las empresas y la
sociedad en general aplicando competencias profesionales mediante la
formación de talento humano que procese las aplicaciones de forma supervisada
trabajando en equipos bajo estándares de codificación, aportando
significativamente a la transformación social, dentro de los contextos
enmarcados en la productividad nacional.
CONTRIBUCIÓN DE LA ASIGNATURA AL PERFIL
PROFESIONAL

 Analizar los requerimientos del usuario mediante metodologías de


desarrollo de software
 Diseñar la interfaz del usuario, estructura del sistema y la base de dato,
basado en el análisis de requerimientos
 Desarrollar software en ambiente: web, escritorio y móvil
 Crear la Base de Datos mediante el modelo físico, para organizar,
almacenar y manipular la información.
 Implementar el software elaborado en el ambiente de trabajo

5
INTRODUCCIÓN.

Bases de Datos Avanzadas es una asignatura obligatoria que consta de 4


créditos y que dotará al estudiante de conceptos avanzados en el área de las
bases de datos.

La asignatura comienza con un tema que tiene como objetivo introducir aspectos
avanzados relacionados con las bases de datos. A continuación, se explica el
procesamiento de transacciones, el control de la concurrencia y las técnicas de
recuperación, además de descripciones de cómo se materializan estos
conceptos en SQL (Structured Query Language). Tras el estudio de las
transacciones se presentan modelos avanzados como las bases de datos
orientadas a objetos y las bases de datos distribuidas. Seguidamente, se
explican varios temas relacionados con el análisis de información en bases de
datos y la extracción de conocimiento a partir de éstas. Finalmente se presenta
XML, una forma alternativa de estructuración de la información, y se introducen
las principales tecnologías y aplicaciones emergentes en la materia.
OBJETIVO GENERAL DE LA ASIGNATURA.

Realizar modelos entidad -relación de nivel medio de complejidad.

Al finalizar esta materia el estudiante estará en la capacidad de implementar una


base de datos de alta complejidad, la cual puede ser usada en cualquier
desarrollo de un sistema

7
UNIDAD 1. SENTENCIAS DDL Y DML

1.1. CONCEPTOS BÁSICOS

(Java, 2010) Las sentencias SQL se dividen en dos categorías; Lenguaje de definición de
datos; data definition language (DDL) y Lenguaje de manipulación de datos; data
manipulation language (DML).

La recuperación de los datos en el lenguaje SQL se realiza mediante la sentencia SELECT,


seleccionar. Esta sentencia permite indicar al SGBD la información que se quiere recuperar. Esta
es la sentencia SQL, con diferencia, más habitual. La sentencia SELECT consta de cuatro partes
básicas:

 La cláusula SELECT seguida de la descripción de lo que se desea ver, los nombres de


las columnas a seleccionar. Esta parte es obligatoria.
 La cláusula FROM seguida de la especificación de las tablas de las que se han de
obtener los datos. Esta parte es obligatoria.
 La cláusula WHERE seguida por un criterio de selección, una condición. Esta parte es
opcional.
 La cláusula ORDER BY seguida por el criterio de ordenación. Esta parte es opcional.

1.2 Lenguaje de Definición de datos DDL


Las sentencias DDL se utilizan para crear y modificar la estructura de las tablas, así como otros
objetos de la base de datos.

 CREATE - para crear objetos en la base de datos.

(Barrena, 2015) Para repasar la sentencia CREATE TABLE de MySQL, su funcionalidad es la


de crear una tabla con todas sus características en una base de datos MySQL.

Tras crear una base de datos los tipos de dato que podemos guardar son:

 Numéricos
 De fecha
 Tipo string
Otras sentencias para trabajar con tablas son:

 TRUNCATE TABLE
 DROP TABLE
 MySQL SELECT
 ALTER TABLE
 MySQL INSERT
 RENAME TABLE
Veamos a las diferentes sintaxis que tenemos tener en la creación de una tabla extraídas de
su web oficial.

Sintaxis básica de la sentencia CREATE TABLE de MySQL

CREATE TABLE nombre_tabla;

Con el código superior tenemos la sentencia estándar para crear la tabla, solamente tenemos
que poner el nombre de la tabla, nombre de la columna y su tipo.

Podemos tener una base de datos con numerosas tablas, por lo que al crear una nueva
podemos tener una existente con el mismo nombre. Para evitar problemas debemos usar la
sentencia ‘IF NOT EXIST‘:

CREATE TABLE IF NOT EXISTS nombre_tabla

Quedando una estructura similar a esta:

CREATE TABLE IF NOT EXISTS nombre_tabla


(definición de la tabla,
definición de columnas,
tipos de columnas
);

CONSTRAINT es una restricción, el nombre de la restricción debe ser única en la base de


datos. Estas exigen la integridad de los datos de las columnas de la tabla.

Explico cada opción del código superior:

 PRIMARY KEY: Solo se puede crear una primary key por tabla, es la clave primaria
que identifica de manera única cada registro/fila de la tabla. Por ejemplo, el documento
de identidad DNI de una persona.

 INDEX|KEY: Ambas son sinónimas, puede haber una o varias. Establecen los índices de
la tabla con los cuales se pueden agilizar las búsquedas en la base de datos. De esta
manera se evita la búsqueda de un parámetro por cada columna de la tabla. Es como un
índice de un libro con el que nos evitamos recorrer cada página.

9
 UNIQUE: Es una restricción por la cual el valor de dicha columna debe ser único y diferente
al del valor de dicha columna de resto de registros. Por ejemplo, se suele usar con las
columnas declaradas como primary key.

 FULLTEXT: Es un índice que solo funciona con las columnas con formato char, varchar,
text y con almacenamiento MyISAM. Este índice facilita las grandes búsquedas sobre
texto y realiza automáticamente comparaciones de texto sobre una cadena dada. Realiza
búsquedas más afinadas que la sentencia LIKE. Se ignoran las palabras con menos de 4
letras y las palabras que aparezcan en más del 50% de los registros.

 FOREIGN KEY: Clave foránea, es un índice por el cual podemos relacionar 2 tablas. Este
valor debe existir en ambas tablas, por ejemplo, el código postal de la tabla ‘usuarios’ y la
tabla ‘población’.

TIPOS DE DATOS

tipo_dato [NOT NULL | NULL] [DEFAULT


valor_defecto]
[AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY]
KEY]
[COMMENT 'string']
[COLUMN_FORMAT {FIXED|DYNAMIC|DEFAULT}]
[STORAGE {DISK|MEMORY|DEFAULT}]
[reference_definition]
Os explico las opciones:

 NOT NULL | NULL: Establecemos si el valor de la columna debe rellenarse


obligatoriamente o no.

 AUTO_INCREMENT: Establece un valor inicial para un incremento posterior con la


inserción de cada nuevo registro.

 COMMENT: Comentario a modo informativo de la columna.

 COLUMN_FORMAT: Establece la ocupación de la columna, FIXED para un valor fijo,


DYNAMIC para un valor variable y DEFAULT para una ocupación por defecto.

 STORAGE: Posibilidad de almacenamiento en memoria o disco.

Tipos de dato de cada columna de la tabla

BIT[(length)]
TINYINT[(length)] [UNSIGNED] [ZEROFILL]
SMALLINT[(length)] [UNSIGNED] [ZEROFILL]
MEDIUMINT[(length)] [UNSIGNED] [ZEROFILL]
INT[(length)] [UNSIGNED] [ZEROFILL]
INTEGER[(length)] [UNSIGNED] [ZEROFILL]
BIGINT[(length)] [UNSIGNED] [ZEROFILL]
REAL[(length,decimals)] [UNSIGNED] [ZEROFILL]
DOUBLE[(length,decimals)] [UNSIGNED] [ZEROFILL]
FLOAT[(length,decimals)] [UNSIGNED] [ZEROFILL]
DECIMAL[(length[,decimals])] [UNSIGNED] [ZEROFILL]
NUMERIC[(length[,decimals])] [UNSIGNED] [ZEROFILL]
DATE
TIME
TIMESTAMP
DATETIME
YEAR
CHAR[(length)] [BINARY]
[CHARACTER SET charset_name] [COLLATE collation_name]
VARCHAR(length) [BINARY]
[CHARACTER SET charset_name] [COLLATE collation_name]
BINARY[(length)]
VARBINARY(length)
TINYBLOB
BLOB
MEDIUMBLOB
LONGBLOB
TINYTEXT [BINARY]
[CHARACTER SET charset_name] [COLLATE collation_name]
TEXT [BINARY]
[CHARACTER SET charset_name] [COLLATE collation_name]
MEDIUMTEXT [BINARY]
[CHARACTER SET charset_name] [COLLATE collation_name]
LONGTEXT [BINARY]
[CHARACTER SET charset_name] [COLLATE collation_name]
ENUM(value1,value2,value3,...)
[CHARACTER SET charset_name] [COLLATE collation_name]
SET(value1,value2,value3,...)
[CHARACTER SET charset_name] [COLLATE collation_name]
spatial_type
Ejemplo para crear una tabla en MySQL con CREATE TABLE

11
Aquí os pongo un ejemplo de una sentencia SQL para crear la tabla en una base de datos
MySQL:

CREATE TABLE IF NOT EXISTS `clientes` (


`nif` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Clave primaria',
`nombre` varchar(50) NOT NULL COMMENT 'nombre cliente',
`apellidos` varchar(100) NOT NULL COMMENT 'Apellidos cliente',
`telefono` int(9) NOT NULL COMMENT 'móvil',
`codigo_postal` int(5) DEFAULT NULL,
`edad` int(3) DEFAULT NULL,
`sexo` char(1) NOT NULL,
`profesion` text NOT NULL,
PRIMARY KEY (`nif`),
UNIQUE KEY `telefono` (`telefono`),
KEY `nombre` (`nombre`),
FULLTEXT KEY `apellidos` (`apellidos`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='tabla de clientes';

 ALTER - modifica la estructura de la base de datos.

Cambia la estructura de una tabla. Por ejemplo, agrega o elimina columnas, crea o elimina
índices, modificar el tipo de columnas existentes o renombrar columnas o la propia tabla.

También modifica las características tales como el tipo de almacenamiento utilizado para
las tablas. Otras sentencias para trabajar con tablas son:

Sintaxis de alter table

ALTER [ONLINE | OFFLINE] [IGNORE] TABLE nombre_tabla


[alter specification [, alter specification] ...]

Ejemplos prácticos de alter table en mysql:

- renombrar y/o cambiar el nombre la tabla


ALTER TABLE nombre_tabla RENAME nombre_nuevo_tabla;
- Cambiar el juego de caracteres de la tabla:
ALTER TABLE nombre_tabla CHARACTER SET latin1;
- Cambiar el engine (motor) almacenamiento
ALTER TABLE nombre_tabla ENGINE = InnoDB;
- Eliminar una columna de la tabla
ALTER TABLE nombre_tabla DROP COLUMN nombre_columna;
- Eliminar varias columnas de una tabla

ALTER TABLE nombre_tabla DROP COLUMN nombre_columna, DROP COLUMN


nombre_columna2;

 DROP - borra objetos de la base de datos.

Con la sentencia DROP TABLE de MySQL podemos borrar de manera rápida y sencilla una
tabla de una base de datos.

Sintaxis:

DROP TABLE nombre_tabla

Esta es la sintaxis básica con la que si tenemos permisos para hacerlo…. borraríamos una tabla.
Podemos afinar la sentencia añadiendo ‘IF EXISTS‘, es decir si existe

DROP TABLE IF EXISTS nombre_tabla

De esta manera solo borraríamos la tabla si esta existe. Es recomendable usar ‘IF EXISTS‘ ya
que si la tabla no existe cortaría la ejecución del código MySQL restante.

Se pueden borrar varias tablas separando el nombre de cada una con una ‘,’.

DROP TABLE IF EXISTS nombre_tabla, nombre_tabla2, nombre_tabla3

 TRUNCATE - elimina todos los registros de la tabla, incluyendo todos los espacios asignados a
los registros.

La sentencia TRUNCATE TABLE con la que podemos vaciar una tabla de una base de
datos MySQL

Sintaxis:
TRUNCATE TABLE nombre_tabla;

1.3. Lenguaje de Manipulación de datos DML

(uaeh, 2010) Lenguaje de Manipulación de Datos

Lenguaje de Manipulación de Datos (Data Manipulation Language, DML) es un idioma


proporcionado por los sistemas gestores de bases de datos que permite a los usuarios de la
misma llevar a cabo las tareas de consulta o modificación de los datos contenidos en las Bases
de Datos del Sistema Gestor de Bases de Datos. El lenguaje de manipulación de datos más

13
popular hoy día es SQL, usado para recuperar y manipular datos en una base de datos relacional.
Otros ejemplos de DML son los usados por bases de datos IMS/DL1, CODASYL u otras.

Elementos del lenguaje de manipulación de datos

Select, Insert, Delete y Update

Clasificación de los DML

Se clasifican en dos grandes grupos:

 lenguajes de consulta procedimentales

Lenguajes procedimentales. En este tipo de lenguaje el usuario da instrucciones al sistema para


que realice una serie de procedimientos u operaciones en la base de datos para calcular un
resultado final.

 lenguajes de consulta no procedimentales

En los lenguajes no procedimentales el usuario describe la información deseada sin un


procedimiento específico para obtener esa información.

Lenguaje de manipulación de datos (DML)

Un lenguaje de manipulación de datos (Data Manipulation Language, o DML en inglés) es un


lenguaje proporcionado por el sistema de gestión de base de datos que permite a los usuarios
llevar a cabo las tareas de consulta o manipulación de los datos, organizados por el modelo de
datos adecuado.

El lenguaje de manipulación de datos más popular hoy día es SQL, usado para recuperar y
manipular datos en una base de datos relacional. Otros ejemplos de DML son los usados por
bases de datos IMS/DL1, CODASYL u otras.

1- INSERT. - Una sentencia INSERT de SQL agrega uno o más registros a una (y sólo una) tabla
en una base de datos relacional.

Ejemplo 1 (inserto valores alumno pepe en la materia spd2 a la tabla cursada):

INSERT INTO ''cursada'' (''alumno'', ''materia'') VALUES (''pepe'', ''spd2'')

2-UPDATE. - Una sentencia UPDATE de SQL es utilizada para modificar los valores de un
conjunto de registros existentes en una tabla.

Ejemplo 1 (modifico la materia donde el alumno sea pepe):

UPDATE ''cursada'' SET ''materia''= ''spd3'' WHERE ''alumno''= ''pepe''

3- DELETE

Una sentencia DELETE de SQL borra uno o más registros existentes en una tabla.
Ejemplo 1 (borro todos los valores de las columnas alumno y materia donde la materia sea spd2):

DELETE FROM ''cursada'' WHERE ''materia''= ''spd2''

1.4. Sentencias select

La sentencia de MySQL SELECT, su funcionalidad es la de realizar consultas sobre una o


varias tablas de una base de datos para extraer un determinado número de filas (resultados).

SELECT

[ALL | DISTINCT | DISTINCTROW ]

[HIGH_PRIORITY]

[STRAIGHT_JOIN]

[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]

[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]

select_expr [, select_expr ...]

[FROM table_references

[PARTITION partition_list]

[WHERE where_condition]

[GROUP BY {col_name | expr | position}

[ASC | DESC], ... [WITH ROLLUP]]

[HAVING where_condition]

[ORDER BY {col_name | expr | position}

[ASC | DESC], ...]

[LIMIT {[offset,] row_count | row_count OFFSET offset}]

[PROCEDURE procedure_name(argument_list)]

15
[INTO OUTFILE 'file_name'

[CHARACTER SET charset_name]

export_options

| INTO DUMPFILE 'file_name'

| INTO var_name [, var_name]]

[FOR UPDATE | LOCK IN SHARE MODE]]

Con SELECT podemos seleccionar una o varias columnas de datos.


FROM table_references nos vale para seleccionar una o varias tablas que almacenan las
columnas previamente indicadas.

En WHERE where_condition se especifican la o las condiciones que se deben cumplir para los
valores solicitados.

Dentro de la claúsula WHERE tenemos los siguientes condicionantes que son importantes y
útiles:

GROUP BY: Nos permite agrupar los datos obtenidos de la base de datos.

HAVING: Permite hacer cálculos y condiciones más complejas que no se pueden hacer con la
cláusula
WHERE.

ORDER BY: Ordenación por columna.

LIMIT: Especificación del número de filas devueltas.


Ejemplos:

SELECT * FROM usuarios

SELECT nombre FROM usuarios

SELECT nombre, apellidos FROM usuarios

SELECT nombre, apellidos FROM usuarios, clientes

SELECT nombre FROM usuarios WHERE edad = 30


1.5. Uso de where, order by, group by, sum, limit

La cláusula WHERE MySQL, es la de condicionar las consultas SELECT que se


realizan a una base de datos.

Sintaxis:

SELECT campo1, campo2...

FROM tabla1, tabla2...

[WHERE condicion1 [AND [OR]] condicion2...

Tal y como podemos apreciar la sintaxis es sencilla, realizamos la


consulta SELECT de la tabla o tablas y después vendrían las
posibles condiciones con WHERE.

Operadores condicionales de la cláusula WHERE MySQL

Veamos los operadores condicionales:

 ‘=‘ El operador = determina si el valor de los campos comparados son iguales.

 ‘!=‘ El operador != determina si el valor de los campos comparados son distintos.

 ‘>‘ El operador > determina si el primer valor es mayor que el segundo valor comparado.

 ‘<‘ El operador < determina si el primer valor es menor que el segundo valor comparado.

 ‘>=‘ El operador >= determina si el primer valor es mayor o igual que el segundo valor
comparado.

 ‘<=‘ El operador <= determina si el primer valor es menor o igual que el segundo valor
comparado.

Order By.

La cláusula de MySQL ORDER BY que tiene como finalidad ordenar los resultados de las
consultas por columnas en vez del campo índice por defecto.

Sintaxis:

SELECT * FROM nombre_tabla

ORDER BY campo1, campo2;

Ejemplos:

17
1. SELECT * FROM nombre_tabla WHERE campo1 = condicionante ORDER BY
campo_ordenacion;
2. SELECT * FROM nombre_tabla ORDER BY campo_ordenacion1 DESC,
campo_ordenacion2 DESC;
3. SELECT * FROM nombre_tabla WHERE campo = condicionante ORDER BY
campo_ordenacion1 DESC, campo_ordenacion2 DESC;
4. SELECT * FROM nombre_tabla WHERE campo1 > condicionante ORDER BY
campo_ordenacion ASC;
5. SELECT * FROM nombre_tabla WHERE campo_ordenacion = condicionante AND
campo_ordenacion2 > condicionante2 ORDER BY campo_ordenacion;
6. SELECT * FROM empleados ORDER BY edad, antiguedad;
7. SELECT * FROM empleados WHERE sexo = 'mujer' ORDER BY edad;
8. SELECT * FROM empleados ORDER BY edad DESC, antiguedad DESC;
9. SELECT * FROM empleados WHERE sexo = 'hombre' ORDER BY antiguedad DESC,
edad DESC;
10. SELECT * FROM empleados WHERE edad > 35 ORDER BY nombre ASC;
11. SELECT * FROM empleados WHERE edad > 35 ORDER BY nombre ASC;

1.6 Funciones de agrupamiento (count - max - min - sum - avg)

(Jairo, 2012) Existen en MySQL funciones que nos permiten contar registros,
calcular sumas, promedios, obtener valores máximos y mínimos. Ya hemos
aprendido "count()", veamos otras.

La función "sum()" retorna la suma de los valores que contiene el campo


especificado. Por ejemplo, queremos saber la cantidad de libros que tenemos
disponibles para la venta:

select sum(cantidad) from libros;

También podemos combinarla con "where". Por ejemplo, queremos saber


cuántos libros tenemos de la editorial "Planeta":

select sum(cantidad) from libros


where editorial ='Planeta';

Para averiguar el valor máximo o mínimo de un campo usamos las funciones


"max()" y "min()" respectivamente. Ejemplo, queremos saber cuál es el mayor
precio de todos los libros:

select max(precio) from libros;


Queremos saber cuál es el valor mínimo de los libros de "Rowling":

select min(precio) from libros


where autor like '%Rowling%';

La función avg() retorna el valor promedio de los valores del campo especificado.
Por ejemplo, queremos saber el promedio del precio de los libros referentes a
"PHP":

select avg(precio) from libros


where titulo like '%PHP%';

Estas funciones se denominan "funciones de agrupamiento" porque operan


sobre conjuntos de registros, no con datos individuales.

Tenga en cuenta que no debe haber espacio entre el nombre de la función y el


paréntesis, porque puede confundirse con una referencia a una tabla o campo.
Las siguientes sentencias son distintas:

select count(*) from libros;


select count (*) from libros;

La primera es correcta, la segunda incorrecta.

Problema Resuelto:

Trabajamos con la tabla "libros" que registra la información de los libros que
vende una librería.

Eliminamos la tabla, si existe:

drop table if exists libros;

Creamos la tabla:

create table libros(


codigo int unsigned auto_increment,
titulo varchar(40) not null,
autor varchar(30),
editorial varchar(15),
precio decimal(5,2) unsigned,
cantidad mediumint unsigned,
primary key(codigo)
);

Ingresamos algunos registros:

19
insert into libros (titulo,autor,editorial,precio,cantidad)
values('El aleph','Borges','Planeta',15,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Martin Fierro','Jose Hernandez','Emece',22.20,200);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Antologia poetica','J.L. Borges','Planeta',40,150);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Aprenda PHP','Mario Molina','Emece',18.20,200);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Cervantes y el quijote','Bioy Casares- J.L. Borges',
'Paidos',36.40,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Manual de PHP', 'J.C. Paez', 'Paidos',30.80,120);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Harry Potter y la piedra filosofal','J.K. Rowling',
'Paidos',45.00,50);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Harry Potter y la camara secreta','J.K. Rowling',
'Paidos',46.00,100);
insert into libros (titulo,autor,editorial,precio,cantidad)
values('Alicia en el pais de las maravillas','Lewis Carroll',
'Paidos',null,200);

Para conocer la suma de las cantidades de libros que tenemos disponibles


tipeamos:

select sum(cantidad) from libros;

Retorna 1220; verifique la suma, sumando los valores de todos los registros del
campo "cantidad".

Solicitamos la suma de las cantidades de los libros de la editorial "Planeta":

select sum(cantidad) from libros


where editorial ='Planeta';

Retorna 250; verifique el total sumando las cantidades de los libros cuya
editorial sea "Planeta".
Si queremos saber cuál es el mayor precio de los libros usamos:

select max(precio) from libros;

Devuelve 46.

Verifiquemos lo anterior realizando una consulta ordenada por precio de forma descendente:

select * from libros


order by precio desc;

Para obtener el valor mínimo de los libros de "Rowling" utilizamos la siguiente


sentencia:
select min(precio) from libros
where autor like '%Rowling%';

Retorna 45.

Verifiquemos el resultado realizando una consulta "select" con la condición


anterior ordenada por precio:

select * from libros


where autor like '%Rowling%'
order by 5;

Solicitamos el promedio del precio de los libros que tratan sobre "PHP":

select avg(precio) from libros


where titulo like '%PHP%';

Retorna 24.50...

Verifiquemos el resultado seleccionado los libros de "PHP" y calculando el


promedio manualmente:

select * from libros


where titulo like '%PHP%';

Recuerde que no debe haber espacio entre el nombre de la función y el


paréntesis. Pruebe las siguientes sentencias:

select count(*) from libros;


select count (*) from libros;

La segunda no se ejecuta, aparece un mensaje de error.

Problema Propuesto :

Un comercio que tiene un stand en una feria registra en una tabla


llamada "visitantes" algunos datos de las personas que visitan o
compran en su stand para luego enviarle publicidad de sus
productos.

1- Elimine la tabla "visitantes", si existe.

2- Créela con la siguiente estructura:

create table visitantes(


nombre varchar(30),
edad tinyint unsigned,
sexo char(1),
domicilio varchar(30),
ciudad varchar(20),
21
telefono varchar(11),
montocompra decimal (6,2) unsigned
);

Note que no tiene clave primaria, no la necesita.

3- Ingrese algunos registros:

insert into visitantes (nombre,edad,


sexo,domicilio,ciudad,telefono,montocompra)
values ('Susana Molina', 28,'f','Colon 123','Cordoba',null,45.50);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Marcela Mercado',36,'f','Avellaneda
345','Cordoba','4545454',0);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Alberto Garcia',35,'m','Gral. Paz 123','Alta
Gracia','03547123456',25);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Teresa Garcia',33,'f','Gral. Paz 123','Alta
Gracia','03547123456',0);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Roberto Perez',45,'m','Urquiza
335','Cordoba','4123456',33.20);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Marina Torres',22,'f','Colon 222','Villa
Dolores','03544112233',25);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Julieta Gomez',24,'f','San Martin 333','Alta
Gracia','03547121212',53.50);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Roxana Lopez',20,'f','Triunvirato 345','Alta Gracia',null,0);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Liliana Garcia',50,'f','Paso 999','Cordoba','4588778',48);
insert into visitantes (nombre,edad,
sexo,domicilio,ciudad,telefono,montocompra)
values ('Juan Torres',43,'m','Sarmiento
876','Cordoba','4988778',15.30);

4- Solicite la cantidad de visitantes al stand (10 registros):

5- Muestre la suma de la compra de todos los visitantes de


"Alta Gracia" (78.5):

6- Muestre el valor máximo de las compras efectuadas


(53.50):

7- Muestre la edad menor de los visitantes (20):

8- Muestre el promedio de edades de los visitantes (33.66):

9- Muestre el promedio del monto de compra (24.55):


Otros problemas:
A) Una academia de informática dicta distintos cursos y
almacena en una tabla
llamada "inscripciones" la siguiente información: nombre del
curso,
documento del alumno, fecha en que se inscribe el alumno,
monto del
pago (algunos dejan una seña, otros pagan el curso
completo).

1- Elimine la tabla si existe.

2- cree la tabla:

create table inscripciones(


nombre varchar(30),
documento char(8),
fechainscripto date,
pago decimal(5,2) unsigned not null
);

3- Ingrese algunos registros:

insert into inscripciones values('PHP básico',


'22333444','2006-08-10',50);
insert into inscripciones values('PHP básico',
'23333444','2006-08-10',50);
insert into inscripciones values('PHP básico',
'24333444','2006-08-11',30);
insert into inscripciones values('PHP experto',
'25333444','2006-08-11',0);
insert into inscripciones values('PHP experto',
'26333444','2006-08-12',200);
insert into inscripciones values('JavaScript básico',
'22333444','2006-08-10',100);
insert into inscripciones values('Operador de PC',
'27333444','2006-08-12',10);
insert into inscripciones values('Operador de PC',
'28333444','2006-08-13',50);
insert into inscripciones values('Operador de PC',
'29333444','2006-08-14',40);
insert into inscripciones values('Operador de PC',
'30333444','2006-08-14',0);
insert into inscripciones values('Diseño web',
'29333444','2006-08-14',200);
insert into inscripciones values('Diseño web',
'30333444','2006-08-14',0);

23
4- calcule la cantidad de inscriptos para el curso de
"Operador de PC":

5- Calcule la suma recaudada por los pagos de los cursos el


día "2006-08-10":

6- Calcule el promedio de los pagos de los inscriptos:

7- Muestre el máximo y el mínimo valor de pago, sin


considerar quienes no pagan:

8- Vea en cuántos cursos se inscribió el alumno con


documento "22333444" y cuánto
abonó en total:

B) Trabaje con la tabla "peliculas" de un video club.

1- Elimine la tabla, si existe.

2- Créela con la siguiente estructura:

-codigo (entero sin signo, autoincrementable),


-titulo (cadena de 30), not null,
-actor (cadena de 20),
-duracion (entero sin signo no mayor a 200 aprox.),
-clave primaria (codigo).

3- Ingrese los siguientes registros:

insert into peliculas (titulo,actor,duracion)


values('Mision imposible','Tom Cruise',120);
insert into peliculas (titulo,actor,duracion)
values('Harry Potter y la piedra filosofal','Daniel R.',180);
insert into peliculas (titulo,actor,duracion)
values('Harry Potter y la camara secreta','Daniel R.',190);
insert into peliculas (titulo,actor,duracion)
values('Mision imposible 2','Tom Cruise',120);
insert into peliculas (titulo,actor,duracion)
values('Mujer bonita','Richard Gere',120);
insert into peliculas (titulo,actor,duracion)
values('Tootsie','D. Hoffman',90);
insert into peliculas (titulo,actor,duracion)
values('Un oso rojo',null,100);
insert into peliculas (titulo,actor,duracion)
values('Elsa y Fred','China Zorrilla',110);
insert into peliculas (titulo,actor,duracion)
values('Mrs. Johns','Richard Gere',180);

4- Muestre el valor de duración más grande:


5- Muestre el promedio de duración de las películas:

6- Cuente la cantidad de películas que comiencen con la


cadena "Harry Potter":

7- Un socio alquiló todas las películas en las cuales trabaja


"Richard Gere",
quiere saber el total de minutos que duran todas sus
películas:

C) Una concesionaria de autos vende autos usados y


almacena la información
en una tabla llamada "autos".

1- Elimine la tabla "autos" si existe.

2- Cree la tabla con la siguiente estructura:

create table autos(


patente char(6),
marca varchar(20),
modelo char(4),
precio decimal(8,2) unsigned,
primary key(patente)
);

3- Ingrese los siguientes registros:

insert into autos


values('ACD123','Fiat 128','1970',15000);
insert into autos
values('ACG234','Renault 11','1990',40000);
insert into autos
values('BCD333','Peugeot 505','1990',80000);
insert into autos
values('GCD123','Renault Clio','1990',70000);
insert into autos
values('BCC333','Renault Megane','1998',95000);
insert into autos
values('BVF543','Fiat 128','1975',20000);

4- Muestre el valor del auto más caro y más barato:

5- Muestre el valor de auto más caro de 1990:

6- Muestre el promedio de los precios de los autos "Fiat 128":

7- Calcule el valor en dinero de todos los autos marca


"Renault" con
modelos menores a "1995":

25
D) Un comercio guarda la información de sus ventas en una
tabla llamada
"facturas" en la que registra el número de factura, la
descripción de los
items comprados, el precio por unidad de los items y la cantidad.

1- Elimine la tabla si existe.

2- Cree la tabla:

create table facturas(


numero int(10) zerofill,
descripcion varchar(30),
precioporunidad decimal(5,2) unsigned,
cantidad tinyint unsigned
);

3- Ingrese algunos registros:

insert into facturas values(504,'escuadra 20 cm.',2.5,100);


insert into facturas values(504,'escuadra 50 cm.',5,80);
insert into facturas values(2002,'compas plastico',8,120);
insert into facturas values(2002,'compas metal',15.4,100);
insert into facturas values(2002,'escuadra 20 cm.',2.5,100);
insert into facturas values(4567,'escuadra 50 cm.',5,200);

4- Cuente la cantidad de items de la factura número "2002":

5- Sume la cantidad de productos de la factura número "2002":

6- Muestre el total en dinero de la factura "504":


UNIDAD II Implementación de Índices, Trigger y vistas

2.1. Introducción, Entorno y uso de índices

(Amor, 2015)

Introducción

En la siguiente temática aprenderemos lo que son los índices y sus principales


características. De esta forma, podremos saber cómo optimizar nuestras
consultas de tipo SELECT mediante la creación y el uso de índices en nuestra
base de datos de tipo MySQL. Gracias al uso de índices, podremos reducir
fácilmente, y de forma considerable, el tiempo de ejecución de nuestras
consultas de tipo SELECT. Sobre todo, esta mejora en los tiempos de ejecución
será mayor cuanto más grandes (mayor cantidad de datos) sean las tablas de la
base de datos con la que estemos trabajando.

Entorno

Es importante considerar las especificaciones técnicas del equipo de cómputo


donde se pondrá en práctica las sentencias sql.

 Hardware:

Componentes básicos: Procesador Core I3, Memoria 2GB.

 Sistema Operativo: Windows 7

Uso de índices de Mysql

MySQL emplea los índices para encontrar las filas que contienen los valores
específicos de las columnas empleadas en la consulta de una forma más rápida.
Si no existiesen índices, MySQL empezaría buscando por la primera fila de la

27
tabla hasta la última buscando aquellas filas que cumplen los valores
establecidos para las columnas empleadas en la consulta. Esto implica que,
cuantas más filas tenga la tabla, más tiempo tardará en realizar la consulta. En
cambio, si la tabla contiene índices en las columnas empleadas en la consulta,
MySQL tendría una referencia directa hacia los datos sin necesidad de recorrer
secuencialmente todos ellos.

En general, MySQL emplea los índices para las siguientes acciones:

 Encontrar las filas que cumplen la condición WHERE de la consulta


cuyas columnas estén indexadas.
 Para recuperar las filas de otras tablas cuando se emplean operaciones
de tipo JOIN. Para ello, es importante que los índices sean del mismo
tipo y tamaño ya que aumentará la eficiencia de la búsqueda. Por
ejemplo: una operación de tipo JOIN sobre dos columnas que tengan
un índice del tipo INT (10).
 Disminuir el tiempo de ejecución de las consultas con ordenación
(ORDER BY) o agrupamiento (GROUP BY) si todas las columnas
presentes en los criterios forman parte de un índice.
 Si la consulta emplea una condición simple cuya columna de la
condición está indexada, las filas serán recuperadas directamente a
partir del índice, sin pasar a consular la tabla.

2.2. TIPOS Y ESTRUCTURA DE ÍNDICES

Tipos de índices:

A continuación, vamos a analizar los distintos tipos de índices que se pueden


crear y las condiciones que deben cumplir cada uno de ellos:
INDEX (NON-UNIQUE): este tipo de índice se refiere a un índice normal, no
único. Esto implica que admite valores duplicados para la columna (o columnas)
que componen el índice. No aplica ninguna restricción especial a los datos de la
columna (o columnas) que componen el índice, sino que se emplea simplemente
para mejorar el tiempo de ejecución de las consultas.

UNIQUE: este tipo de índice se refiere a un índice en el que todas las columnas
deben tener un valor único. Esto implica que no admite valores duplicados para
la columna (o columnas) que componen el índice. Aplica la restricción de que los
datos de la columna (o columnas) deben tener un valor único.

PRIMARY: este tipo de índice se refiere a un índice en el que todas las columnas
deben tener un valor único (al igual que en el caso del índice UNIQUE) pero con
la limitación de que sólo puede existir un índice PRIMARY en cada una de las
tablas. Aplica la restricción de que los datos de la columna (o columnas) deben
tener un valor único.

FULLTEXT: estos índices se emplean para realizar búsquedas sobre texto


(CHAR, VARCHAR y TEXT). Estos índices se componen por todas las palabras
que están contenidas en la columna (o columnas) que contienen el índice. No
aplica ninguna restricción especial a los datos de la columna (o columnas) que
componen el índice, sino que se emplea simplemente para mejorar el tiempo de
ejecución de las consultas. Este tipo de índices sólo están soportados por
InnoDB y MyISAM en MySQL 5.7.

SPATIAL: estos índices se emplean para realizar búsquedas sobre datos que
componen formas geométricas representadas en el espacio. Este tipo de índices
sólo están soportados por InnoDB y MyISAM en MySQL 5.7.

Es importante destacar que todos estos índices pueden construirse empleando


una o más columnas. Del mismo modo, el orden de las columnas que se
especifique al construir el orden es relevante para todos los índices menos para

29
el FULLTEXT (ya que este índice mira en TODAS las columnas que componen
el índice).
Para crear un índice, se empleará la siguiente estructura:
“CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name ON
table_name (index_col_name…) index_type;”

Donde:
index_name: es el nombre del índice.
table_name: es el nombre de la tabla donde se va a crear el índice.
index_col_name: nombre de la columna (o columnas) que formarán el índice.
index_type: es el tipo del índice. Se emplea con USING [BTREE | HASH].

Un ejemplo sería:
CREATE UNIQUE INDEX mi_indice_unico ON mi_tabla (mi_columna) USING
HASH;

Estructuras donde se almacenan los índices

Una vez hemos visto los tipos de índices, vamos a ver los distintos tipos de
estructuras que se pueden crear para almacenar los índices junto con las
características de cada uno de ellas:

 B-TREE: este tipo de índice se usa para comparaciones del tipo =, >,
<, >=, <=, BETWEEN y LIKE (siempre y cuando se utilice sobre
constantes que no empiecen por %). Para realizar búsquedas
empleando este tipo de índice, se empleará cualquier columna (o
conjunto de columnas) que formen el prefijo del índice. Por ejemplo: si
un índice está formado por las columnas [A, B, C], se podrán realizar
búsquedas sobre: [A], [A, B] o [A, B, C].
 HASH: este tipo de índice sólo se usa para comparaciones del tipo = o
<=>. Para este tipo de operaciones son muy rápidos en comparación a
otro tipo de estructura. Para realizar búsquedas empleando este tipo
de índice, se emplearán todas las columnas que componen el índice.

Un índice puede ser almacenado en cualquier tipo de estructura, pero, en función


del uso que se le vaya a dar, puede interesar crear el índice en un tipo
determinado de estructura o en otro. Por norma general, un índice siempre se
creará con la estructura de B-TREE, ya que es la estructura más empleada por
la mayoría de operaciones.

2.3. CREACIÓN Y ELIMINACIÓN DE TRIGGERS

(GUEBS, 2016) A partir de MySQL 5.0.2 se incorporó el soporte básico para


disparadores (Trigger). Un disparador es un objeto con nombre dentro de una
base de datos el cual se asocia con una tabla y se activa cuando ocurre en ésta
un evento en particular. Por ejemplo, las siguientes sentencias crean una tabla y
un disparador para sentencias INSERT dentro de la tabla. El disparador suma
los valores insertados en una de las columnas de la tabla:

mysql> CREATE TABLE account (acct_num INT, amount DECIMAL (10,2));


mysql> CREATE TRIGGER ins_sum BEFORE INSERT ON account
-> FOR EACH ROW SET @sum = @sum + NEW.amount;

Este capítulo describe la sintaxis para crear y eliminar disparadores, y muestra


algunos ejemplos de cómo utilizarlos. Las restricciones en el uso de
disparadores se tratan en Apéndice H, Restricciones en características de
MySQL.

Sintaxis de CREATE TRIGGER

CREATE TRIGGER nombre_disp momento_disp evento_disp


ON nombre_tabla FOR EACH ROW sentencia_disp

31
Un disparador es un objeto con nombre en una base de datos que se asocia con
una tabla, y se activa cuando ocurre un evento en particular para esa tabla.

El disparador queda asociado a la tabla nombre_tabla. Esta debe ser una tabla
permanente, no puede ser una tabla TEMPORARY ni una vista.

momento_disp es el momento en que el disparador entra en acción. Puede


ser BEFORE (antes) o AFTER (despues), para indicar que el disparador se
ejecute antes o después que la sentencia que lo activa.

evento_disp indica la clase de sentencia que activa al disparador. Puede


ser INSERT, UPDATE, oDELETE. Por ejemplo, un disparador BEFORE para
sentencias INSERT podría utilizarse para validar los valores a insertar.

No puede haber dos disparadores en una misma tabla que correspondan al


mismo momento y sentencia.

Por ejemplo, no se pueden tener dos disparadores BEFORE UPDATE. Pero sí


es posible tener los disparadores BEFORE UPDATE y BEFORE
INSERT o BEFORE UPDATE y AFTER UPDATE.

sentencia_disp es la sentencia que se ejecuta cuando se activa el disparador.


Si se desean ejecutar múltiples sentencias, deben colocarse entre BEGIN ...
END, el constructor de sentencias compuestas. Esto además posibilita emplear
las mismas sentencias permitidas en rutinas almacenadas.

Note: Antes de MySQL 5.0.10, los disparadores no podían contener referencias


directas a tablas por su nombre. A partir de MySQL 5.0.10, se pueden escribir
disparadores como el llamado testref, que se muestra en este ejemplo:

CREATE TABLE test1(a1 INT);


CREATE TABLE test2(a2 INT);
CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY
KEY);
CREATE TABLE test4(
a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
b4 INT DEFAULT 0
);

DELIMITER |

CREATE TRIGGER testref BEFORE INSERT ON test1


FOR EACH ROW BEGIN
INSERT INTO test2 SET a2 = NEW.a1;
DELETE FROM test3 WHERE a3 = NEW.a1;
UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
END
|

DELIMITER ;

INSERT INTO test3 (a3) VALUES


(NULL), (NULL), (NULL), (NULL), (NULL),
(NULL), (NULL), (NULL), (NULL), (NULL);

INSERT INTO test4 (a4) VALUES


(0), (0), (0), (0), (0), (0), (0), (0), (0), (0);

Si en la tabla test1 se insertan los siguientes valores:

mysql> INSERT INTO test1 VALUES


-> (1), (3), (1), (7), (1), (8), (4), (4);
Query OK, 8 rows affected (0.01 sec)
Records: 8 Duplicates: 0 Warnings: 0

Entonces los datos en las 4 tablas quedarán así:

mysql> SELECT * FROM test1;


+------+
| a1 |
+------+
33
| 1|
| 3|
| 1|
| 7|
| 1|
| 8|
| 4|
| 4|
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test2;


+------+
| a2 |
+------+
| 1|
| 3|
| 1|
| 7|
| 1|
| 8|
| 4|
| 4|
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test3;


+----+
| a3 |
+----+
| 2|
| 5|
| 6|
| 9|
| 10 |
+----+
5 rows in set (0.00 sec)

mysql> SELECT * FROM test4;


+----+------+
| a4 | b4 |
+----+------+
| 1| 3|
| 2| 0|
| 3| 1|
| 4| 2|
| 5| 0|
| 6| 0|
| 7| 1|
| 8| 1|
| 9| 0|
| 10 | 0|
+----+------+
10 rows in set (0.00 sec)

Las columnas de la tabla asociada con el disparador pueden referenciarse


empleando los alias OLD y NEW. OLD.nombre_col hace referencia a una
columna de una fila existente, antes de ser actualizada o
borrada. NEW.nombre_col hace referencia a una columna en una nueva fila a
punto de ser insertada, o en una fila existente luego de que fue actualizada.

El uso de SET NEW.nombre_col = valor necesita que se tenga el


privilegio UPDATE sobre la columna. El uso de SET nombre_var =
NEW.nombre_col necesita el privilegio SELECT sobre la columna.

Nota: Actualmente, los disparadores no son activados por acciones llevadas a


cabo en cascada por las restricciones de claves extranjeras. Esta limitación se
subsanará tan pronto como sea posible.

35
La sentencia CREATE TRIGGER necesita el privilegio SUPER. Esto se agregó
en MySQL 5.0.2.

Sintaxis de DROP TRIGGER

DROP TRIGGER [nombre_esquema.]nombre_disp

Elimina un disparador. El nombre de esquema es opcional. Si el esquema se


omite, el disparador se elimina en el esquema actual.

Anteriormente a la versión 5.0.10 de MySQL, se requería el nombre de tabla en


lugar del nombre de esquema. (nom_tabla.nom_disp).

Nota: cuando se actualice desde una versión anterior de MySQL 5 a MySQL


5.0.10 o superior, se deben eliminar todos los disparadores antes de actualizar y
volver a crearlos después, o DROP TRIGGER no funcionará luego de la
actualización.

La sentencia DROP TRIGGER necesita que se posea el privilegio SUPER, que


se introdujo en MySQL 5.0.2.

Utilización de disparadores

El soporte para disparadores se incluyó a partir de MySQL 5.0.2. Actualmente,


el soporte para disparadores es básico, por lo tanto hay ciertas limitaciones en
lo que puede hacerse con ellos. Esta sección trata sobre el uso de los
disparadores y las limitaciones vigentes.

Un disparador es un objeto de base de datos con nombre que se asocia a una


tabla, y se activa cuando ocurre un evento en particular para la tabla. Algunos
usos para los disparadores es verificar valores a ser insertados o llevar a cabo
cálculos sobre valores involucrados en una actualización.

Un disparador se asocia con una tabla y se define para que se active al ocurrir
una sentencia INSERT,DELETE, o UPDATE sobre dicha tabla. Puede también
establecerse que se active antes o despues de la sentencia en cuestión. Por
ejemplo, se puede tener un disparador que se active antes de que un registro
sea borrado, o después de que sea actualizado.
Para crear o eliminar un disparador, se emplean las sentencias CREATE
TRIGGER y DROP TRIGGER. La sintaxis de las mismas se describe
en Sección 20.1, “Sintaxis de CREATE TRIGGER” y Sección 20.2, “Sintaxis
de DROP TRIGGER”.

Este es un ejemplo sencillo que asocia un disparador con una tabla para cuando
reciba sentencias INSERT. Actúa como un acumulador que suma los valores
insertados en una de las columnas de la tabla.

La siguiente sentencia crea la tabla y un disparador asociado a ella:

mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));


mysql> CREATE TRIGGER ins_sum BEFORE INSERT ON account
-> FOR EACH ROW SET @sum = @sum + NEW.amount;

La sentencia CREATE TRIGGER crea un disparador llamado ins_sum que se


asocia con la tablaaccount. También se incluyen cláusulas que especifican el
momento de activación, el evento activador, y qué hacer luego de la activación:

 La palabra clave BEFORE indica el momento de acción del disparador.


En este caso, el disparador debería activarse antes de que cada registro
se inserte en la tabla. La otra palabra clave posible aqui es AFTER.
 La plabra clave INSERT indica el evento que activará al disparador. En el
ejemplo, la sentenciaINSERT causará la activación. También pueden
crearse disparadores para sentencias DELETE y UPDATE.
 Las sentencia siguiente, FOR EACH ROW, define lo que se ejecutará
cada vez que el disparador se active, lo cual ocurre una vez por cada fila
afectada por la sentencia activadora. En el ejemplo, la sentencia activada
es un sencillo SET que acumula los valores insertados en la
columna amount. La sentencia se refiere a la columna
como NEW.amount, lo que significa “el valor de la columna amountque
será insertado en el nuevo registro.”

37
Para utilizar el disparador, se debe establecer el valor de la variable acumulador
a cero, ejecutar una sentencia INSERT, y ver qué valor presenta luego la
variable.

mysql> SET @sum = 0;


mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);
mysql> SELECT @sum AS 'Total amount inserted';
+-----------------------+
| Total amount inserted |
+-----------------------+
| 1852.48 |
+-----------------------+

En este caso, el valor de @sum luego de haber ejecutado la


sentencia INSERT es 14.98 + 1937.50 - 100, o 1852.48.

Para eliminar el disparador, se emplea una sentencia DROP TRIGGER. El


nombre del disparador debe incluir el nombre de la tabla:

mysql> DROP TRIGGER account.ins_sum;

Debido a que un disparador está asociado con una tabla en particular, no se


pueden tener múltiples disparadores con el mismo nombre dentro de una tabla.
También se debería tener en cuenta que el espacio de nombres de los
disparadores puede cambiar en el futuro de un nivel de tabla a un nivel de base
de datos, es decir, los nombres de disparadores ya no sólo deberían ser únicos
para cada tabla sino para toda la base de datos. Para una mejor compatibilidad
con desarrollos futuros, se debe intentar emplear nombres de disparadores que
no se repitan dentro de la base de datos.

Adicionalmente al requisito de nombres únicos de disparador en cada tabla, hay


otras limitaciones en los tipos de disparadores que pueden crearse. En particular,
no se pueden tener dos disparadores para una misma tabla que sean activados
en el mismo momento y por el mismo evento. Por ejemplo, no se pueden definir
dos BEFORE INSERT o dos AFTER UPDATE en una misma tabla. Es
improbable que esta sea una gran limitación, porque es posible definir un
disparador que ejecute múltiples sentencias empleando el constructor de
sentencias compuestas BEGIN ... END luego de FOR EACH ROW. (Más
adelante en esta sección puede verse un ejemplo).

También hay limitaciones sobre lo que puede aparecer dentro de la sentencia


que el disparador ejecutará al activarse:

 El disparador no puede referirse a tablas directamente por su nombre,


incluyendo la misma tabla a la que está asociado. Sin embargo, se
pueden emplear las palabras clave OLD y NEW. OLD se refiere a un
registro existente que va a borrarse o que va a actualizarse antes de que
esto ocurra. NEW se refiere a un registro nuevo que se insertará o a un
registro modificado luego de que ocurre la modificación.
 El disparador no puede invocar procedimientos almacenados utilizando
la sentencia CALL. (Esto significa, por ejemplo, que no se puede utilizar
un procedimiento almacenado para eludir la prohibición de referirse a
tablas por su nombre).
 El disparador no puede utilizar sentencias que inicien o finalicen una
transacción, tal como START TRANSACTION, COMMIT, o ROLLBACK.

Las palabras clave OLD y NEW permiten acceder a columnas en los registros
afectados por un disparador. (OLD y NEW no son sensibles a mayúsculas). En
un disparador para INSERT, solamente puede utilizarse NEW.nom_col; ya que
no hay una versión anterior del registro. En un disparador paraDELETE sólo
puede emplearse OLD.nom_col, porque no hay un nuevo registro. En un
disparador para UPDATE se puede emplear OLD.nom_col para referirse a las
columnas de un registro antes de que sea actualizado, y NEW.nom_col para
referirse a las columnas del registro luego de actualizarlo.

Una columna precedida por OLD es de sólo lectura. Es posible hacer referencia
a ella pero no modificarla. Una columna precedida por NEW puede ser
referenciada si se tiene el privilegio SELECTsobre ella. En un
disparador BEFORE, también es posible cambiar su valor con SET
NEW.nombre_col =valor si se tiene el privilegio de UPDATE sobre ella. Esto

39
significa que un disparador puede usarse para modificar los valores antes que se
inserten en un nuevo registro o se empleen para actualizar uno existente.

En un disparador BEFORE, el valor de NEW para una


columna AUTO_INCREMENT es 0, no el número secuencial que se generará en
forma automática cuando el registro sea realmente insertado.

OLD y NEW son extensiones de MySQL para los disparadores.

Empleando el constructor BEGIN ... END, se puede definir un disparador que


ejecute sentencias múltiples. Dentro del bloque BEGIN, también pueden
utilizarse otras sintaxis permitidas en rutinas almacenadas, tales como
condicionales y bucles. Como sucede con las rutinas almacenadas, cuando se
crea un disparador que ejecuta sentencias múltiples, se hace necesario redefinir
el delimitador de sentencias si se ingresará el disparador a través del
programa mysql, de forma que se pueda utilizar el caracter ';' dentro de la
definición del disparador. El siguiente ejemplo ilustra estos aspectos. En él se
crea un disparador para UPDATE, que verifica los valores utilizados para
actualizar cada columna, y modifica el valor para que se encuentre en un rango
de 0 a 100. Esto debe hacerse en un disparador BEFORE porque los valores
deben verificarse antes de emplearse para actualizar el registro:

mysql> delimiter //
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account
-> FOR EACH ROW
-> BEGIN
-> IF NEW.amount < 0 THEN
-> SET NEW.amount = 0;
-> ELSEIF NEW.amount > 100 THEN
-> SET NEW.amount = 100;
-> END IF;
-> END;//
mysql> delimiter ;
Podría parecer más fácil definir una rutina almacenada e invocarla desde el
disparador utilizando una simple sentencia CALL. Esto sería ventajoso también
si se deseara invocar la misma rutina desde distintos disparadores. Sin
embargo, una limitación de los disparadores es que no pueden utilizar CALL.
Se debe escribir la sentencia compuesta en cada CREATE TRIGGER donde se
la desee emplear.

MySQL gestiona los errores ocurridos durante la ejecución de disparadores de


esta manera:

 Si lo que falla es un disparador BEFORE, no se ejecuta la operación en


el correspondiente registro.
 Un disparador AFTER se ejecuta solamente si el
disparador BEFORE (de existir) y la operación se ejecutaron
exitosamente.
 Un error durante la ejecución de un disparador BEFORE o AFTER deriva
en la falla de toda la sentencia que provocó la invocación del disparador.
 En tablas transaccionales, la falla de un disparador (y por lo tanto de
toda la sentencia) debería causar la cancelación (rollback) de todos los
cambios realizados por esa sentencia. En tablas no transaccionales,
cualquier cambio realizado antes del error no se ve afectado.

2.4. USO DE VISTAS Y SINTAXIS

(Barrena, ANER BARRENA, 2016) vamos a repasar la sentencia CREATE VIEW MySQL,
esta tiene como finalidad crear tablas virtuales a partir de consultas SELECT a otras tablas.

Las vistas de una base de datos MySQL tienen la misma estructura de filas y columnas de
una tabla, no puede haber una vista con el mismo nombre de una tabla.

Sintaxis de CREATE VIEW MySQL

Veamos la sintaxis básica de esta función extraída de su web oficial:

41
CREATE [OR REPLACE] VIEW nombre_vista [column_list] AS consulta_SELECT

Os explico el código superior para crear vistas en MySQL:

 OR REPLACE: Reemplaza una vista existente en caso de coincidir en nombre.

 nombre_vista: Nombre de la vista a crear.

 column_list: Listado de columnas a crear.

 consulta_SELECT: Consulta SELECT que queremos realizar para obtener la


información que contendrá la vista.

Ventajas de usar vistas en MySQL


Enumero varias ventajas de las que soy consciente:

 Privacidad de la información: Mostramos a los usuarios con acceso a la vista


únicamente la información que creamos conveniente. De esta manera no se tiene
acceso a la tabla original con todas sus filas y columnas.

 Optimización del rendimiento de la base de datos: Podemos crear de querys sobre


vistas complejas, es decir, vistas cuya información ha sido extraída y creada a través
de unas SELECT complejas. De esta manera nos ahorramos estar ejecutando queys
pesadas y atacamos directamente al resultado de dichas querys.

 Tablas de prueba: Para los desarrolladores que no tengan entornos de


preproducción es muy útil usar las vistas para no tener miedo a perder información.

Ejemplo para crear vistas con CREATE VIEW MySQL

Os pongo un par de ejemplos para crear vistas en MySQL:

CREATE VIEW vista futbolistas AS SELECT * FROM futbolistas;

En el código superior estaríamos creando una vista con todas las filas y columnas de la tabla
futbolistas.
CREATE VIEW vista_futbolistas AS SELECT futbolistas.id, nombre, apellidos FROM
futbolistas INNER JOIN tarjetas_amarillas ON futbolistas.id = tarjetas_amarillas.id_futbolista;
En este otro código estaríamos obteniendo una vista con los datos de los futbolistas que
contienen tarjetas amarillas usando la sentencia INNER JOIN.

CREATE VIEW vista_futbolistas AS SELECT futbolistas.id, nombre, apellidos FROM


futbolistas WHERE edad < 30;
En este otro código estaríamos obteniendo una vista con los datos de los futbolistas que
tienen menos de 30 años sentencia WHERE.

2.5. VENTAJAS Y USO DE VISTAS

Ventajas:

 Facilita el manejo de grandes volúmenes de información, haciéndolos más


fáciles y rápidos de manipular.
 Brinda mayor seguridad a la información.
 Evita la redundancia de la información.
 Mejora la metodología de trabajo, se hace más organizada.
 Facilita la realización de consultas en la BD por lo que se facilita la toma
de decisiones.

Desventajas:

 Rendimiento ya que las vistas crean una apariencia de tabla por lo que el
SMBD debe traducir las consultas definidas en una vista.
 restricciones de actualización, cuando se solicita la actualización de una
fila de la vista se debe traducir en una petición de actualización a la tabla
de origen de la vista, esto en vistas muy grandes puede causar problemas
y evitar que las actualizaciones se realicen.

43
UNIDAD III PROCEDIMIENTOS ALMACENADOS, INDICES Y
CURSORES

3.1 Procedimientos almacenados y Tablas de Permisos

Los procedimientos almacenados requieren la tabla proc en la base de


datos mysql. Esta tabla se crea durante la instalación de MySQL 5.0. Si está
actualizando a MySQL 5.0 desde una versión anterior, asegúrese de actualizar
sus tablas de permisos para asegurar que la tabla proc existe.

Desde MySQL 5.0.3, el sistema de permisos se ha modificado para tener en


cuenta los procedimientos almacenados como sigue:

 El permiso CREATE ROUTINE se necesita para crear procedimientos


almacenados.
 El permiso ALTER ROUTINE se necesita para alterar o borrar
procedimientos almacenados. Este permiso se da automáticamente al
creador de una rutina.
 El permiso EXECUTE se requiere para ejectuar procedimientos
almacenados. Sin embargo, este permiso se da automáticamente al
creador de la rutina. También, la característica SQL SECURITY por
defecto para una rutina es DEFINER, lo que permite a los usuarios que
tienen acceso a la base de datos ejecutar la rutina asociada.

SINTAXIS:

CREATE PROCEDURE sp_name ([parameter[,...]])


[characteristic ...] routine_body

CREATE FUNCTION sp_name ([parameter[,...]])


RETURNS type
[characteristic ...] routine_body

parameter:
[ IN | OUT | INOUT ] param_name type

type:
Any valid MySQL data type
45
characteristic:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'

routine_body:
procedimientos almacenados o comandos SQL válidos

Estos comandos crean una rutina almacenada. Desde MySQL 5.0.3, para crear una
rutina, es necesario tener el permiso CREATE ROUTINE , y los permisos ALTER
ROUTINE y EXECUTE se asignan automáticamente a su creador. Si se permite logueo
binario necesita también el permiso SUPER.

Por defecto, la rutina se asocia con la base de datos actual. Para asociar la rutina
explícitamente con una base de datos, especifique el nombre
como db_name.sp_name al crearlo.

Si el nombre de rutina es el mismo que el nombre de una función de SQL, necesita usar
un espacio entre el nombre y el siguiente paréntesis al definir la rutina, o hay un error de
sintaxis. Esto también es cierto cuando invoca la rutina posteriormente.

La cláusula RETURNS puede especificarse sólo con FUNCTION, donde es obligatorio.


Se usa para indicar el tipo de retorno de la función, y el cuerpo de la función debe
contener un comando RETURN value.

La lista de parámetros entre paréntesis debe estar siempre presente. Si no hay


parámetros, se debe usar una lista de parámetros vacía () . Cada parámetro es un
parámetro IN por defecto. Para especificar otro tipo de parámetro, use la palabra
clave OUT o INOUT antes del nombre del parámetro. Especificando IN, OUT,
o INOUT sólo es valido para una PROCEDURE.

El comando CREATE FUNCTION se usa en versiones anteriores de MySQL para


soportar UDFs (User Defined Functions) (Funciones Definidas por el Usuario).
UDFs se soportan, incluso con la existencia de procedimientos almacenados. Un UDF
puede tratarse como una función almacenada externa. Sin embargo, tenga en cuenta
que los procedimientos almacenados comparten su espacio de nombres con UDFs.

Un marco para procedimientos almacenados externos se introducirá en el futuro. Esto


permitira escribir procedimientos almacenados en lenguajes distintos a SQL. Uno de los
primeros lenguajes a soportar será PHP ya que el motor central de PHP es pequeño,
con flujos seguros y puede empotrarse fácilmente. Como el marco es público, se espera
soportar muchos otros lenguajes.

Un procedimiento o función se considera “determinista” si siempre produce el mismo


resultado para los mismos parámetros de entrada, y “no determinista” en cualquier otro
caso. Si no se da niDETERMINISTIC ni NOT DETERMINISTIC por defecto es NOT
DETERMINISTIC.

Para replicación, use la función NOW() (o su sinónimo) o RAND() no hace una rutina no
determinista necesariamente. Para NOW(), el log binario incluye el tiempo y hora y
replica correctamente. RAND()también replica correctamente mientras se invoque sólo
una vez dentro de una rutina. (Puede considerar el tiempo y hora de ejecución de la
rutina y una semilla de número aleatorio como entradas implícitas que son idénticas en
el maestro y el esclavo.)

Actualmente, la característica DETERMINISTIC se acepta, pero no la usa el


optimizador. Sin embargo, si se permite el logueo binario, esta característica afecta si
MySQL acepta definición de rutinas..

Varias características proporcionan información sobre la naturaleza de los datos usados


por la rutina. CONTAINS SQL indica que la rutina no contiene comandos que leen o
escriben datos. NO SQL indica que la rutina no contiene comandos SQL . READS SQL
DATA indica que la rutina contiene comandos que leen datos, pero no comandos que
escriben datos. MODIFIES SQL DATA indica que la rutina contiene comandos que
pueden escribir datos. CONTAINS SQL es el valor por defecto si no se dan
explícitamente ninguna de estas características.

La característica SQL SECURITY puede usarse para especificar si la rutina debe ser
ejecutada usando los permisos del usuario que crea la rutina o el usuario que la invoca.
El valor por defecto es DEFINER. Esta característica es nueva en SQL:2003. El creador
o el invocador deben tener permisos para acceder a la base de datos con la que la rutina

47
está asociada. Desde MySQL 5.0.3, es necesario tener el permiso EXECUTE para ser
capaz de ejecutar la rutina. El usuario que debe tener este permiso es el definidor o el
invocador, en función de cómo la característica SQL SECURITY .

MySQL almacena la variable de sistema sql_mode que está en efecto cuando se crea
la rutina, y siempre ejecuta la rutina con esta inicialización.

La cláusula COMMENT es una extensión de MySQL, y puede usarse para describir el


procedimiento almacenado. Esta información se muestra con los comandos SHOW
CREATE PROCEDURE y SHOW CREATE FUNCTION .

MySQL permite a las rutinas que contengan comandos DDL (tales


como CREATE y DROP) y comandos de transacción SQL (como COMMIT). Esto no lo
requiere el estándar, y por lo tanto, es específico de la implementación.

Los procedimientos almacenados no pueden usar LOAD DATA INFILE.

Nota: Actualmente, los procedimientos almacenados creados con CREATE


FUNCTION no pueden tener referencias a tablas. (Esto puede incluir algunos
comandos SET que pueden contener referencias a tablas, por ejemplo SET a:=
(SELECT MAX(id) FROM t), y por otra parte no pueden contener comandos SELECT ,
por ejemplo SELECT 'Hello world!' INTO var1.) Esta limitación se elminará en breve.

Los comandos que retornan un conjunto de resultados no pueden usarse desde una
función almacenada. Esto incluye comandos SELECT que no usan INTO para tratar
valores de columnas en variables, comandos SHOW y otros comandos como EXPLAIN.
Para comandos que pueden determinarse al definir la función para que retornen un
conjunto de resultados, aparece un mensaje de error Not allowed to return a result set
from a function (ER_SP_NO_RETSET_IN_FUNC). Para comandos que puede
determinarse sólo en tiempo de ejecución si retornan un conjunto de resultados, aparece
el error PROCEDURE %s can't return a result set in the given
context(ER_SP_BADSELECT).

El siguiente es un ejemplo de un procedimiento almacenado que use un


parámetro OUT . El ejemplo usa el cliente mysql y el comando delimiter para cambiar
el delimitador del comando de ; a //mientras se define el procedimiento . Esto permite
pasar el delimitador ; usado en el cuerpo del procedimiento a través del servidor en lugar
de ser interpretado por el mismo mysql.
mysql> delimiter //

mysql> CREATE PROCEDURE simpleproc (OUT param1 INT)


-> BEGIN
-> SELECT COUNT(*) INTO param1 FROM t;
-> END
-> //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;

mysql> CALL simpleproc(@a);


Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @a;


+------+
| @a |
+------+
|3 |
+------+
1 row in set (0.00 sec)

Al usar el comando delimiter, debe evitar el uso de la antibarra ('\') ya que es el carácter
de escape de MySQL.

El siguiente es un ejemplo de función que toma un parámetro, realiza una operación con
una función SQL, y retorna el resultado:

mysql> delimiter //

mysql> CREATE FUNCTION hello (s CHAR(20)) RETURNS CHAR(50)


-> RETURN CONCAT('Hello, ',s,'!');
-> //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;

49
mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world! |
+----------------+
1 row in set (0.00 sec)

Si el comando RETURN en un procedimiento almacenado retorna un valor con un tipo


distinto al especificado en la cláusula RETURNS de la función, el valor de retorno se
coherciona al tipo apropiado. Por ejemplo, si una función retorna un valor ENUM o SET,
pero el comando RETURN retorna un entero, el valor retornado por la función es la
cadena para el miembro de ENUM correspondiente de un conjunto de miembros SET .

3.2 Introducción a índices

Imagina que tiene toda la información telefónica de los habitantes de un país como
Venezuela, de una población aproximada de 30 millones de habitantes. Suponga
también que los datos no están ordenados.

Nota: Para que los índices se noten realmente, debemos contar con un número
significativo de registros en una tabla. Pero no es necesario que sean millones. Creando
una buena estrategia de índices podemos mejorar sensiblemente la velocidad de las
consultas en tablas con varios miles de registros. Claro está que la mejora suele ser
proporcional y depende mucho de esa cantidad de registros. También un uso
inadecuado de ellos puede redundar en pérdida de rendimiento, por lo que conviene
saber qué es lo que estamos haciendo.

Veamos una consulta como esta:

SELECT * FROM personas WHERE apellido="zamora"

Sin ningún orden en nuestros datos, MySQL debe leer todos los registros de la tabla
"personas" y efectuar una comparación entre el campo "apellido" y la cadena de
caracteres "Zamora” para encontrar alguna coincidencia (en la vida real habrá muchas
coincidencias). A medida que esta base de datos sufra modificaciones, como un
incremento en el número de registros, dicha consulta irá requiriendo un mayor el
esfuerzo de la CPU y el uso de memoria necesaria para ejecutarse.

Si tuviéramos una guía telefónica a mano localizaríamos fácilmente a cualquiera con


apellido "zamora" yendo al final de la guía, a la letra "Z". El método en sí está dado en
función a como están ordenados los datos y en el conocimiento de los mismos. En otras
palabras, localizamos rápidamente a "zamora" porque está ordenado por apellido y
porque conocemos el abecedario.

Si abrimos un libro técnico observamos que posee un índice al final del libro, contenido
por términos o conceptos importantes con su correspondiente numero de página. Si
sabemos de qué trata el libro buscamos la palabra que nos interesa y encontramos la
expresión junto con su número de página.

Los índices de base de datos son muy similares. Al igual que el escritor decide crear un
índice de términos y conceptos importantes de su libro, como administradores de una
base de datos decidimos crear un índice respecto a una columna.

Creando índices en MySQL

Usando el ejemplo inicial, para que la consulta anterior se ejecutase más rápido en
nuestro sistema gestor de base de datos, nos vendría bien crear un índice por apellido.
Para crear ese índice podemos utilizar una sentencia en lenguaje SQL como la siguiente:

ALTER TABLE personas ADD INDEX (apellido)

De esta forma sencilla indicamos a MySQL que genere una lista ordenada de todos los
apellidos de la tabla personas, así como en el ejemplo del libro tenemos los números de
teléfono ordenados por el apellido.

3.3. Bucles y Cursores

51
(Fernández, 2013) Aunque es recomendable no abusar de los bucles en MySQL, es más,
a veces no son necesarios, y siempre hay que buscar una solución que no los use, a
veces no la vamos a encontrar y será en esos casos cuando debamos utilizarlos.

Veamos un bucle muy sencillo, parecido a un for de los de toda la vida, en el que
contamos del 1 al 9:

DELIMITER $$
CREATE PROCEDURE simple_loop ( )
BEGIN
DECLARE counter BIGINT DEFAULT 0;

my_loop: LOOP
SET counter=counter+1;

IF counter=10 THEN
LEAVE my_loop;
END IF;

SELECT counter;

END LOOP my_loop;


END$$
DELIMITER ;

cuando hagamos:

CALL simple_loop();

Veremos algo como esto:

+———+
| counter |
+———+
| 1 |
+———+
1 row in set (0.01 sec)
+———+
| counter |
+———+
| 2 |
+———+
1 row in set (0.01 sec)

+———+
| counter |
+———+
| 3 |
+———+
1 row in set (0.01 sec)

+———+
| counter |
+———+
| 4 |
+———+
1 row in set (0.01 sec)

+———+
| counter |
+———+
| 5 |
+———+
1 row in set (0.01 sec)+———+
| counter |
+———+
| 6 |
+———+
1 row in set (0.01 sec)

+———+
| counter |
+———+
| 7 |

53
+———+
1 row in set (0.01 sec)

+———+
| counter |
+———+
| 8 |
+———+
1 row in set (0.01 sec)

+———+
| counter |
+———+
| 9 |
+———+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

Vemos que el código que iteraremos está entre LOOP…END LOOP, lo que aparece
justo antes (my_loop) es una etiqueta para nombrar ese bucle. Ahora bien, en este
ejemplo simplemente incrementamos la variable counter, y con una condición IF
hacemos que el bucle llegue a su fin cuando counter sea 10. Ese 10 no lo veremos
porque abandonamos el bucle antes del SELECT.

Propongamos un ejemplo más complicado, vamos a registrar las puntuaciones


obtenidas en un juego, este juego consistirá en una prueba que debemos realizar en el
menor tiempo posible, a pata coja y con obstáculos, y tenemos dos tipos de falta, uno
es apoyar la pierna levantada, y otra es chocar con un obstáculo, al final de la prueba
se asignarán los puntos y se almacenarán en la tabla, para no tener que calcularlos cada
vez.

CREATE TABLE Runners (


Runner_id BIGINT NOT NULL AUTO_INCREMENT,
Name VARCHAR(120) NOT NULL,
Time BIGINT NOT NULL,
Penalty1 BIGINT NOT NULL,
Penalty2 BIGINT NOT NULL,
Points BIGINT,
PRIMARY KEY (Runner_id)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;

Ahora introducimos algo de información para probar:

INSERT INTO Runners VALUES (NULL, 'Michael', 123, 5, 2, NULL);


INSERT INTO Runners VALUES (NULL, 'Sarah', 83, 3, 3, NULL);
INSERT INTO Runners VALUES (NULL, 'John', 323, 1, 1, NULL);
INSERT INTO Runners VALUES (NULL, 'Ramon', 100, 8, 4, NULL);
INSERT INTO Runners VALUES (NULL, 'Andrew', 143, 4, 3, NULL);
INSERT INTO Runners VALUES (NULL, 'Antoine', 199, 3, 2, NULL);
INSERT INTO Runners VALUES (NULL, 'David', 101, 2, 1, NULL);

Lo primero que vamos a hacer, será un procedimiento que incluya el bucle básico, con
SELECTs, para ver que todo funciona y que lo estamos haciendo bien. (Debajo explicaré
para qué es cada cosa):

DROP PROCEDURE IF EXISTS cursorTest;


DELIMITER $$
CREATE PROCEDURE cursorTest (
) BEGIN
-- Variables donde almacenar lo que nos traemos desde el SELECT
DECLARE v_name VARCHAR(120);
DECLARE v_time BIGINT;
DECLARE v_penalty1 BIGINT;
DECLARE v_penalty2 BIGINT;
-- Variable para controlar el fin del bucle
DECLARE fin INTEGER DEFAULT 0;

-- El SELECT que vamos a ejecutar


DECLARE runners_cursor CURSOR FOR
SELECT Name, Time, Penalty1, Penalty2 FROM Runners;

-- Condición de salida
DECLARE CONTINUE HANDLER FOR NOT FOUND SET fin=1;

55
OPEN runners_cursor;
get_runners: LOOP
FETCH runners_cursor INTO v_name, v_time, v_penalty1, v_penalty2;
IF fin = 1 THEN
LEAVE get_runners;
END IF;

SELECT v_name, v_time, v_penalty1, v_penalty2;

END LOOP get_runners;

CLOSE runners_cursor;
END$$
DELIMITER ;

Tenemos que tener en cuenta que en este cursor recorreremos el resultado de un


SELECT y en cada fila podremos almacenar el valor de cada campo en variables (por
eso declaramos v_name, v_time, v_penalty y v_penalty1). Al final, cada fila será como
un SELECT Name, Time, Penalty1, Penalty2 INTO v_name, v_time, v_penalty1,
v_penalty2 WHERE … y en cada iteración, tendremos unos valores para esas variables,
correspondiendo con filas obtenidas de forma consecutiva. Para esto es el DECLARE
xxx CURSOR FOR SELECT …

Tenemos que poner también una condición de finalización, normalmente, cuando no


haya más filas, por eso el DECLARE CONTINUE HANDLER FOR NOT FOUND SET
fin=1, en ese caso, cuando no encontremos más filas, pondremos un 1 en la variable fin.

Dentro del bucle, analizaremos el valor de la variable fin para ver si finalizamos (LEAVE
xxxxx) o ejecutamos una iteración.

Demos un paso más, vamos a crear una función que asigne las puntuaciones a cada
uno de los corredores con una fórmula. Por ejemplo la siguiente: siendo Time el tiempo
en segundos que se tarda en realizar la prueba, 500-Time serán los puntos iniciales, a
los que tenemos que restar 5*penalty1 y 3*penalty2. Por tanto:

DROP FUNCTION IF EXISTS calculate_runner_points;


DELIMITER $$
CREATE FUNCTION calculate_runner_points (
In_time BIGINT,
In_penalty1 BIGINT,
In_penalty2 BIGINT
) RETURNS BIGINT
BEGIN
DECLARE points BIGINT;

SET points = 500 - In_time - In_penalty1*5 - In_penalty2*3;

RETURN points;
END$$
DELIMITER ;

Ahora el código para calcular los puntos de los jugadores puede ser:

DROP PROCEDURE IF EXISTS calculate_all_points;


DELIMITER $$
CREATE PROCEDURE calculate_all_points (
) BEGIN
-- Variables donde almacenar lo que nos traemos desde el SELECT
DECLARE v_name VARCHAR(120);
DECLARE v_time BIGINT;
DECLARE v_penalty1 BIGINT;
DECLARE v_penalty2 BIGINT;
DECLARE v_runner_id BIGINT;
-- Variable para controlar el fin del bucle
DECLARE fin INTEGER DEFAULT 0;

-- El SELECT que vamos a ejecutar


DECLARE runners_cursor CURSOR FOR
SELECT Runner_id, Name, Time, Penalty1, Penalty2 FROM Runners;

-- Condición de salida
DECLARE CONTINUE HANDLER FOR NOT FOUND SET fin=1;

OPEN runners_cursor;
get_runners: LOOP
57
FETCH runners_cursor INTO v_runner_id, v_name, v_time, v_penalty1,
v_penalty2;
IF fin = 1 THEN
LEAVE get_runners;
END IF;

UPDATE Runners SET Points=calculate_runner_points(v_time, v_penalty1,


v_penalty2) WHERE Runner_id=v_runner_id;

END LOOP get_runners;

CLOSE runners_cursor;
END$$
DELIMITER ;

Pero claro, como dije al principio, tenemos que mirar siempre, si hay alguna solución
posible que no utilice bucles, sobre todo porque cuando empezamos a utilizarlos nos
emocionamos (igual que dice que también nos emocionamos con las expresiones
regulares) y vemos la solución con bucles más inmediata que sin bucles, pero claro, con
bucles todo irá mucho más lento. Podríamos haber hecho:

UPDATE Runners SET Points=calculate_runner_points(Time, Penalty1, Penalty2);

Aunque podemos hacer algunas cosas más con el ejemplo del bucle, por ejemplo, si el
tiempo es mayor de 250, se intercambien los penalties, editando directamente el código
del bucle, metiendo una sentencia IF, aunque eso mismo lo podemos hacer también
desde la función que calcula los puntos.

Otro pequeño ejemplo (bueno, no tan pequeño) que me viene a la cabeza es que
tenemos un sistema de usuarios en el que cada usuario tiene información en tres tablas:
una para login, password e información de acceso; otra para información de perfil y otra
de permisos. En este caso, en todas las tablas excepto en la de permisos habrá una
entrada por usuario, pero en los permisos estableceremos el elemento sobre el que un
usuario tiene permiso y qué tipo de permiso tiene, y como podemos tener permiso sobre
varios objetos, puede haber varias entradas por usuario.
También tenemos una tabla de mensajes entre usuarios.
Por otro lado tenemos las páginas, que serán objetos de nuestro sistema y serán sobre
las que los usuarios podrán ver, editar, crear derivados y borrar (los diferentes permisos
del sistema), eso sí, para las páginas existe una jerarquía, por lo que podremos tener
páginas “hijas”. Pero cuando creamos una página en el sistema:

 Al menos tendrán que tener permiso total sobre ella los administradores del
sistema (marcados en la tabla de acceso)
 Si un usuario tenía permiso de edición sobre una página padre, podrá editar la
nueva página hija
 Si un usuario podía crear derivadas en la página padre, podrá hacerlo en la hija
 Si un usuario podía editar y crear derivadas en la padre, podrá borrar en la hija
 Además, tenemos que enviar un mensaje (meter el mensaje en la tabla), al
usuario con los permisos que tendrá en la nueva página
 Tenemos para ello las funciones y procedimientos:
o puede_crear_derivadas(usuario, pagina) – Que devolverá TRUE si el
usuario puede crear páginas derivadas
o puede_editar(usuario, pagina) – Que hará lo mismo que la anterior pero
con el nuevo permiso
o nuevo_permiso(usuario, pagina, permiso) – Insertará un nuevo permiso
en la tabla de permisos
o mensaje(from, to, mensaje) – Enviará un usuario a un usuario.

Las funciones puede_crear_derivadas() y puede_editar() en principio son fáciles de


entender, pero su funcionamiento interno es mucho más complejo, las ha hecho un
compañero de trabajo y no tenemos ganas de meternos a ver qué ha liado. Lo mismo
pasa con nuevo_permiso() (que puede insertar entradas en la tabla o modificar las
existentes) o con mensaje(), que enviará notificaciones y además creará una tarea para
mandar el mensaje por e-mail, por lo que nuestro procedimiento para crear una página
quedaría así:

DROP PROCEDURE IF EXISTS crear_pagina;


DELIMITER $$
CREATE PROCEDURE crear_pagina (
IN in_nombre VARCHAR(120),
IN in_parent BIGINT
) BEGIN
-- Variables donde almacenar lo que nos traemos desde el SELECT
DECLARE v_user_id BIGINT;
DECLARE v_crear_derivadas TINYINT;

59
DECLARE v_object_id BIGINT;
DECLARE v_mens TEXT;

-- Variable para controlar el fin del bucle


DECLARE fin INTEGER DEFAULT 0;

-- El SELECT que vamos a ejecutar


DECLARE users_cursor CURSOR FOR
SELECT User_id FROM Users;

-- Condición de salida
DECLARE CONTINUE HANDLER FOR NOT FOUND SET fin=1;

INSERT INTO Paginas (Nombre, Parent) VALUES (in_nombre, in_parent);


SELECT LAST_INSERT_ID() INTO v_object_id;

OPEN users_cursor;
get_users: LOOP
FETCH users_cursor INTO v_user_id;

IF fin = 1 THEN
LEAVE get_users;
END IF;

SET v_mens = CONCAT('Nuevos permisos sobre la pagina: ',in_nombre,': ');

IF puede_crear_derivadas(v_user_id, in_parent) THEN


CALL nuevo_permiso(v_user_id, v_object_id, 'derivadas');
SET v_mens = CONCAT(v_mens, 'Crear derivadas ');
SET v_crear_derivadas=1;
ELSE
SET v_crear_derivadas=0;
END IF;

IF puede_editar(v_user_id, in_parent) THEN


CALL nuevo_permiso(v_user_id, v_object_id, 'editar');
SET v_mens = CONCAT(v_mens, 'Editar ');
IF v_crear_derivadas=1 THEN
CALL nuevo_permiso(v_user_id, v_object_id, 'borrar');
SET v_mens = CONCAT(v_mens, 'Borrar ');
END IF;
END IF;

CALL mensaje(1, v_user_id, v_mens);


END LOOP get_users;

CLOSE users_cursor;
END$$
DELIMITER ;

Seguro que en el futuro se me ocurren ejemplos algo mejores, es más, se aceptan


sugerencias en los comentarios, intentaré recrear los ejemplos y resolverlos en futuros
posts.

61
UNIDAD IV TRANSACCIONES, CONCURRENCIAS Y SEGURIDAD
DE BASE DE DATOS

4.1. Introducción a transacciones

(uaeh, 2010) El servidor de bases de datos MySQL soporta distintos tipos de tablas,

tales como ISAM, MyISAM, InnoDB y BDB (Berkeley Database). De éstos,


InnoDB es el tipo de tabla más importante (después del tipo predeterminado,
MyISAM), y merece una atención especial.

Las tablas del tipo InnoDB están estructuradas de forma distinta que MyISAM,
ya que se almacenan en un sólo archivo en lugar de tres, y sus principales
características son que permite trabajar con transacciones, y definir reglas de
integridad referencial.

El soporte de transacciones que provee MySQL no es algo nuevo en MySQL 4,


ya que desde la versión 3.23 se podía hacer uso de tablas InnoDB, la única
diferencia es que con la llegada de la versión 4.0 de MySQL, el soporte para
este tipo de tablas es habilitado por default.

Las transacciones aportan una fiabilidad superior a las bases de datos. Si


disponemos de una serie de consultas SQL que deben ejecutarse en conjunto,
con el uso de transacciones podemos tener la certeza de que nunca nos
quedaremos a medio camino de su ejecución. De hecho, podríamos decir que
las transacciones aportan una característica de "deshacer" a las aplicaciones
de bases de datos.

Para este fin, las tablas que soportan transacciones, como es el caso de
InnoDB, son mucho más seguras y fáciles de recuperar si se produce algún
fallo en el servidor, ya que las consultas se ejecutan o no en su totalidad. Por
otra parte, las transacciones pueden hacer que las consultas tarden más tiempo
en ejecutarse.

Seguramente alguna vez hemos escuchado hablar acerca de las


transacciones, pero probablemente no entendemos bien lo que son, y como
deben de ser usadas. La idea de este artículo es presentar algunos ejemplos
para mostrar que no es nada complicado, y difícil de aprender.
En este artículo se asume que se cuenta ya con un servidor MySQL con
soporte para el tipo de tablas InnoDB. En nuestro caso haremos uso de un
servidor MySQL 4.013 ejecutándose en un sistema MSWindows.

Para asegurarnos que tenemos soporte para el tipo de tablas InnoDB podemos
ejecutar la siguiente sentencia:

mysql> SHOW VARIABLES LIKE '%innodb%';

+---------------------------------+------------+

| Variable_name | Value |

+---------------------------------+------------+

| have_innodb | YES |

| innodb_additional_mem_pool_size | 1048576 |

| innodb_buffer_pool_size | 8388608 |

| innodb_data_file_path | ibdata:30M |

| innodb_data_home_dir | |

| innodb_file_io_threads |4 |

| innodb_force_recovery |0 |

| innodb_thread_concurrency |8 |

| innodb_flush_log_at_trx_commit | 1 |

| innodb_fast_shutdown | ON |

| innodb_flush_method | |

| innodb_lock_wait_timeout | 50 |

| innodb_log_arch_dir |. |

| innodb_log_archive | OFF |

| innodb_log_buffer_size | 1048576 |

| innodb_log_file_size | 5242880 |

63
| innodb_log_files_in_group |2 |

| innodb_log_group_home_dir |. |

| innodb_mirrored_log_groups |1 |

| innodb_max_dirty_pages_pct | 90 |

+---------------------------------+------------+

20 rows in set (0.00 sec)

La variable más importante es por supuesto have_innodb que tiene el valor


YES.

En efecto, una de las principales características de las tablas del tipo InnoDB es
que pueden trabajar con transacciones, o sentencias SQL que son agrupadas
como una sola. Un ejemplo típico de esto es una transacción bancaria. Por
ejemplo, si una cantidad de dinero es transferida de la cuenta de una persona a
otra, se requerirán por lo menos dos consultas:

UPDATE cuentas SET balance = balance - cantidad_transferida WHERE client


e = persona1;

UPDATE cuentas SET balance = balance + cantidad_transferida WHERE client


e = persona2;

Estas dos consultas deben trabajar bien, ¿pero que sucede si ocurre algún
imprevisto y "se cae" el sistema después de que se ejecuta la primer consulta,
pero la segunda aún no se ha completado?. La persona1 tendrá una cantidad
de dinero removida de su cuenta, y creerá que ha realizado su pago, sin
embargo, la persona2 estará enfadada puesto que pensará que no se le ha
depositado el dinero que le deben. En este ejemplo tan sencillo se ilustra la
necesidad de que las consultas sean ejecutadas de manera conjunta, o en su
caso, que no se ejecute ninguna de ellas. Es aquí donde las transacciones
toman un papel muy importante.

Los pasos para usar transacciones en MySQL son:

 Iniciar una transacción con el uso de la sentencia BEGIN.


 Actualizar, insertar o eliminar registros en la base de datos.
 Si se quieren los cambios a la base de datos, completar la transacción
con el uso de la sentencia COMMIT. Únicamente cuando se procesa
un COMMIT los cambios hechos por las consultas serán permanentes.
 Si sucede algún problema, podemos hacer uso de la
sentencia ROLLBACK para cancelar los cambios que han sido
realizados por las consultas que han sido ejecutadas hasta el momento.

Vamos a ejecutar algunas consultas para ver como trabajan las transacciones.
Lo primero que tenemos que hacer es crear una tabla del tipo InnoDB e insertar
algunos datos.

Para crear una tabla InnoDB, procedemos con el código SQL estándar
CREATE TABLE, pero debemos especificar que se trata de una tabla del tipo
InnoDB (TYPE= InnoDB). Esto es aplicable a cualquier tipo de tabla, pero
cuando no se especifica nada, MySQL supone que se trata de una tabla
MyISAM.

mysql> CREATE TABLE innotest (campo INT NOT NULL PRIMARY KEY) TYP
E = InnoDB;

Query OK, 0 rows affected (0.10 sec)

mysql> INSERT INTO innotest VALUES(1);

Query OK, 1 row affected (0.08 sec)

mysql> INSERT INTO innotest VALUES(2);

Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO innotest VALUES(3);

Query OK, 1 row affected (0.04 sec)

65
mysql> SELECT * FROM innotest;

+-------+

| campo |

+-------+

| 1|

| 2|

| 3|

+-------+

3 rows in set (0.00 sec)

De acuerdo, nada espectacular. Ahora veamos como usar transacciones.

mysql> BEGIN;

Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO innotest VALUES(4);

Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM innotest;

+-------+

| campo |

+-------+

| 1|

| 2|

| 3|

| 4|
+-------+

4 rows in set (0.00 sec)

Si en este momento ejecutamos un ROLLBACK, la transacción no será


completada, y los cambios realizados sobre la tabla no tendrán efecto.

mysql> ROLLBACK;

Query OK, 0 rows affected (0.06 sec)

mysql> SELECT * FROM innotest;

+-------+

| campo |

+-------+

| 1|

| 2|

| 3|

+-------+

3 rows in set (0.00 sec)

Ahora vamos a ver que sucede si perdemos la conexión al servidor antes de


que la transacción sea completada.

mysql> BEGIN;

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO innotest VALUES(4);

Query OK, 1 row affected (0.00 sec)

67
mysql> SELECT * FROM innotest;

+-------+

| campo |

+-------+

| 1|

| 2|

| 3|

| 4|

+-------+

4 rows in set (0.00 sec)

mysql> EXIT;

Bye

Cuando obtengamos de nuevo la conexión, podemos verificar que el registro no


se insertó, ya que la transacción no fue completada.

Welcome to the MySQL monitor. Commands end with ; or g.

Your MySQL connection id is 449 to server version: 4.0.13

Type 'help;' or 'h' for help. Type 'c' to clear the buffer.

mysql> SELECT * FROM innotest;

+-------+

| campo |
+-------+

| 1|

| 2|

| 3|

+-------+

3 rows in set (0.00 sec)

Ahora vamos a repetir la sentencia INSERT ejecutada anteriormente, pero


haremos un COMMIT antes de perder la conexión al servidor al salir del monitor
de MySQL.

mysql> BEGIN;

Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO innotest VALUES(4);

Query OK, 1 row affected (0.00 sec)

mysql> COMMIT;

Query OK, 0 rows affected (0.02 sec)

mysql> EXIT;

Bye

Una vez que hacemos un COMMIT, la transacción es completada, y todas las


sentencias SQL que han sido ejecutadas previamente afectan de manera
permanente a las tablas de la base de datos.

Welcome to the MySQL monitor. Commands end with ; or g.

69
Your MySQL connection id is 450 to server version: 4.0.13

Type 'help;' or 'h' for help. Type 'c' to clear the buffer.

mysql> SELECT * FROM innotest;

+-------+

| campo |

+-------+

| 1|

| 2|

| 3|

| 4|

+-------+

4 rows in set (0.00 sec)

4.2. Lecturas Consistentes

Por default, las tablas InnoDB ejecutan una lectura consistente (consistent read).
Esto significa que cuando una sentencia SELECT es ejecutada, MySQL regresa
los valores presentes en la base de datos hasta la transacción más reciente que
ha sido completada. Si alguna transacción está en progreso, los cambios hechos
por alguna sentencia INSERT o UPDATE no serán reflejados. Sin embargo,
existe una excepción: las transacciones abiertas si pueden ver sus propios
cambios. Para demostrar esto, necesitamos establecer dos conexiones al
servidor MySQL.
Primero agregaremos un registro dentro de una transacción con la primera
conexión (ID 524):

Welcome to the MySQL monitor. Commands end with ; or g.


Your MySQL connection id is 524 to server version: 4.0.13

Type 'help;' or 'h' for help. Type 'c' to clear the buffer.

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO innotest VALUES(5);


Query OK, 1 row affected (0.00 sec)

Ahora, desde la segunda conexión (ID 525) consultamos los datos de nuestra
tabla.

Welcome to the MySQL monitor. Commands end with ; or g.


Your MySQL connection id is 525 to server version: 4.0.13

Type 'help;' or 'h' for help. Type 'c' to clear the buffer.

mysql> SELECT * FROM innotest;


+-------+
| campo |
+-------+
| 1|
| 2|
| 3|
| 4|
+-------+
4 rows in set (0.00 sec)

Como se puede observar, el registro que hemos insertado desde la 1ra. conexión
no es regresado puesto que forma parte de una transacción que no ha sido
completada. Ahora, desde la 1ra. conexión ejecutamos la misma consulta
SELECT.
71
mysql> SELECT * FROM innotest;
+-------+
| campo |
+-------+
| 1|
| 2|
| 3|
| 4|
| 5|
+-------+
5 rows in set (0.00 sec)

Después de completar la transacción con una sentencia COMMIT en la 1ra.


conexión podremos verificar que desde la 2da. conexión los cambios ya son
visibles.

mysql> SELECT * FROM innotest;


+-------+
| campo |
+-------+
| 1|
| 2|
| 3|
| 4|
| 5|
+-------+
5 rows in set (0.00 sec)

4.3. Aplicación de Lecturas Consistentes

Vamos a crear una sencilla tabla llamada ventas que sea del tipo InnoDB.

mysql> CREATE TABLE ventas(


-> id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
-> producto VARCHAR(30) NOT NULL,
-> cantidad TINYINT NOT NULL) TYPE = InnoDB;
Query OK, 0 rows affected (0.96 sec)

Insertamos un registro.

mysql> INSERT INTO ventas VALUES(0,'Gansito marinela',3);


Query OK, 1 row affected (0.16 sec)

mysql> SELECT * FROM ventas;


+----+------------------+----------+
| id | producto | cantidad |
+----+------------------+----------+
| 1 | Gansito marinela | 3 |
+----+------------------+----------+
1 row in set (0.01 sec)

Ahora vamos a iniciar una transacción con la sentencia BEGIN.

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

Actualizamos el registro.

mysql> UPDATE ventas SET cantidad=4 WHERE id=1;


Query OK, 1 row affected (0.07 sec)
Líneas correspondientes: 1 Cambiadas: 1 Avisos: 0

Verificamos que los cambios han sucedido.

mysql> SELECT * FROM ventas;


+----+------------------+----------+
| id | producto | cantidad |
+----+------------------+----------+
| 1 | Gansito marinela | 4 |
+----+------------------+----------+
1 row in set (0.00 sec)

Si queremos deshacer los cambios, entonces ejecutamos un ROLLBACK.


73
mysql> ROLLBACK;
Query OK, 0 rows affected (0.06 sec)

Verificamos que se deshicieron los cambios.

mysql> SELECT * FROM ventas;


+----+------------------+----------+
| id | producto | cantidad |
+----+------------------+----------+
| 1 | Gansito marinela | 3 |
+----+------------------+----------+
1 row in set (0.00 sec)

Vamos a actualizar el registro usando otra transacción.

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE ventas SET cantidad=2 WHERE id=1;


Query OK, 1 row affected (0.00 sec)
Líneas correspondientes: 1 Cambiadas: 1 Avisos: 0

mysql> SELECT * FROM ventas;


+----+------------------+----------+
| id | producto | cantidad |
+----+------------------+----------+
| 1 | Gansito marinela | 2 |
+----+------------------+----------+
1 row in set (0.00 sec)

Vamos a confirmar que deseamos los cambios.

mysql> COMMIT;
Query OK, 0 rows affected (0.05 sec)

En este momento los cambios son permanentes y definitivos.

mysql> SELECT * FROM ventas;


+----+------------------+----------+
| id | producto | cantidad |
+----+------------------+----------+
| 1 | Gansito marinela | 2 |
+----+------------------+----------+
1 row in set (0.00 sec)

4.4. Transacciones

Una transacción es una unidad de programa que accesa y posiblemente actualiza varios
elementos de datos.

Estados de transacción

 Active, el estado inicial y permanece durante toda la ejecución


 Partially committed, después de que se ha ejecutado el último statement
 Failed, después de algun error, no se puede continuar
 Aborted, se hace un "rollback" hacia un estado anterior consistente
 Committed, después del éxito

Programación

Crear Conexión

Abrir Conexión
(Openconnection)

Iniciar Transacción
(Begin Transaction) setAutoCommit(0/false)

Queries...
(Insert, Select, Update, Delete...)

-Error
(Abort Transaction) rollback

75
Procesar resultados
(Print, a= , b= )

Asegurar Transacción
(End Transaction) commit

Cerrar Conexión
(Closeconnection)

Notas:

 Por default toda transacción tiene autocommit = 1


 El rollback generalmente se usa en el catch de los programas, aunque puede ir
en cualquier parte.
 Una transacción que no llega al commit automáticamente hace el rollback

4.5. Control de Concurrencias

El control de accesos concurrentes y específicamente de transacciones concurrentes es


manejado por un módulo del dbms llamado "scheduler".

Es importante recordar que muchos de los datos de la base no se encuentran nada más
en disco, sino tambien en los buffers de memoria, de ahí que el scheduler interactúa con
ellos y en su defecto solicita la lectura de los datos del disco.
Ilustración 1: concurrencias

Scheduler del DBMS

El calendarizador crea agendas, secuencias ordenadas de las acciones tomadas por una
o más transacciones.

El siguiente ejemplo muestra 2 transacciones cuya única característica de consistencia


es que A=B ya que ambas operaciones son iguales para ambos elementos. Por otro lado
sabemos que si las transacciones son ejecutadas aisladamente la consistencia se
preservará.

Ilustración 2: Ejemplo concurrencias

4.6. Control de concurrencias por DBMS

77
SET AUTOCOMMIT = {0 | 1}

Si el modo de autocommit está en apagadado SET AUTOCOMMIT = 0, entonces se


asume que la misma transacción continua hasta que se realice un COMMIT o un
ROLLBACK. Donde un commit actualizará Por default el modo de autocommit está
encendido, de manera que cada operación es considerada una transacción y todo
cambio se va reflejando automáticamente en la base de datos.

Niveles de aislamiento en SQL

SQL estándar define 4 niveles de aislamiento en términos de 3 fenómenos que deben


ser prevenidos entre transacción concurrentes. Estos son:

 dirty read: Una transacción lee datos escritos por una transacción concurrente
que no ha hecho "commit"
 nonrepeatable read: Una transacción re-lee datos que leyó previamente y
encuentra que han sido modificados por otra transacción (que hizo commit en el
inter).
 phantom read: Una transacción re-ejecuta un query regresando un conjunto de
tuplas que satisfacen una condición de búsqueda y encuentra que el resultado
ha cambiado debido a otra transacción que hizo "commit" recientemente.

4.7. Ejemplo de Transacciones por concurrencias

Partiendo del esquema siguiente se presentan las acciones de 2 transacciones


concurrentes.

mysql> desc bank;


+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | | PRI | 0 | |
| debit | float | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.08 sec)
Tabla 1: Ejemplo de transacciones

T1 T2
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.09 sec) Query OK, 0 rows affected
mysql> select * from bank; (0.00 sec)
+------+-------+ mysql> select * from bank;
| id | debit | +------+-------+
+------+-------+ | id | debit |
| 32 | 999 | +------+-------+
| 64 | 7865 | | 32 | 999 |
+------+-------+ | 64 | 7865 |
2 rows in set (0.00 sec) +------+-------+
mysql> insert into bank values(66,3453); 2 rows in set (0.00 sec)
Query OK, 1 row affected (0.09
sec)

mysql> select * from bank;


+------+-------+
| id | debit |
+------+-------+
| 32 | 999 |
| 64 | 7865 |
+------+-------+
2 rows in set (0.15 sec)
mysql> select * from bank;
+------+-------+
| id | debit |
+------+-------+
| 32 | 999 |
| 64 | 7865 |
mysql> select * from bank; +------+-------+
+------+-------+ 2 rows in set (0.00 sec)
| id | debit |
+------+-------+
| 32 | 999 |
| 64 | 7865 |

79
| 66 | 3453 |
+------+-------+
3 rows in set (0.00 sec)
mysql> delete from bank where id=64;
Query OK, 1 row affected (0.12
sec)
mysql> select * from bank; mysql> select * from bank;
+------+-------+ +------+-------+
| id | debit | | id | debit |
+------+-------+ +------+-------+
| 32 | 999 | | 32 | 999 |
| 66 | 3453 | | 64 | 7865 |
+------+-------+ +------+-------+
2 rows in set (0.00 sec) 2 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00
mysql> commit; sec)
Query OK, 0 rows affected (0.07 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00
sec)
mysql> set autocommit=0; mysql> select * from bank;
Query OK, 0 rows affected (0.00 sec) +------+-------+
mysql> select * from bank; | id | debit |
+------+-------+ +------+-------+
| id | debit | | 32 | 999 |
+------+-------+ | 66 | 3453 |
| 32 | 999 | +------+-------+
| 66 | 3453 | 2 rows in set (0.00 sec)
+------+-------+ mysql> rollback;
2 rows in set (0.00 sec) Query OK, 0 rows affected
mysql> rollback; (0.01 sec)
Query OK, 0 rows affected
(0.00 sec)
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected Query OK, 0 rows affected
(0.01 sec) (0.00 sec)
mysql> select * from bank where id=32 mysql> update bank set debit=666
for update; where id=32;
+------+-------+ .
| id | debit | .
+------+-------+ .
| 32 | 999 | .
+------+-------+ .
1 row in set (0.00 sec) .
mysql> rollback; .Wait unlock
Query OK, 0 rows affected .
(0.00 sec) .
.
.
mysql> update bank set debit=666
where id=32;
Query OK, 1 row affected
(13.02 sec)
Rows matched: 1
mysql> select * from bank; Changed: 1 Warnings: 0
+------+-------+
| id | debit | mysql> select * from bank;
+------+-------+ +------+-------+
| 32 | 999 | | id | debit |
| 66 | 3453 | +------+-------+
+------+-------+ | 32 | 666 |
2 rows in set (0.00 sec) | 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected
(0.07 sec)

mysql> select * from bank; mysql> select * from bank;

81
+------+-------+ +------+-------+
| id | debit | | id | debit |
+------+-------+ +------+-------+
| 32 | 999 | | 32 | 666 |
| 66 | 3453 | | 66 | 3453 |
+------+-------+ +------+-------+
2 rows in set (0.00 sec) 2 rows in set (0.01 sec)

mysql> commit;
Query OK, 0 rows affected
(0.07 sec)
mysql> select * from bank; mysql> select * from bank;
+------+-------+ +------+-------+
| id | debit | | id | debit |
+------+-------+ +------+-------+
| 32 | 666 | | 32 | 666 |
| 66 | 3453 | | 66 | 3453 |
+------+-------+ +------+-------+
2 rows in set (0.01 sec) 2 rows in set (0.01 sec)

mysql> set autocommit=0; mysql> set autocommit=0;


Query OK, 0 rows affected Query OK, 0 rows affected
(0.00 sec) (0.00 sec)
mysql> select * from bank where id=32 mysql> update bank set debit=777
lock in share mode; where id=32;
+------+-------+ .
| id | debit | .
+------+-------+ .
| 32 | 666 | .>Wait unlock
+------+-------+ .
1 row in set (0.00 sec) .
mysql> update bank set debit=888 where .
id=32; .
ERROR 1213 (40001): Deadlock found .
when trying to get lock; .
Try restarting transaction .
.
.
.
>T1 tiene bloqueado a 32, T2 espera la .
liberacion .
>al hacer el update en T1 ahora T1 .
espera a T2 .
>por lo tanto se produce un deadlock mysql> update bank set debit=777
>el dbms hace rollback en T1 (ultimo en where id=32;
espera) y procesa T2 Query OK, 1 row affected
(23.10 sec)
Rows matched: 1
Changed: 1 Warnings: 0

mysql> select * from bank;


+------+-------+
mysql> select * from bank; | id | debit |
+------+-------+ +------+-------+
| id | debit | | 32 | 777 |
+------+-------+ | 66 | 3453 |
| 32 | 666 | +------+-------+
| 66 | 3453 | 2 rows in set (0.00 sec)
+------+-------+ mysql> commit;
2 rows in set (0.00 sec) Query OK, 0 rows affected
(0.00 sec)
mysql> select * from bank;
+------+-------+
| id | debit |
+------+-------+
mysql> select * from bank; | 32 | 777 |
+------+-------+ | 66 | 3453 |
| id | debit | +------+-------+
+------+-------+ 2 rows in set (0.00 sec)
| 32 | 666 |
| 66 | 3453 |

83
+------+-------+
2 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected
(0.00 sec)
mysql> select * from bank;
+------+-------+
| id | debit |
+------+-------+
| 32 | 777 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.01 sec)

mysql> set autocommit=0; mysql> set autocommit=0;


Query OK, 0 rows affected Query OK, 0 rows affected
(0.00 sec) (0.00 sec)
mysql> select * from bank where id=32 mysql> select * from bank where
for update; id=32 for update;
+------+-------+ .
| id | debit | .
+------+-------+ .
| 32 | 777 | .
+------+-------+ .
1 row in set (0.00 sec) .
mysql> update bank set debit=111 where .> Wait unlock
id=32; .
.
Query OK, 1 row affected (0.00 .
sec) .
Rows matched: 1 Changed: 1 .
Warnings: 0
mysql> select * from bank where
id=32 for update;
mysql> commit; +------+-------+
Query OK, 0 rows affected | id | debit |
(0.01 sec) +------+-------+
| 32 | 111 |
+------+-------+
1 row in set (32.45 sec)

mysql> select * from bank;


+------+-------+ mysql> rollback;
| id | debit | Query OK, 0 rows affected
+------+-------+ (0.00 sec)
| 32 | 111 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)

mysql> set autocommit=0;


Query OK, 0 rows affected mysql> select * from bank;
(0.00 sec) +------+-------+

85
mysql> update bank set debit=000 where | id | debit |
id=32; +------+-------+
Query OK, 1 row affected (6.88 | 32 | 111 |
sec) | 66 | 3453 |
Rows matched: 1 Changed: 1 +------+-------+
Warnings: 0 2 rows in set (0.00 sec)
mysql> select * from bank;
+------+-------+
| id | debit |
+------+-------+
| 32 | 0 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected
(0.00 sec)

mysql> select * from bank;


+------+-------+
| id | debit |
+------+-------+
| 32 | 0 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)
mysql> select * from bank;
+------+-------+
| id | debit |
+------+-------+
| 32 | 0 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected Query OK, 0 rows affected
(0.00 sec) (0.00 sec)
mysql> select * from bank where
id=32 for update;
+------+-------+
| id | debit |
+------+-------+
| 32 | 0 |
mysql> update bank set debit=333 where +------+-------+
id=32; 1 row in set (0.00 sec)
.
.
.
.
.
.> Wait unlock
. mysql> commit;
. Query OK, 0 rows affected
. (0.00 sec)
.
.
mysql> update bank set debit=333 where
id=32;
Query OK, 1 row affected (3.20
sec)
Rows matched: 1 Changed: 1
Warnings: 0
mysql> select * from bank;
+------+-------+ mysql> select * from bank;
| id | debit | +------+-------+
+------+-------+ | id | debit |
| 32 | 333 | +------+-------+
| 66 | 3453 | | 32 | 0 |
+------+-------+ | 66 | 3453 |
2 rows in set (0.00 sec) +------+-------+
mysql> commit; 2 rows in set (0.00 sec)

87
Query OK, 0 rows affected
(0.00 sec)
mysql> select * from bank;
+------+-------+
| id | debit |
+------+-------+ mysql> select * from bank;
| 32 | 333 | +------+-------+
| 66 | 3453 | | id | debit |
+------+-------+ +------+-------+
2 rows in set (0.00 sec) | 32 | 0 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected
(0.00 sec)
mysql> select * from bank;
+------+-------+
| id | debit |
+------+-------+
| 32 | 333 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected Query OK, 0 rows affected
(0.00 sec) (0.00 sec)
mysql> select * from bank where
id=32 lock in share mode;
+------+-------+
| id | debit |
+------+-------+
| 32 | 333 |
+------+-------+
1 row in set (0.00 sec)

mysql> select * from bank where id=32


lock in share mode;
+------+-------+
| id | debit |
+------+-------+
| 32 | 333 |
+------+-------+
1 row in set (0.00 sec)
mysql> update bank set debit=444 where
id=32;
.
.
.
. mysql> update bank set debit=777
.> Wait unlock where id=32;
. ERROR 1213 (40001): Deadlock
. found when trying to get lock;
. Try restarting transaction
.
.
. >T2 tiene bloqueado a 32, T1
. espera la liberacion

89
>al hacer el update en T2 ahora T2
mysql> update bank set debit=444 where espera a T1
id=32; >por lo tanto se produce un
Query OK, 1 row affected deadlock
(30.83 sec) >el dbms hace rollback en T2
Rows matched: 1 Changed: 1 (ultimo en espera) y procesa T1
Warnings: 0

mysql> select * from bank;


+------+-------+
| id | debit |
+------+-------+
| 32 | 444 | mysql> select * from bank;
| 66 | 3453 | +------+-------+
+------+-------+ | id | debit |
2 rows in set (0.00 sec) +------+-------+
mysql> commit; | 32 | 333 |
Query OK, 0 rows affected | 66 | 3453 |
(0.00 sec) +------+-------+
2 rows in set (0.00 sec)
mysql> select * from bank; mysql> commit;
+------+-------+ Query OK, 0 rows affected
| id | debit | (0.00 sec)
+------+-------+
| 32 | 444 | mysql> select * from bank;
| 66 | 3453 | +------+-------+
+------+-------+ | id | debit |
2 rows in set (0.00 sec) +------+-------+
| 32 | 444 |
| 66 | 3453 |
+------+-------+
2 rows in set (0.00 sec)
mysql> set autocommit=0; mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.01
mysql> select * from bank where id=32 sec)
lock in share mode;
+----+-------+
| id | debit |
+----+-------+
| 32 | 444 |
+----+-------+ mysql> update bank set debit=555
1 row in set (0.00 sec) where id=32;
> .
> Nadie puede modificar la tupla 32 .
> .
.
mysql> commit; .> Wait unlock
Query OK, 0 rows affected .
(0.00 sec) .
.
.
.
.
mysql> update bank set debit=555
mysql> select * from bank; where id=32;
+----+-------+ Query OK, 1 row affected
| id | debit | (13.21 sec)
+----+-------+ Rows matched: 1
| 32 | 444 | Changed: 1 Warnings: 0
| 66 | 3453 |
+----+-------+
2 rows in set (1.02 sec)

mysql> select * from bank;


+----+-------+ mysql> commit;
| id | debit | Query OK, 0 rows affected (0.14
+----+-------+ sec)
| 32 | 555 | mysql> select * from bank;

91
| 66 | 3453 | +----+-------+
+----+-------+ | id | debit |
2 rows in set (0.01 sec) +----+-------+
| 32 | 555 |
| 66 | 3453 |
+----+-------+
2 rows in set (0.00 sec)

4.8. Tipos de Usuario

En los SGBD en general pueden intervenir una gran cantidad de usuarios, cada uno con
mayor o menor nivel de acceso y cualificación. Respecto a la seguridad de una base de
datos, conviene recordar qué tipo de usuarios se distinguen:

Administrador

El DBA (Database Administrator) es aquella persona o grupo de personas que sostiene


la máxima responsabilidad en el mantenimiento de una SGDB. Sus funciones se pueden
resumir en:

 Definir el esquema lógico de la base de datos.


 Definir el esquema físico.
 Definir los subesquemas (lo que conocemos como vistas o consultas).
 Llevar un mantenimiento de los mencionados esquemas.
 Concretar qué procedimientos seguir para mantener la seguridad, integridad y
privacidad de los datos.
Usuario especializado
Este tipo de usuario es el que desarrolla otros sistemas más complejos a partir del
SGBD. También caen en esta categoría aquellos usuarios que utilizan el SGDB como
fuente de información específica para su labor, volcando grandes cantidades de datos
para su análisis en bloque.

Usuario técnico
Son aquellos que elaboran aplicaciones del SGDB para ser utilizadas por los usuarios
finales.

Usuario final
Usuarios no especializados. Utilizan las aplicaciones diseñadas por los técnicos para
manejar la base de datos. La información que tratan está restringida al marco de su
actividad concreta por medio de los subesquemas (vistas) previamente definidos.

4.9. Implementación de políticas de seguridad

El SGBD debe garantizar que la información esté accesible únicamente a aquellos


usuarios que estén autorizados, impidiendo el acceso en caso contrario. En MySQL,
esta función la realiza el sistema de privilegios, el cual establece quién puede conectar
al servidor. También garantiza que cada usuario ejecute sólo aquellas operaciones para
las que esté autorizado.

El servidor de MySQL trabaja con cuentas (nombre de host, usuario, contraseña y


privilegios) organizadas en el diccionario de datos. Al acceder a él, primero se
comprueba la identidad del usuario y a continuación los permisos para hacer
operaciones sobre la base de datos. En ambas etapas se usan tablas user, db y host.

Permisos

La segunda etapa del control de acceso consiste en una serie de operaciones que
realiza el servidor para verificar que cada cliente tiene suficientes privilegios para realizar
sus peticiones. En este proceso, se accede a las tablas:

 user: permisos de acceso global.


 host: permisos de acceso al servidor..
 db: permisos a nivel de base de datos.
 tables_priv: permisos a nivel de tabla.
 columns_priv: permisos a nivel de columna.

Los valores en estas tablas son tipo "sí" (Y) y "no" (N). Un permiso Y, autoriza a realizar
la operación. Un permiso N, hará al sistema pasar a la siguiente tabla (desde db hasta
todas las tables_priv y columns_priv) hasta dar con permisos positivos. Si ninguna tabla
autoriza al usuario (todas con valor N), la operación será denegada.

Cada tabla de permisos tiene dos tipos de columnas:

93
 Columnas de alcance: Determinan el alcance de cada registro en las tablas, es
decir, el contexto en que el registro se aplica.
 Columnas de privilegios: Indican qué privilegios se otorgan a una cuenta sobre
ciertas bases de datos, tablas y/o columnas (y otros objetos del SGBD).

Al realizar una nueva conexión, el servidor realiza comprobaciones sobre las tablas de
permisos:

 En la tabla user, las columnas de alcance determinan si se rechazan o se aceptan


conexiones entrantes. Las columnas de privilegios otorgados en ella son de carácter
global (aplicados a todas las BD alojadas en el servidor).
 En la tabla db, las columnas de alcance determinan qué usuarios pueden acceder a
qué bases de datos desde qué equipo. Las columnas de privilegios determinan qué
operaciones se permiten a estos usuarios sobre estas BD y sus tablas.
 La tabla host se usa junto a la tabla db para configurar registros que se apliquen a
varios equipos.

Cuando un usuario intenta acceder a un servidor MySQL, éste procede a validar su


identidad y su clave (en la tabla user, lee las columnas host, user y password). Una vez
validado el acceso, entra en estado de espera de peticiones. El servidor sólo acepta la
conexión si las columnas host y user de alguna de las user coinciden con el número de
máquina (IP) y usuario del cliente, además de la clave de acceso.

4.10. Aplicación

Para concluir con el contenido de la guía, el estudiante deberá elaborar una aplicación
que sirva para la administración de base de datos, la misma que podrá realizarla con el
uso de cualquier lenguaje de programación.
Bibliografía
Amor, R. V. (11 de Septiembre de 2015). Adictos al trabajo. Obtenido de Introducción a índices
en MySQL: https://www.adictosaltrabajo.com/2015/09/11/introduccion-a-indices-en-
mysql/
Barrena, A. (24 de 09 de 2015). anerbarrena.com. Obtenido de Crear una tabla en una base de
datos: https://www.anerbarrena.com/create-table-mysql-5023/
Barrena, A. (11 de 01 de 2016). ANER BARRENA. Obtenido de CREAR VISTAS:
https://www.anerbarrena.com/create-view-mysql-5101/
Fernández, G. (6 de diciembre de 2013). poesiabinaria.net. Obtenido de
https://poesiabinaria.net/2013/12/bucles-y-cursores-en-mysql-con-ejemplos/:
https://poesiabinaria.net/2013/12/bucles-y-cursores-en-mysql-con-ejemplos/
Gardey, J. P. (22 de 11 de 2012). Definicion.de. Obtenido de Definición de modelo de datos:
https://definicion.de/modelo-de-datos/
GUEBS. (12 de 05 de 2016). MANUALES. Obtenido de DISPARADORES:
https://manuales.guebs.com/mysql-5.0/triggers.html
Hispano, M. (25 de 07 de 2016). Transacciones en Mysql. Obtenido de Programacion.net:
https://programacion.net/articulo/transacciones_en_mysql_242
Jairo, J. (15 de 04 de 2012). Mysql. Obtenido de funciones de agrupamiento:
http://jjmojicamysql.blogspot.com/2012/04/34-funciones-de-agrupamiento-count-
max.html
Java, E. (13 de 11 de 2010). Edu Java. Obtenido de DL y DML. Sentencias SELECT FROM
WHERE y ORDER BY con Mysql Workbench.: http://www.edu4java.com/es/sql/sql4.html
Logicalis. (12 de 07 de 2015). Modelo relacional en la gestión de bases de datos. Obtenido de
Modelo relacional en la gestión de bases de datos:
https://blog.es.logicalis.com/analytics/conceptos-basicos-del-modelo-relacional-en-la-
gestion-de-bases-de-datos
Lucid. (10 de 09 de 2015). Lucid Chart. Obtenido de Qué es un modelo de base de datos:
https://www.lucidchart.com/pages/es/qu%C3%A9-es-un-modelo-de-base-de-datos
MirCha, J. (22 de 10 de 2014). EDTEAM. Obtenido de Modelo Entidad-Relación:
https://ed.team/blog/modelo-entidad-relacion
Morales, J. D. (30 de 06 de 2014). IBM. Obtenido de Características y tipos de bases de datos:
https://www.ibm.com/developerworks/ssa/data/library/tipos_bases_de_datos/index.html
MUÑOZ, E. (29 de 01 de 2015). A un clic de distancia. Obtenido de A un clic de distancia:
https://aunclicdedistancia.wordpress.com/2015/01/29/pasar-del-modelo-entidad-
relacion-al-modelo-relacional/
uaeh. (10 de 09 de 2010). cidecame. Obtenido de lenguaje de manipulacion de datos:
http://cidecame.uaeh.edu.mx/lcc/mapa/PROYECTO/libro14/53__lenguaje_de_manipula
cin_de_datos_dml.html

95
Valencia, U. P. (04 de 01 de 2011). Blog Historia de la Informática. Obtenido de Historia de las
bases de datos: http://histinf.blogs.upv.es/2011/01/04/historia-de-las-bases-de-datos/

Vous aimerez peut-être aussi