Sunteți pe pagina 1din 174

Lenguaje Sql con MySQL avanzado Parte I.

Vistas

PARTE I.
VISTAS

Tema 1. Vistas

1.1 Antes de comenzar.................................................................................2


1.2 Ventajas de su utilizacin ......................................................................6
1.3 Creacin. Diccionario de Datos.............................................................8
1.4 Modificacin........................................................................................14
1.5 Borrado ................................................................................................14
1.6 Operaciones sobre vistas. Restricciones..............................................15
1.7 Ejercicios resueltos ..............................................................................19

Pgina 1
Lenguaje Sql con MySQL avanzado Parte I. Vistas

TEMA 1.
Vistas
Alberto Carrera Martn

1.1 Antes de comenzar


a) COMPROBACIN DEL ESTADO DEL SERVIDOR

Antes de empezar a trabajar asegrate que la base de datos est arrancada. Si utilizas
Windows XP podrs comprobarlo mediante:

Botn Inicio / Panel de Control / Rendimiento y Mantenimiento / Herramientas


Administrativas / Servicios

Ilustracin 1. Servicio de MySQL

En este caso se encuentra iniciado, si no fuera as lo podras arrancar haciendo clic con
el botn derecho sobre el servicio y eligiendo la opcin de Iniciar.

Tambin lo puedes saber de otras formas y de manera grfica ejecutando el monitor de


sistema de MySQL que tras lanzarlo:

Botn Inicio / Todos los programas / MySQL / MySQL System Tray Monitor

aparecer con el siguiente icono en la barra de estado . Al hacer un clic con el botn
derecho sobre l se puede comprobar que la base de datos est corriendo o
ejecutndose tal y como se aprecia en la primera lnea de siguiente ilustracin 2. El
rectngulo verde del icono ya hace presagiar tal situacin; si la figura que nos
hubiramos encontrado dentro del icono hubiera sido un cuadrado rojo, entonces la base
de datos estara parada y por tanto deberamos arrancarla (opcin Start Instance en
ilustracin 3 siguiente)

Pgina 2
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Ilustracin 2. Monitor de MySQL. Servicio Ilustracin 3. Monitor de MySQL. Servicio


ejecutndose detenido

b) CREACIN DE LA BASE DE DATOS DE EJEMPLO

La realizaremos de dos maneras de las varias posibles existentes. Lee las dos y despus
pon en prctica la que desees (o puedes experimentar con las dos)

b1) Creacin de la base de datos ejemplo utilizando una ventana de consola de


windows

Los archivos binarios del servidor se encuentran instalados en la carpeta:

C:\Archivos de programa\MySQL\MySQL Server 5.0\bin

Nota: La unidad de disco anterior (C:) y la ruta de carpetas puede cambiar en funcin de
donde se realizara la instalacin.

Dentro de la carpeta anterior crea a su vez una carpeta denominada bases y copia all el
script tablas1.sql que encontrars en la seccin de materiales del curso. Una vez hecho,
arranca una ventana de consola (Botn Inicio / Todos los programas / Accesorios /
Smbolo de sistema), sitate en la carpeta anterior (C:\Archivos de
programa\MySQL\MySQL Server 5.0\bin>) y ejecuta el comando siguiente que te
permitir conectarte a la base de datos como usuario root y al mismo tiempo ejecutar
las instrucciones que contiene el script tablas1.sql copiado a la carpeta bases anterior:

mysql -u root -p < bases\tablas1.sql

La Instruccin anterior es equivalente a:


mysql -u root -p < "C:\Archivos de programa\MySQL\MySQL Server 5.0\bin\bases\tablas1.sql"

Tras el mensaje de introduccin de la clave del usuario root

Enter password: ******

El script tablas1.sql crea, adems de las tablas que puedes ver al final de este apartado,
un usuario1 de clave usuario1 con todos los permisos de trabajo sobre la base de datos

Pgina 3
Lenguaje Sql con MySQL avanzado Parte I. Vistas

practica1 que se acaba de crear. Se puede comprobar si en la ventana de consola abres


una conexin con el usuario1:

D:\Archivos de programa\MySQL\MySQL Server 5.0\bin>mysql -u usuario1 -p


Enter password: ********
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 41 to server version: 5.0.20a-nt

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

mysql> SHOW DATABASES;


+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| practica1 |
| test |
+--------------------+
4 rows in set (0.77 sec)

mysql> USE practica1


Database changed
mysql> SHOW TABLES;
+---------------------+
| Tables_in_practica1 |
+---------------------+
| centros |
| departamentos |
| empleados |
+---------------------+
3 rows in set (0.01 sec)

b2) Creacin de la base de datos ejemplo utilizando una herramienta cliente

Otra forma ms atractiva de realizar la tarea anterior es utilizar la herramienta grfica de


MySQL Query Browser (para este
curso se ha utilizado la versin 1.1.2.0
que se encuentra en el fichero mysql-
query-browser-1.1.20-win.msi). Su
instalacin es muy sencilla. Tras
lanzarla desde el monitor de MySQL
visto anteriormente en el apartado
1.1.a (ilustraciones 2 y 3) o tambin
mediante Botn Inicio / Todos los
programas /MySQL / MySQL Query
Browser aparecer la pantalla inicial
de la ilustracin 4 de la derecha. Tras
introducir el usuario/clave de root,
pulsa OK para avanzar a la siguiente
pantalla.
Ilustracin 4. MySQL Query Browser

Pgina 4
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Observa que se ha
introducido el usuario y la
clave del Administrador root
pero no la base de datos a la
que se conecta (Default
Schema) por lo que tras
pulsar el botn OK puede
aparecer la siguiente
ilustracin 5 de la izquierda
pantalla para recordrnoslo:

Ilustracin 5. MySQL Query Browser

Pulsaremos el botn Ignore para continuar y entrar en la herramienta cliente. Una vez
en ella abriremos (opcin File / Open Script del men) el script tablas1.sql que
podrs encontrar en la seccin de materiales del curso (ver ilustracin 6)

Ilustracin 6. Cargando un script en MySQL Query Browser

A continuacin puedes ejecutar el script mediante el botn Execute (parte superior


derecha como se puede apreciar en la ilustracin 6 anterior) con lo que habrs creado el

Pgina 5
Lenguaje Sql con MySQL avanzado Parte I. Vistas

usuario usuario1 de clave usuario1 y la base de datos prctica1 con las 3 tablas que la
componen (haciendo doble clic en cada una de ellas podrs ver sus registros)

Ilustracin 7. Base de datos practica1 creada

1.2 Ventajas de su utilizacin


Nota: Para probar los ejemplos siguientes puedes hacerlo conectndote a la base de
datos (default schema) practica1 como usuario usuario1 de clave usuario1 mediante las
dos formas vistas en el apartado anterior. Por su sencillez y claridad utilizaremos la
segunda de ellas, la herramienta grfica MySQL Query Browser. Despus de introducir
cada sentencia recuerda pulsar el botn Execute.

Antes de definir lo que es una vista vamos a ver las ventajas que nos aportan.

- Seguridad y confidencialidad: Si un empleado de la centralita telefnica necesita para


su trabajo conocer exclusivamente el nombre y extensin telefnica de todos sus
Compaeros, el resto de columnas con datos privados como el nmero de hijos,
sueldo de la tabla debe ser invisible para l. Por tanto el Administrador de la base
de datos crear una visin parcial (vista1a en el ejemplo) de la tabla:

Instruccin 1

y slo le permitir consultar dicha vista y no la tabla original:

Pgina 6
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Instruccin 2

Desde el punto de vista de la administracin, es ms fcil administrar permisos sobre


una vista que asignar privilegios a las columnas de las tablas.

- Facilidad de uso para consultas complejas. Si un Jefe desea consultar los Centros
junto con sus Departamentos y nombres de los Directores:

Ilustracin 8. Centros, Departamentos y Directores

en lugar de tener que realizar la siguiente consulta:

Instruccin 3

el Administrador puede preparar la siguiente vista:

Pgina 7
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Instruccin 4

para que el Jefe slo tenga que consultar:

Instruccin 5
y con ello obtendr las 8 filas y 5 columnas anteriores con toda la informacin sobre
centros, departamentos y sus directores.

Nota: Si no ests acostumbrado a la sintaxis anterior en la que se utiliza la clusula


JOIN ON para unir tablas puedes utilizar esta otra:

Instruccin 6

Hay que advertir que aunque el aspecto de una vista sea el de una tabla, una vista no es
nada ms que una instruccin SELECT del SQL almacenada con un nombre (vista1a,
vista1b). Por tanto no contiene datos (filas) permanentemente como una tabla y de
esta manera no hay ficheros asociados a ellas. De todas formas, como se puede
comprobar con la ltima instruccin lanzada, la forma de manejar vistas y tablas es muy
similar y podemos estar consultando un origen de datos sin saber si ste es una tabla o
una vista.

1.3 Creacin. Diccionario de Datos


Pueden utilizarse las vistas a partir de la versin de MySql 5.0.1.
Para poder crear vistas se requiere el privilegio CREATE VIEW as como los
privilegios para poder seleccionar las columnas que forman parte de la vista. Sintaxis
bsica:

CREATE [OR REPLACE]


[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
[DEFINER = {nombreusuario} | CURRENT_USER}]
[SQL SECURITY {DEFINER | INVOKER}]
VIEW nombre_vista [(lista_columnas)]
AS sentencia_select
[WITH [CASCADED | LOCAL] CHECK OPTION]

Pgina 8
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Recuerda que las palabras reservadas que aparecen entre corchetes [] son opcionales. La
| indica elegir una opcin entre las posibles alternativas.

La sentencia anterior crea una nueva vista o reemplaza una existente (clusula OR
REPLACE) si esta ya existiera.

El creador de la vista, en este caso usuario1, debe estar autorizado para poder crear
vistas, es decir, poseer el privilegio CREATE VIEW. Si observas las lneas 4 y 5 del
script tablas1.sql con el que ests trabajando y que aparecen a continuacin, el
Administrador root se lo ha concedido, tanto este privilegio como todos los posibles:

Instruccin 7
El resto de clusulas de creacin de vistas:

- ALGORITHM permite elegir entre dos modos de ejecucin: MERGE o


TEMPTABLE. Si se utiliza MERGE, las consultas que se ejecutan sobre la vista se
combinan con la consulta de definicin de la vista para crear una nueva consulta. Si se
usa TEMPTABLE, se utiliza la definicin de la vista para crear una tabla temporal
donde se almacenan los datos (y por tanto las consultas se ejecutan sobre esta tabla
temporal). Es una clusula opcional. Si no se especifica o se define como
UNDEFINED, MySQL analizar la mejor opcin posible, generalmente MERGE pues
el inconveniente que presenta la opcin TEMPTABLE es que las tablas temporales
sobre las que trabaja no tienen ndices y por tanto el rendimiento de las consultas puede
ser inferior si se utiliza esta opcin.

- DEFINER (a partir de MySQL 5.1.2) especifica el propietario de la vista. Por defecto


es el usuario actual (CURRENT_USER).

- SQL SECURITY (a partir de MySQL 5.1.2) especifica el modo de interactuar con la


vista. Se puede precisar que las consultas que se efecten sobre la vista se traten con los
privilegios del propietario de la vista (DEFINER) o con los privilegios de quien ejecute
las consultas sobre la vista (INVOKER); la primera de las dos anteriores es la opcin
por defecto.

- nombre_vista es el nombre que se da a la vista, no debe ser el mismo que el utilizado


para una tabla. Si se desea crear la vista en otra base de datos distinta de la actual
entonces deber especificarse la base de datos mediante la siguiente notacin:
base_datos.nombre_vista.

- lista_columnas son los nombres de columnas que va a contener la vista. Si no se


ponen, se entender que sern todas las columnas que devuelva la sentencia SELECT de
consulta; esta ltima determinar las columnas y tablas que aparecern en la vista; si se
indica la lista de columnas deber hacerse separada por comas y el nmero de nombres
debe ser el mismo que el nmero de columnas devuelto por la sentencia SELECT. Al
igual que las tablas, las vistas no pueden tener nombres de columnas duplicados. Las
columnas devueltas por la sentencia SELECT pueden ser simples referencias a
columnas de la tabla, pero tambin pueden ser expresiones conteniendo funciones,
constantes, operadores, etc.

Pgina 9
Lenguaje Sql con MySQL avanzado Parte I. Vistas

- WITH CHECK OPTION aade una opcin de seguridad a la hora de realizar


operaciones de manipulacin de datos utilizando vistas (en el apartado 1.6 veremos por
su importancia algn ejemplo). Utilizando LOCAL los datos que se inserten a travs de
la vista deben respetar solamente las restricciones de la vista. Si se especifica
CASCADED en el momento de la creacin de la vista entonces deben respetarse las
restricciones de la vista fuente o padre a partir de la cual fue creada. La opcin por
defecto es CASCADED.

Ejemplos de utilizacin.

Notas:
- El nombre que le damos a las vistas en este tema, para su fcil localizacin, es
vista1a, vista1b... Realmente debemos utilizar nombres mucho ms
significativos que indiquen qu informacin va a presentar (como p.ej. los
utilizados en los ejemplos del apartado 6).
- Todos los ejemplos se ejecutan desde el browser teniendo seleccionada la base
de datos practica1 (ver ilustracin 7 anterior). De esta manera se omite la
seleccin de la misma (sentencia USE practica1) o preceder al nombre de cada
tabla o vista el nombre de la base de datos (SELECT * FROM
practica1.vista1a;).

Ejemplo1. Nombre y presupuesto de todos los departamentos del centro nmero 10:

Instruccin 8

Si consultamos la vista anterior:

Instruccin 9

Como comentbamos anteriormente, a efectos de consulta y manipulacin, una vista


puede ser considerada como una tabla. A continuacin seleccionamos de la vista
anterior aquellos departamentos que superan los 5000000 de presupuesto:

Pgina 10
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Instruccin 10
Posiblemente una vez creada no puedas ver la vista en el panel del esquema a no ser que
cierres el programa cliente grfico y vuelvas a entrar. Para evitar esto, selecciona con el
botn derecho la base de datos practica1 y elige la opcin de refrescar (Refresh) tal y
como aparece en la ilustracin 9:

Ilustracin 9. Refrescando la base de datos practica1


Ejemplo 2.

Nombre, salario y nmero de departamento de los empleados que superan los 400 de
salario:

Instruccin 11
Ejemplo 3.

A quin debemos felicitar el 12 de octubre:

Instruccin 12
Ejemplo 4

Nombre, nmero de departamento y antigedad en aos en la empresa de los empleados


(recordad que la columna fecin contiene la fecha de ingreso en la empresa):

Pgina 11
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Instruccin 13
Ejemplo 5

Total de empleados y media de sus salarios

Instruccin 14

Dnde se guarda la definicin de la vista?

Si miras el panel Schemata (ilustraciones 9 y 10) en la


parte superior derecha del browser encontrars
informacin sobre las base de datos. Una de ellas, la
information_schema, contiene datos acerca de los
datos, es decir informacin sobre todos los objetos de
las bases de datos existentes en el servidor, es lo que
se conoce como DICCIONARIO DE DATOS o
CATALOGO del SISTEMA. La informacin que
contienen sus tablas es de slo lectura; realmente no se
trata de tablas sino de vistas. Haciendo doble clic en
cada una de sus entradas vers toda la informacin
relativa a la misma. Si lo haces sobre el objeto VIEWS
podrs ver todas las caractersticas de las vistas
existentes en las bases de datos del servidor MySQL
(ilustracin 11 siguiente):

Ilustracin 10. Schemata

Pgina 12
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Ilustracin 11. Vista VIEWS

Si tuvieras acceso a varias bases de datos y slo quisieras saber las vistas de que
dispones en la base de datos practica1, la misma informacin anterior la podras
obtener:

Instruccin 15

Viendo las filas resultantes puedes comprobar que la columna TABLE_NAME contiene
el nombre de la vista y que la columna VIEW_DEFINITION la sentencia SELECT que
la construye. Por tanto si intentaras ver esta ltima columna solamente y para una vista
en concreto:

Instruccin 16
Lo que ocurre con la anterior consulta es que puedes encontrar una expresin un poco
confusa a la hora de mostrar la sentencia SELECT y adems en caso de que sta sea
larga no verse por completo y aparecer partida. Por eso quizs sea mejor seleccionar la
vista deseada del diccionario de datos, en concreto su columna VIEW_DEFINITION
(p.ej. de cualquiera de las vistas que aparece en la ilustracin 11 anterior) y con el
botn derecho elegir la opcin View Field in Popup Editor.
Tambin puedes conseguir ver completa la instruccin que construye la vista mediante
una ventana cliente (Tools / MySQL Command Line Client de la barra de mens) y el
comando:

SHOW CREATE VIEW practica1.vista1g;

Pgina 13
Lenguaje Sql con MySQL avanzado Parte I. Vistas

A travs de los conceptos aqu expuestos podrs comprender la funcionalidad del resto
de columnas de la tabla (vista) anterior.

1.4 Modificacin
La sintaxis bsica es muy similar a la de creacin:

ALTER
[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
[DEFINER = {nombreusuario} | CURRENT_USER}]
[SQL SECURITY {DEFINER | INVOKER}]
VIEW nombre_vista [(lista_columnas)]
AS sentencia_select
[WITH [CASCADED | LOCAL] CHECK OPTION]

Lo que hace es modificar la especificacin de la vista. Realmente es como si se borrara


y se volviera a crear con la nueva especificacin de la sentencia SELECT por lo que no
se suele utilizar esta sentencia puesto que se puede conseguir los mismos resultados que
utilizando la clusula REPLACE de la sentencia CREATE. Como equivale a borrarla y
volverla a crear, el usuario que lo haga debe tener los privilegios de creacin y borrado
para vistas y los de las columnas referenciadas en la sentencia SELECT de creacin de
la vista.

Ejemplo, si queremos que la ltima columna de la vista1f no sea la antigedad en la


empresa sino la edad de los empleados, podemos borrar la vista (siguiente apartado) y
volverla a crear o:

Instruccin 17

1.5 Borrado
A partir de la version de MySql 5.0.1 como las anteriores sentencias. Sintaxis:

DROP VIEW [IF EXISTS]


vista1 [, vista2] ...

Borra una o ms vistas. Para poder realizarlo se debe poseer los privilegios de borrado
sobre vistas. La clusula IF EXISTS evita que aparezca mensaje de error en caso de
que la vista a borrar no exista y en su lugar aparece un mensaje informativo.

Pgina 14
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Ejemplo de borrado de la vista vista1b

Instruccin 18

1.6 Operaciones sobre vistas. Restricciones


Antes de indicar las operaciones que se pueden realizar sobre las vistas y sus
restricciones, apuntar las normas ms importantes a tener en cuenta a la hora de la crear
vistas:
- La sentencia SELECT de creacin de la vista no puede hacer referencia en la
clusula FROM ni a una tabla temporal (TEMPORARY TABLE) ni contener
una subconsulta.
- La sentencia SELECT no puede referirse a variables de usuario o de sistema.
- Dentro de un procedimiento almacenado (se estudian en el siguiente tema), la
definicin de la vista no puede hacer referencia ni a los argumentos del
procedimiento ni a las variables locales del mismo.
- Cualquier tabla o vista a la que referencia la nueva vista debe existir
previamente.
- No se puede asociar un disparador con una vista (se tratan los disparadores
tambin en el siguiente tema de este curso).

a) Consulta Ya visto en los apartados anteriores.


b) Actualizacin En general, si una vista est basada en una sola tabla, al modificar la
vista se est modificando directamente la tabla.
Ejemplo: Si tenemos la siguiente vista:

Instruccin 19

Cualquiera de las dos actualizaciones siguientes nos lleva al mismo resultado,


actualizando 3 filas, cambiando la extensin telefnica 760 por la 990 a 3 empleados
de la empresa. La diferencia est en que sentencia de la derecha se est modificando
la tabla empleados a travs de la vista y en la de la izquierda se hace directamente a
travs de la tabla.

Instruccin 20 Instruccin 21

Es posible poder realizar actualizaciones de las tablas a travs de las vistas?

No siempre. Depende de la sentencia SELECT que construye la vista a partir de la


tabla. Antes de ver algunas restricciones Cmo se puede averiguar si se pueden

Pgina 15
Lenguaje Sql con MySQL avanzado Parte I. Vistas

modificar datos de una tabla a travs de una vista? Muy sencillo, consultando el
diccionario de datos o lo que es lo mismo, la base de datos information_schema

Ilustracin 12. Vista VIEWS


La columna IS_UPDATABLE de la vista VIEWS nos indica si a travs de una vista
puedo modificar (YES) o no una tabla.

Recuerda que puedes obtener la misma informacin sin moverte de tu esquema


(base de datos) practica1:

Instruccin 22

Pgina 16
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Por qu vista1a y vista1g no son actualizables y el resto s?

Hay una serie de restricciones a tener en cuenta. Si se da alguna de las


caractersticas siguientes en la sentencia SELECT que construye la vista a partir de
la tabla, la vista no es actualizable:

- Contiene funciones de agregado como SUM(), MIN(), MAX(), COUNT() (por


eso la vista1g no es actualizable.
- Contiene la clusula DISTINCT o GROUP BY o HAVING o UNION o JOIN u
ORDER BY (vista1a).
- Contiene una subconsulta.

c) Insercin Es posible realizar inserciones en la tabla a travs de una vista definida


sobre ella? Si como podrs comprobar con este ejemplo, que inserta una fila en la
tabla empleados a travs de una vista construida sobre ella:

Instruccin 23

Pero la tabla base empleados tiene muchos ms campos como nmero de


departamento, salario, comisin, nmero de hijos Con qu valores se rellenan
estos valores? Mira y vers:

Ilustracin 13. Campos de la tabla empleados


Por qu se ha podido hacer esto?

Si miras la descripcin de la tabla mediante el script que lanzaste para crearla o


directamente desde el browser (botn derecho sobre la tabla empleados de la base de
datos practica1 opcin Edit Table) como aparece en la siguiente figura, vers que el
nico campo que no debe quedar nulo (por ser clave primaria en este caso) es el de
nmero de empleado (numem):

Ilustracin 14. Descripcin de la tabla empleados

Pgina 17
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Siempre que no nos dejemos de rellenar los campos obligatorios de la tabla base
podremos hacer inserciones a travs de la vista.

En cambio, si utilizamos la vista vista1d (contiene las columnas nombre, salario y


nmero de departamento) para insertar en la tabla base de empleados no podremos
hacerlo pues nos estamos dejando de introducir el campo numem (que es obligatorio y
no autonumrico).
Realmente la informacin de esas tres ltimas lneas no es del todo cierta, pues si
pruebas a realizar la siguiente insercin:

Instruccin 24

te va a dejar hacerlo siendo que falta el campo obligatorio (y no autonumrico) del


nmero de empleado (numem). La razn de ello, como podrs comprobar tal y como
hemos hecho en la descripcin de esta tabla, es que esta columna tiene como valor por
defecto 0 y por tanto le asigna a Raquel el nmero de empleado 0. A partir de ese
momento ya no puedes hacer ms inserciones como la que viene a continuacin pues ya
no puede volver a asignarle el 0 como nmero de empleado al estar la clave repetida:

Instruccin 25

Lo que debes recordar es que siempre podremos realizar inserciones en las tablas a
travs de sus vistas siempre y cuando no nos dejemos los campos obligatorios de la
tabla. Deberemos tener en cuenta adems de la anterior todas las restricciones que
aparecen al final del apartado b anterior.

Antes de terminar el tema comentar una opcin que puede aadir cierta seguridad a las
vistas y que se haba dejado planteada en el apartado 1.3 de este tema en relacin a la
creacin de vistas y su sintaxis bsica. La clusula es:

[WITH CHECK OPTION]


Para qu sirve ?

Observa la siguiente vista:

Instruccin 26

La sentencia anterior crea una vista con todos los datos de los empleados del
Departamento 100. A travs de esta vista, como se ha trabajado en los puntos anteriores
se puede realizar cualquier operacin de manipulacin de la tabla base empleados. Por
eso, estas dos instrucciones son totalmente correctas:

Pgina 18
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Instruccin 27

Instruccin 28

Pero en la segunda de ellas, hemos utilizado una vista que representa los empleados del
Departamento 100 para introducir un empleado que no cumple realmente la clusula
WHERE del SELECT de creacin de la vista ya que su departamento es el 120.
Si hubiramos creado la vista de esta otra forma (se ha aadido la comprobacin al
final):

Instruccin 29
no hubiramos tenido problemas en aadir al empleado Mario Carrera por ser del
Departamento 100 pero no nos hubiera dejado realizar la insercin de la empleada
Carmen Bailn dando mensaje de error ya que antes de hacer la insercin MySQL
chequea que los datos a introducir cumplan con la seleccin de la clusula WHERE de
la sentencia SELECT de creacin de la vista que seleccionaba las filas de empleados del
Departamento 100 y estamos intentando introducir una empleada de otro Departamento.
Recordad que tal y como se expuso en el apartado 1.3, la clusula WITH CHECK
OPTION equivale a WITH CASCADED CHECK OPTION en caso de que no se
especifique la opcin LOCAL, al ser CASCADED la opcin por defecto, y por tanto
todas las vistas que se creen a partir de la vista vista1i debern seguir respetando las
restricciones de la clusula WHERE de esta ltima.

1.7 Ejercicios resueltos.


Los siguientes ejemplos sirven para completar los vistos en este tema as como para
recordar las sentencias SQL y poder hacer los ejercicios propuestos.

Centros y sus Departamentos (puedes utilizar


si lo deseas la clusula JOIN ON en lugar de
la aqu propuesta):

Instruccin 30
Ilustracin 15. Resultado de la Instruccin 30

Pgina 19
Lenguaje Sql con MySQL avanzado Parte I. Vistas

Instruccin 31. Departamentos que no dependen jerrquicamente de otros

Instruccin 32. Nmero de empleados por Departamento

Ilustracin 16. Resultado de la Instruccin 32

La misma consulta que la anterior instruccin 32 pero especificando el nombre del


Departamento en lugar de su nmero:

Instruccin 33

Ilustracin 17. Resultado de la instruccin 33

Pgina 20
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

PARTE II.
PROGRAMAS ALMACENADOS

Tema 2. Programas almacenados en MySQL

2.1. Introduccin...................................................................................................3
2.2. Ventajas de su utilizacin..............................................................................3
2.3. Edicin de programas....................................................................................4
2.4. Fundamentos bsicos del lenguaje ................................................................8
2.4.1. Variables, tipos de datos, comentarios y literales.................................8
2.4.2. Variables de usuario ...........................................................................12
2.4.3. Parmetros ..........................................................................................13
2.4.4. Principales operadores........................................................................15
2.4.5. Expresiones.........................................................................................16
2.4.6. Funciones incorporadas ......................................................................17
2.4.7. Bloques de instrucciones ....................................................................20
2.4.8. El comando IF ....................................................................................22
2.4.9. El comando CASE..............................................................................24
2.4.10. El comando LOOP, LEAVE e ITERATE ........................................25
2.4.11. El comando REPEAT UNTIL.....................................................26
2.4.12. El comando WHILE .........................................................................27
2.4.13. Bucles anidados ................................................................................28
2.5. Procedimientos ............................................................................................28
2.5.1. Creacin de Procedimientos. Diccionario de datos ............................28
2.5.2. Modificacin de Procedimientos ........................................................31
2.5.3. Borrado de Procedimientos ................................................................32
2.5.4. Utilizacin de instrucciones DDL y DML
en procedimientos almacenados ........................................................32
2.5.5. Utilizacin de instrucciones de consulta
en procedimientos almacenados ........................................................32
2.5.6 Almacenar en variables el valor de una fila de una tabla ....................33
2.5.7 Sentencias preparadas. SQL dinmico ................................................35
2.6. Cursores.......................................................................................................38
2.6.1. Sentencias utilizadas con cursores. Ejemplos ....................................38
2.6.2. Cursores anidados...............................................................................43
2.7. Manejo de errores........................................................................................44
2.7.1 Introduccin a la gestin de errores..................................................44
2.7.2 Tipos de manejador ..........................................................................48
2.7.3 La condicin del manejador .............................................................49
2.7.4 Orden de actuacin del manejador ...................................................52
2.7.5 mbito de actuacin del manejador .................................................53
2.7.6 Ejemplo de tratamiento de errores....................................................54
2.8 Funciones....................................................................................................55
2.8.1 Creacin de funciones. Diccionario de datos ...................................55
2.8.2 Ejemplos de utilizacin de Funciones ..............................................56
2.8.3 Modificacin de funciones ...............................................................60
2.9 Triggers.......................................................................................................60
2.9.1 Creacin de triggers. Diccionario de datos.......................................60

Pgina 1
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.9.2 Borrado de triggers ...........................................................................62


2.9.3 Utilizacin de triggers ......................................................................63

Anexo. MySQL desde otras aplicaciones

1. Introduccin: Ventajas y desventajas ...................................................................66


2. Ejecucin de procedimientos almacenados. Tratamiento de errores....................67
2.1 En PHP ................................................................................................67
2.2 En Visual Basic Express 2005.............................................................69
3. Ejecucin de funciones.........................................................................................70
3.1 En PHP ................................................................................................70
3.2 En Visual Basic Express 2005.............................................................71
4. Ejecucin de sentencias DDL...............................................................................71
4.1 En PHP ................................................................................................71
4.2 En Visual Basic Express 2005.............................................................72
5. Ejecucin de sentencias preparadas......................................................................72
4.1 En PHP ................................................................................................72
4.2 En Visual Basic Express 2005.............................................................74

Pgina 2
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

TEMA 2
Programas almacenados en MySQL
Alberto Carrera Martn

2.1 Introduccin
La aparicin de los procedimientos almacenados, funciones y triggers
(desencadenadores) en MySQL 5 ha supuesto una enorme revolucin en este gestor de
bases de datos que ya disfrutaba de una gran popularidad. Se ha producido el salto para
que MySQL pueda ser utilizado como SGBD empresarial. A ello hay que aadir que la
sintaxis de los programas es sencilla lo que facilita su escritura.

Un programa almacenado es un conjunto de instrucciones almacenadas dentro del


servidor de bases de datos y que se ejecutan en l. Este conjunto se identifica por un
nombre.

Tipos de programas almacenados:

- Procedimientos almacenados: El ms comn de los programas almacenados.


Resuelven un determinado problema cuando son llamados y pueden aceptar
varios parmetros de entrada y devolver varios de salida.
- Funciones almacenadas: Similares a los procedimientos salvo que slo
devuelven un valor como parmetro de salida. La ventaja que presentan las
funciones es que pueden ser utilizadas dentro de instrucciones SQL y por tanto
aumentan considerablemente las capacidades de este lenguaje.
- Triggers o desencadenadores o disparadores: Son programas que se activan
(disparan) ante un determinado suceso ocurrido dentro de la base de datos.

Por el momento, MySQL a diferencia de otros SGBD comerciales no ofrece la


posibilidad de utilizar ni paquetes ni clases.

Los programas almacenados en MySQL cumplen en gran medida el estndar ANSI de


especificacin de ANSI SQL:2003 SQL/PSM (Persistent Stored Module).

2.2 Ventajas de su utilizacin


Ventajas de la utilizacin programas almacenados en comparacin con los realizados en
un determinado lenguaje y que no residen en el servidor:

- Mayor seguridad y robustez en la base de datos. Al permitir que los usuarios


puedan ejecutar diferentes programas (los que estn autorizados), se est
limitando e impidiendo el acceso directo a las tablas donde estn almacenados
los datos evitando la manipulacin directa de stas por parte de los usuarios y
por tanto eliminando la posibilidad de prdida accidental de los datos. Los
programas sern los que accedern a las tablas.

Pgina 3
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

- Mejor mantenimiento de las aplicaciones que acceden a los programas


almacenados y por tanto disminucin de la posibilidad de aparicin de errores.
En lugar de que cada aplicacin cliente disponga de sus propios programas para
realizar operaciones de insercin de consulta o actualizacin, los programas
almacenados permiten centralizar los mtodos de acceso y actualizacin
anteriores presentando una interfaz comn para todos los programas.
- Mayor portabilidad de las aplicaciones (relacionada con el punto anterior) puesto
que la lgica de la aplicacin ya queda implementada en los programas
almacenados permitiendo al programador centrarse slo en su interfaz.
- Debido a la fuerte integracin con el lenguaje SQL, no se necesita de ningn
tipo de conector o driver como ODBC (Open DataBase Connectivity) o JDBC
(Java DataBase Connectivity) para poder construir y ejecutar sentencias SQL.
Bastar agrupar estas ltimas bajo un programa almacenado que se llamar en el
momento en el que se necesite.
- Reduccin del trfico de red. El cliente llama a un procedimiento del Servidor
envindole unos datos. ste los recibe y tras procesarlos devuelve unos
resultados. Por la red no viajan nada ms que los datos. En contrapartida sealar
que se produce una carga ms elevada en el servidor, mucho ms que si las
aplicaciones se ejecutaran en los clientes, pero hoy en da no supone mucho
inconveniente con la tecnologa actual de servidor de que se dispone.

2.3 Edicin de programas


Para editar programas se pueden utilizar diferentes herramientas o mtodos:

- A travs de la lnea de comando del cliente MySQL.


- Herramienta MySQL Query Browser.
- Cualquier editor de texto o cualquier otra herramienta de terceros como el TOAD
para MySQL.

Como hemos hecho con el tema anterior de vistas, utilizaremos la herramienta MySQL
Query Browser porque presenta mayores ventajas como ayuda incorporada, posibilidad
de ejecutar sentencias SQL o la visualizacin de palabras clave del lenguaje resaltadas
en diferentes colores entre otras caractersticas.

Pasaremos a continuacin a crear nuestro primer procedimiento. Para ello:

1. Arranca la herramienta MySQL Query Browser (Botn Inicio / Todos los


Programas / MySQL / MySQL Query Borwser si trabajas bajo el sistema
operativo Windows). Aunque el usuario root solo debe utilizarse para labores de
administracin principalmente, para probar los diferentes procedimientos de
estos temas puedes conectarte con l y a la base de datos test tal y como muestra
la siguiente figura:

Pgina 4
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 1. Conexin a la base de datos


2. Elige el comando u opcin de men File / New Script Tab.
3. Introduce las lneas de cdigo que vienen a continuacin:

DELIMITER $$
DROP PROCEDURE IF EXISTS ejemplo1 $$
CREATE PROCEDURE ejemplo1 ()
BEGIN
SELECT 'Mi primer programa en MySQL';
END$$
DELIMITER ;

Breve explicacin: MySQL utiliza el carcter ; para finalizar cada sentencia SQL.
Como dentro del cuerpo del procedimiento las instrucciones van separadas por ;
(5 lnea) para distinguirlas de las sentencias SQL necesitamos utilizar otro carcter
delimitador (en la 1 lnea se habilita y en la ltima se vuelve a dejar el que estaba).
La sentencia DROP PROCEDURE. borra el procedimiento si este estuviera
previamente creado (de no ponerlo dara error); en la 3 lnea se crea dicho
procedimiento.
La 4 lnea indica el comienzo del cuerpo del procedimiento que finaliza en la
penltima lnea.

Ilustracin 2. Nuestro primer procedimiento

Pgina 5
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

4. Pulsa el botn Guardar o comando del men File / Save Nombre del script:
ejemplo1.
5. Para compilar el procedimiento anterior en busca de posibles errores (no es el
caso si lo has copiado igual que aqu aparece) elige la opcin de men Script /
Execute o el botn Execute (botn verde de la parte superior derecha de la
ilustracin 2 anterior). Si no te apareciera su nombre (ejemplo1) o cualquier otro
objeto que crees en adelante en la pestaa Schemata tal y como figura en la
ilustracin 2 anterior, selecciona la base de datos test y con el botn derecho la
opcin Refresh (refrescar):

Ilustracin 3. Opcin Refresh


6. Para ejecutar el procedimiento, haz doble clic sobre su nombre ejemplo1 que
figura dentro de la pestaa Schemata de la ilustracin 2 anterior (o tambin en la
ilustracin 4 siguiente). Esto se traduce en la siguiente sentencia de llamada:

Ilustracin 4. Ejecutando el procedimiento

A continuacin pulsa el botn Execute (si no aparece en pantalla se consigue la


misma funcionalidad pulsando la combinacin de teclas <ctrl.>+<enter> o
eligiendo el comando Query / Execute del men). Aparecer el resultado de la
ejecucin del programa:

Pgina 6
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 5. Resultado de la ejecucin

Puedes tambin crear procedimientos y funciones de una forma rpida utilizando la


opcin de men Script / Create Stored Procedure Function. De esta manera ahorrars
mucho tiempo introduciendo cdigo al insertarse automticamente las sentencias
bsicas. Para probar esta ltima posibilidad selecciona la base de datos test y pulsa la
opcin de men anterior:

Ilustracin 6. Creacin de procedimientos mediante comandos de men

Pulsa el botn Create PROCEDURE

Procedimiento 1 ejemplo1_bis

y vers que slo tienes que escribir en la lnea 6 que est vaca la instruccin:

SELECT 'Mi primer programa en MySQL';

El nombre que precede al del procedimiento corresponde al de la base de datos donde


queda almacenado dicho procedimiento, que es con la que ests trabajando al tenerla
seleccionada.

Pgina 7
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Para modificar o volver a editar el contenido de cualquier programa puedes hacerlo:

- Si no aparece en pantalla, utiliza el comando File / Open Script del men para
abrir el script que contiene el programa almacenado.
- Modifica las lneas de cdigo necesarias.
- Volver a guardarlo con la opcin del men File / Save.
- Sigue los mismos pasos vistos para compilarlo y ejecutarlo.

Si todo ha ido bien dispones del programa en


dos sitios, tanto en el disco duro de tu
ordenador como dentro de la base de datos.

Podras tambin modificar el contenido del


programa almacenado seleccionndolo de la
pestaa Schemata, despus hacer clic con el
botn derecho y comando Edit Procedure tal
y como muestra la ilustracin 7.
Despus de modificarlo puedes guardarlo,
compilarlo y ejecutarlo como se ha detallado
anteriormente.
Quizs sea ms interesante utilizar el primero
de estos dos mtodos, modificar directamente
el script almacenado en el disco por razones
de seguridad (disponer de una copia fuera de
la base de datos), por poder migrar el
procedimiento a otra base de datos distinta
(observa en este ltimo mtodo en la lnea 3
como este procedimiento ejemplo1bis est
asociado a la base de datos test) y siempre se
pueden guardar diferentes versiones en disco
(ejemplo1, ejemplo 1_1, ejemplo1_2) que
contengan los diferentes cambios que se han
ido produciendo en la mejora del programa.
Ilustracin 7. Otra forma de editar procedimientos

2.4 Fundamentos bsicos del lenguaje


2.4.1 Variables, tipos de datos, comentarios y literales
Las variables son elementos de datos con un nombre cuyo valor puede ir cambiando a lo
largo de la ejecucin del programa.

SINTAXIS:

DECLARE nombre_variable1 [,nombre_variable2...] tipo [DEFAULT valor];

La declaracin de variables se realiza antes del comienzo de las instrucciones (y antes


de los cursores y manejadores de errores que se ver tambin en este curso)

Pgina 8
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

De la sintaxis anterior se deduce que se pueden declarar varias variables del mismo tipo
seguidas y separadas por comas. Al mismo tiempo que se declaran las variables se
pueden definir asignndoles un valor inicial mediante la clusula DEFAULT; si no se
les asigna ninguno entonces las variables quedan definidas al valor NULL. En el
momento de la declaracin hay que indicar el tipo de datos de la variable; pueden
utilizarse cualquiera de los que se emplean en el momento de crear tablas mediante la
instruccin CREATE TABLE. Veremos los que se utilizan ms frecuentemente.

TIPOS DE DATOS NUMRICOS


INT, INTEGER Entero. Los valores pueden ir desde -2147483648 a
2147483647 para enteros con signo o desde 0 a 4294967295
para enteros sin signo
TINYINT El ms pequeo de los enteros. Rango entre -128 y -127 con
signo, o de 0 a 255 sin signo
SMALLINT Entero entre -32768 y 32767 (valores con signo) o entre 0 y
65535 (valores con signo)
MEDIUMINT Entero de valores con signo que van de -8388608 a 8388607 o
para valores sin signo de 0 a 16777215
BIGINT Entero grande. Con signo puede tomar valores desde -
9223372036854775808 a 9223372036854775807 y sin signo
de 0 a 18446744073709551615
FLOAT Real de precisin simple. Permite almacenar nmeros de -
1.7E38 a 1.7E38 con signo o de 0 a 3.4E38 para valores sin
signo
DOUBLE Real de precisin doble. Puede llegar a alcanzar valores de 0 a
1.7E308 para nmeros sin signo
DECIMAL(precisin, Equivalen al tipo DOUBLE pero se diferencian en que ocupan
escala) bastante mayor espacio (por almacenar valores exactos y no
NUMERIC(precisin, aproximados). Si el nmero de decimales es importante
escala) (cantidades monetarias) es mejor utilizar el tipo NUMERIC.
Precisin indica el nmero de dgitos totales, escala es el
nmero de decimales a la derecha de la coma del total de
dgitos que viene expresado en la precisin

TIPOS DE DATOS DE TEXTO


CHAR(longitud) Cadenas de texto de longitud fija hasta un mximo de longitud
de 255 caracteres. Si el valor a almacenar es ms corto que la
longitud de la variable el resto de caracteres se rellenan a
blancos
VARCHAR(longitud) Cadenas de texto de longitud variable hasta un mximo de 64
KB. A diferencia del tipo CHAR, si el valor a almacenar es
ms corto, el tamao real de la variable es el nmero de
caracteres que ocupa el valor pues no rellena a blancos. Como
almacena la longitud junto con los caracteres, su utilizacin en
los programas hace que le ejecucin de stos sea un poco ms
lenta que si se utiliza el tipo CHAR
ENUM Almacena un valor concreto de un conjunto posible de valores
SET Similar a ENUM pero permite guardar ms de un valor
TEXT Texto de hasta 64 KB. de tamao
LONGTEXT Texto de hasta 4 GB. de tamao

Pgina 9
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

TIPOS DE DATOS DE FECHA Y HORA


DATE Fechas con el formato AAAA-MM-DD entre 1000-01-01 y 9999-12-31
DATETIME Fecha y hora con el formato AAAA-MM-DD hh:mm:ss. Para la parte de
la hora el rango debe estar entre 00:00:00 y 23:59:59

OTROS TIPOS DE DATOS


BLOB Hasta 64KB. de datos binarios
LONGBLOB Hasta 4GB. de datos binarios

A continuacin un ejemplo de declaracin de variables, el contenido de algunas de ellas


se visualiza mediante las lneas de cdigo 16 a 18

Procedimiento 2 variables1

Otro ejemplo de declaracin de variables. Utilizamos la sentencia de asignacin SET


para asignar valores a variables como se ver ms adelante:

Procedimiento 3 variables2

Pgina 10
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

De las figuras anteriores se observa:

- Aparecen dos tipos de COMENTARIOS:


o Comentarios de una sola lnea, precedidos de --
o Comentarios de varias lneas, entre /* */, aunque en este caso slo se
extiende el comentario a lo largo de una sola lnea.
- Los LITERALES de texto y fecha van encerradas entre .

Las reglas para nombrar variables son bastante flexibles pues a diferencia de otros
lenguajes se permite:

1. Nombres largos (ms de 255 caracteres).


2. Caracteres especiales.
3. Pueden comenzar con caracteres numricos.

Todas las variables que se pueden utilizar deben ser escalares, es decir, un solo valor, a
diferencia de otros lenguajes que permiten definir variables basadas en tipos de datos
compuestos como son los registros, arrays

Para asignar valores a variables se utiliza la siguiente sintaxis:

SET nombre_variable1 = expresin1 [,nombre_variable2 = expresin2 ...]

En este caso y a diferencia de otros lenguajes es necesario especificar la sentencia SET


para asignar valores a las variables. Se puede en una sola instruccin realizar varias
asignaciones:

Procedimiento 4 asigna1

Pgina 11
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.4.2 Variables de usuario


Un tipo especial de variables son las variables de usuario ya que pueden ser
manipuladas dentro y fuera de los programas almacenados. Son una caracterstica de
MySQL desde la versin 3. Ejemplo de utilizacin:

Procedimiento 5 variables3

Puedes probar el procedimiento variables3 anterior desde el cliente de lnea de


comandos de MySQL. Accede a l desde dentro de la herramienta MySQL Query
Browser mediante el comando de men Tools / MySQL Command Line Client.

Este tipo de variables no necesitan declaracin y van precedidas del carcter @. Son de
un tipo de datos variant y pueden almacenar texto, fechas y nmeros. En las siguientes
lneas se crea una variable de usuario de nombre v1, se inicializa a 20 y a continuacin
se llama (CALL) al programa variables3 que lo que hace es doblar el valor de la
variable como se puede comprobar tanto en las lneas de procedimiento como en la
ventana de ejecucin desde la lnea de comandos de MySQL:

mysql> USE TEST


Database changed
mysql> SET @v1=20;
Query OK, 0 rows affected (0.00 sec)

mysql> CALL variables3();


Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @v1;


+------+
| @v1 |
+------+
| 40 |
+------+
1 row in set (0.00 sec)

Su alcance es de una sesin y por tanto son accesibles desde cualquier programa que se
ejecuta durante esa sesin, lo que las asemeja al concepto de variables globales como se
muestra en el siguiente ejemplo:

Procedimiento 7 variables5
Procedimiento 6 variables4

Pgina 12
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Probando los dos procedimientos anteriores:


mysql> USE TEST
Database changed
mysql> CALL variables4();
+------------------------+
| @v1 |
+------------------------+
| Raquel y Mario Carrera |
+------------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.02 sec)

2.4.3 Parmetros
Los parmetros son variables que se envan y reciben de los programas a los que se
llaman.

Se definen en la clusula CREATE de creacin de los procedimientos de la siguiente


forma:

CREATE PROCEDURE([[IN |OUT |INOUT] nombre_parmetro tipo de datos...])

Los tipos de parmetros, segn el modo en que se pasan al procedimiento llamado son:

1. IN: Opcin por defecto. En otros lenguajes representa el modo de paso de


parmetros por valor, es decir, el procedimiento almacenado trabaja con una
copia del parmetro que recibe y por tanto no modifica nada el valor del
parmetro que se le pasa al programa almacenado tal y como se puede ver en el
siguiente ejemplo y posterior ejecucin:

Procedimiento 8 parametro1

Ejecutando el procedimiento parametro1:


mysql> USE TEST
Database changed
mysql> SET @p1 =10;
Query OK, 0 rows affected (0.02 sec)

mysql> CALL parametro1 (@p1);


Query OK, 0 rows affected (0.02 sec)

mysql> SELECT @p1;


+------+
| @p1 |
+------+
| 10 |
+------+
1 row in set (0.00 sec)

Pgina 13
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2. OUT: Es una forma de paso de parmetros por variable, es decir, las


modificaciones del parmetro dentro del programa almacenado modifican
directamente el parmetro pasado como argumento. Hasta que no se le asigne un
valor determinado dentro del programa, su valor dentro de l ser nulo. Se
suelen utilizar como flags o indicadores de cmo ha ido la ejecucin de un
programa como veremos en el apartado de tratamiento de errores. Ejemplo:

Procedimiento 9 parametro2

Probando el procedimiento parametro2: Este procedimiento trabajar


directamente sobre la variable que se le pase como argumento.

mysql> USE TEST


Database changed

mysql> SET @p1=100;


Query OK, 0 rows affected (0.00 sec)

mysql> CALL parametro2(@p1);


+------+------+
| v_v1 | p_p1 |
+------+------+
| NULL | NULL |
+------+------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.05 sec)

mysql> SELECT @p1;


+------+
| @p1 |
+------+
| 28 |
+------+
1 row in set (0.00 sec)

3. INOUT: Otra forma de paso de parmetros por variable pero con la


caracterstica de que se le puede pasar un valor inicial que el programa llamado
tendr en cuenta (y no lo considerar NULL como en caso del parmetro OUT).
Ejemplo:

Pgina 14
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 10 parametro3
Ejecutando el procedimiento parametro3:

mysql> SET @p1=1000;


Query OK, 0 rows affected (0.00 sec)

mysql> CALL parametro3(@p1);


Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> SELECT @p1;


+------+
| @p1 |
+------+
| 6.01 |
+------+
1 row in set (0.00 sec)

2.4.4 Principales Operadores


Similares a los de otros lenguajes de programacin. Se utilizan en la mayora de los
casos junto con la sentencia SET para asignar valores a variables, formando parte de
expresiones de comparacin y en bucles.

OPERADORES MATEMTICOS
+ Suma
- Resta
* Multiplicacin
/ Divisin
DIV Divisin entera
% Resto

Procedimiento 11 operadores1

Pgina 15
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

OPERADORES DE COMPARACIN.
Comparan dos valores y devuelven como resultado CIERTO (1), FALSO (0) O
NULO (NULL))
OPERADOR SIGNIFICADO EJEMPLO Y RESULTADO
> Mayor que 3 > 2 Cierto
< Menor que 4 < 6 Cierto
<= Menor o igual que 4 <= 3 Falso
>= Mayor o igual que 4>=3 Cierto
= Igual a 4 = 4 Cierto
< > != Distinto de 4 <> 4 Falso
<=> Comparacin de valores nulos. NULL <=> NULL Cierto
Devuelve cierto si ambos valores son
nulos
BETWEEN Comprendido entre dos valores 45 BETWEEN 25 AND 50
Cierto
IS NULL Si valor nulo 3 IS NULL Falso
IS NOT NULL Si valor no nulo 3 IS NOT NULL Cierto
NOT No comprendido entre dos valores 45 NOT BETWEEN 25 AND
BETWEEN 50 Falso
IN Pertenencia al conjunto o lista 45 IN (44, 45, 46) Cierto
NOT IN No pertenencia al conjunto o lista 45 NOT IN (44, 45, 46)
Falso
LIKE Coincidencia con patrn de "ALBERTO CARRERA"
bsqueda LIKE "%CARRERA"
Cierto

TABLA DE VERDAD DEL OPERADOR LGICO AND


AND (&&) CIERTO FALSO NULL
CIERTO CIERTO FALSO NULL
FALSO FALSO FALSO NULL
NULL NULL NULL NULL

TABLA DE VERDAD DEL OPERADOR LGICO OR


OR (||) CIERTO FALSO NULL
CIERTO CIERTO CIERTO CIERTO
FALSO CIERTO FALSO NULL
NULL CIERTO NULL NULL

TABLA DE VERDAD DEL OPERADOR LGICO NOT


NOT (!) CIERTO / TRUE (1) FALSO / FALSE (0) NULL
FALSO (0) CIERTO (1) NULL

2.4.5 Expresiones
Se trata de una combinacin de literales, variables y operadores que se evalan para
devolver un valor.

Ver lneas 12 a 14 del procedimiento anterior operadores1.

Pgina 16
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.4.6 Funciones incorporadas


En los programas almacenados pueden seguirse utilizando la mayora de las funciones
incluidas en MySQL y que se utilizan para formar las sentencias SQL, excepto las que
trabajan con grupos de datos (clusula GROUP BY) puesto que las variables en los
programas almacenados son escalares y almacenan un solo valor. Por eso funciones
como SUM, COUNT, MIN, MAX y AVG pueden emplearse en programas
almacenados siempre y cuando devuelvan una fila y no varias (como consecuencia p.ej.
en este ltimo caso de utilizar la clusula GROUP BY).

A continuacin se detallarn las ms importantes. Para ms informacin consultar el


manual.
FUNCIONES MATEMTICAS
FUNCIN DEVUELVE EJEMPLO Y RESULTADO
ABS(num) Valor absoluto de num SELECT ABS(-3) 3
SIGN(num) -1, 0 o 1 en funcin del SELECT SIGN(2), SIGN(-2), SIGN(0)
valor de num 1, -1, 0
MOD(num1, Resto de la divisin de SELECT MOD (5,2) 1
num2) num1 por num2
FLOOR(num) Mayor valor entero SELECT FLOOR(23.9) 23
inferior a num
CEILING(num) Menor valor entero SELECT CEILING(23.9) 24
superior a num
ROUND(num) Redondeo entero ms SELECT ROUND(23.5),
prximo ROUND(23.4); 24 23
ROUND(num,d) Redondeo a d SELECT ROUND(23.545,2),
decimales ms prximo ROUND(23.44,1) 23,55 23,4
TRUNCATE Num truncado a d SELECT TRUNCATE (22.89, 1),
(num, d) decimales TRUNCATE (15326,-3) 22,8 5000
POW(num1, Num1 elevado a la SELECT POW(2,5) 32
num2) num2 potencia
SQRT (num) Raz cuadrada de num SELECT SQRT(36) 6

FUNCIONES DE CADENA
FUNCIN DEVUELVE EJEMPLO Y
RESULTADO
LIKE(plantilla) Resultado de comparar una cadena SELECT 'ALBERTO' LIKE
con una plantilla 'ALBER%' 1 (cierto)
NOT LIKE Lo contrario a la fila anterior SELECT 'ALBERTO' NOT
(plantilla) LIKE 'ABIERTO' 1
_ (subrayado) Se trata de un comodn que SELECT 'ALBERTO' LIKE
reemplaza un carcter en una 'ALBERT_' 1
cadena
% Como el caso anterior pero para SELECT 'ALBERTO' LIKE
uno o ms caracteres 'ALBER%' 1 (cierto)
\ Como en otros lenguajes se trata del SELECT '30%' LIKE '30\%'
carcter de escape, si precede al 1
comodn elimina su funcin y lo
trata como un carcter ms

Pgina 17
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

FUNCIONES DE CADENA (Continuacin)


BINARY Por defecto en las comparaciones SELECT 'ALBERTO' LIKE
entre cadenas no se distingue BINARY 'Alberto' 0
maysculas de minsculas salvo (falso)
que se indique esta opcin
STRCMP(cad1, -1 si cad1 < cad2, 0 si cad1=cad2 o SELECT
cad2) 1 si cad1 > cad2 STRCMP('ALBERTO',
'ABIERTO') 1
UPPER(cad) La cadena cad en maysculas SELECT UPPER('Alberto')
ALBERTO
LOWER(cad) La cadena cad en minsculas SELECT LOWER('Alberto')
alberto

FUNCIONES DE FECHA
FUNCIN DEVUELVE EJEMPLO Y RESULTADO
NOW() Fecha y hora segn el SELECT NOW() 2006-08-01
formato aaaa-mm-dd 00:40:25
hh:mm:ss
DAYOFWEEK(fecha) Cifra que representa el SELECT DAYOFWEEK('1966-
da de la semana (1 11-03') 5 |
domingo, 2 lunes)
WEEKDAY(fecha) dem de DAYOFWEEK SELECT WEEKDAY('1966-11-
pero con otros valores: 0 03') 3
lunes, 1 martes
DAYOFMONTH(fecha) Da del mes (entre 1 y SELECT
31) DAYOFMONTH('1966-11-03')
3
DAYOFYEAR(fecha) Da del ao (entre 1 y SELECT DAYOFYEAR('1966-
366) 11-03') 307
MONTH(fecha); Mes del ao (entre 1 y SELECT MONTH('1966-11-03')
12) 11
DAYNAME(fecha) Nombre del da de la SELECT DAYNAME('1966-11-
fecha 03') Thursday
MONTHNAME(fecha) Nombre del mes SELECT MONTHNAME('1966-
11-03') November
QUARTER(fecha) Trimestre del ao (entre SELECT QUARTER('1966-11-
1 y 4) 03') 4
WEEK(fecha [,inicio]) Semana del ao (entre 1 SELECT WEEK('2006-12-20',1)
51
y 52). Inicio especifica el
comienzo de la semana.
Si no se especifica vale 0
(domingo). Para empezar
el lunes utilizar el 1
YEAR(fecha) Ao (entre 1000 y 9999) SELECT YEAR('2006-12-20')
2006
HOUR(fecha) La hora SELECT HOUR(NOW()) 1
MINUTE(fecha) Los minutos SELECT MINUTE(NOW()) 5
SECOND(fecha) Los segundos SELECT SECOND(NOW())
58

Pgina 18
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

FUNCIONES DE FECHA (Continuacin)


TO_DAYS(fecha) Nmero de das SELECT TO_DAYS('2006-08-
transcurridos desde el 01') 732889
ao 0 hasta la fecha
DATE_ADD(fecha, La fecha sumado el SELECT DATE_ADD('2006-08-
INTERVAL valor tipo intervalo especificado 01', INTERVAL 1 MONTH)
de intervalo 2006-09-01
DATEDIFF(fecha1, El nmero de das SELECT DATEDIFF('2006-08-
fecha2) transcurridos entre 01', '2006-07-26') 6
fecha1 y fecha2
CURDATE() Fecha actual segn el SELECT CURRENT_DATE()
CURRENT_DATE() formato aaaa-mm-dd 2006-08-01
CURTIME() Fecha actual segn el SELECT CURRENT_TIME()
CURRENT_TIME() formato hh:mm:ss 01:12:43
DATE_FORMAT (fecha, Devuelve la fecha en el SELECT
formato) formato especificado. DATE_FORMAT(NOW(), 'Hoy
Consultar el manual para es %d de %M de %Y') Hoy es
las posibilidades de la 01 de August de 2006
opcin formato.

FUNCIONES DE CONTROL
FUNCIN DESCRIPCIN EJEMPLO Y
RESULTADO
IF(expr1, expr2, expr3) Si la expresin expr1 es cierta, SET @A=20; SET
devuelve expr2, sino expr3 @B=15;
SELECT IF(@A<@B,
@A+@B, @A - @B);
5
IFNULL(expr1, expr2) Si la expresin expr1 es NULL SET @A=20;
devuelve expr2, sino expr1 SELECT IFNULL(@A,
0); 20
NULLIF(expr1, expr2) Si la expresin expr1 es igual a SET @A=20; SET
expr2, devuelve NULL sino @B=15;
expr1
SELECT NULLIF(@B,
@A); 15
CASE valor WHEN Compara el valor con cada una de SELECT CASE
comp1 THEN res1 las expresiones comp. Si se WEEKDAY(NOW())
[WHEN comp2 THEN verifica la igualdad entonces WHEN 5 THEN 'Fin de
res2] [ELSE reselse] devuelve el valor res asociado, en semana'
END cualquier otro caso devuelve WHEN 6 THEN 'Fin de
reselse semana'
ELSE 'No es fin de
semana'
END;

Pgina 19
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

FUNCIONES DE AGREGACIN
FUNCIN DEVUELVE EJEMPLO Y
RESULTADO
AVG(columna) Media de los valores de la columna SELECT AVG(salario)
especificada FROM empleados
302.9412
COUNT Nmero de valores no nulos de la SELECT
(columna | *) columna (si esta se especifica como COUNT(comision)
argumento). Utilizando el carcter * FROM empleados 14
devuelve el nmero total de valores SELECT COUNT(*)
incluyendo los nulos FROM empleados 34
(luego 20 trabajadores no
tienen comisin)
MIN(columna) Valor mnimo de la columna SELECT MIN(salario)
FROM empleados 100
MAX(columna) Valor mximo de la columna SELECT MAX(salario)
FROM empleados 720
SUM(columna) Suma de valores contenidos en la SELECT SUM(salario)
columna FROM empleados
10300

OTRAS FUNCIONES
FUNCIN DESCRIPCIN EJEMPLO Y
RESULTADO
CAST (expresin AS Convierte la expresin al tipo SELECT
tipo) indicado CONVERT(20060802,
CONVERT DATE) 2006-08-02
(expresin, tipo)
LAST_INSERT_ID() Devuelve el valor creado por SELECT
una columna de tipo LAST_INSERT_ID() 0
AUTO_INCREMENT en la
ltima insercin
VERSION() Devuelve la versin del servidor SELECT VERSION()
MySQL 5.0.20a-nt
CONNECTION_ID() Devuelve el identificador de SELECT
conexin CONNECTION_ID() 4
DATABASE() Devuelve la base de datos actual SELECT DATABASE()
test
USER() Devuelve el usuario actual SELECT USER()
root@localhost

2.4.7 Bloques de instrucciones


Hasta ahora hemos estado trabajando con procedimientos con un solo bloque de
instrucciones, comenzando con la sentencia BEGIN y terminando con la sentencia
END:

Pgina 20
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

CREATE {PROCEDURE | FUNCTION |TRIGGER} nombre_del_programa


BEGIN
Instrucciones
END;

Este es el caso ms sencillo pues muchos programas almacenados MySQL contienen


varios bloques agrupando un conjunto de instrucciones y comenzando cada uno de ellos
con la instruccin BEGIN y finalizando con la END. Con la estructura de bloques se
consigue reunir las instrucciones en agrupaciones lgicas que realizan una determinada
funcin, como por ejemplo los bloques de los manejadores de errores que se tratan en
este curso. Tambin se consigue delimitar el mbito de las variables, declarndolas y
definindolas dentro de un bloque interno, de este modo no son visibles fuera de l. En
cambio una variable externa al bloque interno ser accesible tambin desde dentro de
este ltimo. En el caso en que la variable externa al bloque y la interna tuvieran el
mismo nombre, dentro del bloque interno se estar referenciando a la variable interna.

Un bloque no solo agrupa instrucciones sino tambin otros elementos que se tratan en
este curso como:
1. Declaracin de variables y condiciones.
2. Declaracin de cursores.
3. Declaracin de manejadores de error.
4. Cdigo de programa.

En el momento de declararse, para evitar mensajes de error, debe seguirse el orden


anterior, comenzando en primer lugar por la declaracin de variables.

Como ocurre con otros lenguajes de programacin, los bloques pueden etiquetarse. Esta
forma de actuar garantiza una fcil lectura del procedimiento y por tanto de su
mantenimiento y permite abandonar el bloque antes de que este concluya si as fuera
necesario (sentencia LEAVE). Sintaxis:

[etiqueta:] BEGIN
Declaracin de variables y condiciones.
Declaracin de cursores.
Declaracin de manejadores de error.
Cdigo de programa.
END [etiqueta];

Ejemplos de aplicacin:

Procedimiento 12 bloque1

Pgina 21
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 13 bloque2

Procedimiento 14 bloque3

Procedimiento 15 bloque4

2.4.8 El comando IF
Igual en su funcionamiento que otros lenguajes de programacin. Ejecuta la accin cuya
expresin (condicin) es cierta (de no ser cierta ninguna entonces ejecuta la accin
asociada a la sentencia ELSE).

Sintaxis:

IF condicin1 THEN instrucciones


[ELSEIF condicin2 THEN instrucciones ....]
[ELSE instrucciones]
END IF;

Pgina 22
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ejemplo:

Procedimiento 16 condicional1
El valor inicial (lnea 6) de la variable v_edad conduce a que la instruccin que se
ejecute sea la de la lnea 12 al no cumplir ninguna de las dos condiciones anteriores.

La forma de actuar es la siguiente: Si la expresin de la lnea 7 es cierta, se ejecutar la


instruccin de la lnea 8, sino si la expresin de la lnea 9 es cierta entonces se ejecutar
la instruccin de la lnea 10; si no son ciertas ninguna de las dos expresiones entonces
se ejecutar la instruccin de la lnea 12.

El ejemplo que viene a continuacin nos sirve para ver una versin ms abreviada de
la sentencia condicional que la del ejemplo anterior as como para sealar el
comportamiento de la sentencia condicional ante valores nulos.

Procedimiento 17 condicional2

En este caso se ejecutar la instruccin 10 puesto que la edad contiene un valor nulo que
no hace cierta la expresin de la lnea 7. Hay que tener cuidado pues se est asignando
el calificativo de adulto a una persona de la que se desconoce su edad. Para estos
casos puede utilizarse la forma ms simple de la sentencia condicional:

Pgina 23
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 18 condicional3

2.4.9 El comando CASE


Similar a la sentencia condicional IF anterior. Todo lo que se puede expresar con la
sentencia IF se puede expresar con la sentencia CASE. Esta ltima se suele utilizar
cuando el nmero de expresiones o condiciones a evaluar es elevado y por tanto la
lectura del cdigo se hace ms fcil y agradable.
Existen 2 formas posibles si lo que se evala es una expresin o una condicin:

CASE expresin
WHEN valor1 THEN
instrucciones
[WHEN valor2 THEN
instrucciones ...]
[ELSE
instrucciones]
END CASE;

En el ejemplo siguiente, tras evaluar la expresin de la lnea 8 se ejecutar la instruccin


de la lnea 10. El comportamiento ante una expresin nula es exactamente el mismo que
para la sentencia IF. Si el valor en la lnea 7 de la variable fuera NULL, el resultado del
procedimiento sera el de la lnea 14 siendo que no se corresponde por tanto con la
opcin de pago elegida.

Procedimiento 19 condicional4

Pgina 24
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

La otra sintaxis es similar a la anterior pero con condiciones en lugar de expresiones:

CASE
WHEN condicin1 THEN
instrucciones
[WHEN condicin2 THEN
instrucciones ...]
[ELSE
instrucciones]
END CASE;

Ejemplo:

Procedimiento 20 condicional5

En el ejemplo anterior se ejecutara la instruccin 8.


Slo se ejecuta la instruccin o instrucciones asociadas a una expresin o condicin (no
hace falta por tanto una instruccin de salida o de ruptura como ocurre en otros
lenguajes). Una vez que se ejecuta se finaliza la instruccin CASE y la ejecucin del
programa continua por la lnea siguiente.

2.4.10 El comando LOOP, LEAVE e ITERATE


Sintaxis

[etiqueta:] LOOP
instrucciones
END LOOP [etiqueta];

Todas las instrucciones comprendidas entre las palabras reservadas LOOP Y END
LOOP (bucle), se ejecutan un nmero de veces hasta que la ejecucin del bucle se
encuentra con la instruccin:

LEAVE etiqueta

en ese momento se abandona el bucle.

Pgina 25
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

En el siguiente ejemplo la instruccin 10 se ejecutar 4 veces:

Procedimiento 21 bucle1

La sentencia:

ITERATE etiqueta

se utiliza para forzar que la ejecucin del bucle termine en el momento donde se
encuentra con esta instruccin y contine por el principio del bucle. De esta manera en
el ejemplo que viene a continuacin se ejecutar 3 veces la instruccin de la lnea 13
(para los valores de i del 1 al 4 excepto el 3)

Procedimiento 22 bucle2

2.4.11 El comando REPEAT UNTIL


Sintaxis:
[etiqueta:] REPEAT
instrucciones
UNTIL expresin
END REPEAT [etiqueta]

Pgina 26
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Las instrucciones se ejecutarn hasta que sea cierta la expresin. Por lo menos el
conjunto de instrucciones se ejecuta una vez pues la evaluacin de la expresin se hace
posterior a la ejecucin de las instrucciones. El mismo procedimiento bucle1 anterior
pero utilizando esta otra instruccin quedara:

Procedimiento 23 bucle3
Equivale a la sentencia iterativa vista anteriormente

etiqueta: LOOP
instrucciones
IF expresin THEN LEAVE etiqueta; END IF;
END LOOP etiqueta;

Podra utilizarse la sentencia ITERATE pero puede llevar a situaciones contradictorias


si llegara a ejecutar otra vez el conjunto de instrucciones aun habiendo hecha cierta la
expresin de finalizacin del bucle.

La sentencia LEAVE tambin puede utilizarse.

2.4.12 El comando WHILE


Sintaxis:

[etiqueta:] WHILE expresin DO


instrucciones
END WHILE [etiqueta]

Ejecuta el conjunto de instrucciones mientras sea cierta la expresin. Ejemplo:

Procedimiento 24 bucle4

Pgina 27
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Equivale a la instruccin iterativa anterior:

etiqueta: LOOP
IF !expresin THEN LEAVE etiqueta; END IF;
resto de instrucciones;
END LOOP etiqueta;

2.4.13 Bucles anidados


Consiste en utilizar comandos de repeticin dentro de otros. Ejemplo de utilizacin:

Procedimiento 25 bucle5
Al ejecutar el ejemplo anterior se visualizarn los valores i-j: 1-1, 1-2, 2-1, 2-2

Como se ha indicado en el apartado anterior 2.4.7 de bloques de instrucciones, es


importante etiquetar el comienzo y final del bucle no solo por la instruccin LEAVE o
ITERATE sino por claridad en el seguimiento del cdigo.

2.5 Procedimientos
2.5.1 Creacin de procedimientos. Diccionario de Datos
La sintaxis completa de creacin de procedimientos es:

CREATE PROCEDURE nombre_procedimiento ([parametro1[,...]])


[LANGUAGE SQL]
[ [NOT] DETERMINISTIC]
[ {CONTAINS SQL | MODIFIES SQL DATA | READS SQL DATA | NO SQL} ]
[SQL SECURITY {DEFINER | INVOKER} ]
[COMMENT comentario]
bloque_de_instrucciones_del_procedimiento

Pgina 28
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Para crear un procedimiento o funcin el usuario debe disponer del privilegio CREATE
ROUTINE y el de ALTER ROUTINE para modificarlo o borrarlo; este ltimo
privilegio se asigna automticamente al creador del procedimiento o funcin. Para poder
ejecutar un procedimiento o funcin debe disponerse del privilegio EXECUTE.

Aspectos a tener en cuenta:

1. El nombre del procedimiento cumple con las normas vistas en este captulo
referidas a nombrar variables. La lista de argumentos suministrados al
procedimiento tambin ha sido estudiada.
2. La clusula LANGUAGE SQL indica que el lenguaje utilizado en los
procedimientos cumple el estndar SQL:PSM. Es una clusula innecesaria en
este momento pues los procedimientos en MySQL slo soportan este estndar.
Si MySQL en un futuro aceptara la escritura de procedimientos almacenados en
otros lenguajes como C o Java entonces ya sera necesario indicar el lenguaje de
programacin utilizado en el procedimiento.
3. [NOT] DETERMINISTIC. Referido al comportamiento del procedimiento. Si
un procedimiento es DETERMINISTIC, siempre ante una misma entrada
devuelve una misma salida. Funciones numricas como el valor absoluto,
cuadrado, raz cuadrada son DETERMINISTIC pues siempre devuelven el
mismo resultado ante un mismo valor. Por otro lado, una funcin que devolviera
el nmero de das transcurridos desde 1900 hasta la fecha sera NOT
DETERMINISTIC pues cambia segn el da que se ejecuta. Por defecto la
opcin es NOT DETERMINISTIC. Al igual que la anterior se puede prescindir
de su utilizacin debido a que por el momento no es tenida en cuenta por el
servidor. Ms adelante podr ser utilizada para la optimizacin de consultas.
4. [{CONTAINS SQL | MODIFIES SQL DATA | READS SQL DATA | NO
SQL}] Indica el tipo de acceso que realizar el procedimiento a la base de
datos, si slo se va a leer datos se especificar la clusula READ SQL DATA, si
adems de ello los modifica entonces la clusula MODIFIES SQL DATA ser la
que se emplee. Si el procedimiento no realiza ningn tipo de acceso a la base de
datos puede utilizarse la clusula NO SQL. Por defecto se utiliza la opcin
CONTAINS SQL que indica que el procedimiento contiene consultas SQL.
Estos parmetros se utilizan para mejorar el rendimiento.
5. [SQL SECURITY {DEFINER | INVOKER}] Indica si el procedimiento
almacenado se ejecutar con los permisos del usuario que lo cre (DEFINER) o
con los permisos del usuario que llama al procedimiento (INVOKER) La opcin
por defecto es DEFINER. Hay que tener muy en cuenta que con la opcin
DEFINER un usuario que lance el procedimiento podr acceder a los datos
aunque no posea los privilegios sobre las tablas que almacenan dichos datos; se
trata de un mecanismo de acceso a los procedimientos sin dar directamente
acceso a los datos.
6. [COMMENT comentario] Comentario sobre el procedimiento que puede ayudar
al usuario a conocer/recordar el funcionamiento del procedimiento. Esta
informacin puede consultarse como se ha visto anteriormente en el diccionario
de datos. Puede prescindirse de dicha clusula y realizar la escritura de los
comentarios aclaratorios al principio del bloque de instrucciones del
procedimiento utilizando los caracteres /* */ o --

Pgina 29
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Para conocer toda la informacin sobre los procedimientos almacenados se proceder a


consultar el diccionario de datos de manera similar a cmo se ha hecho anteriormente
con otros objetos como las vistas. Se incluye tambin informacin para las funciones
que se tratan ms adelante.

Sintaxis:

SHOW {PROCEDURE | FUNCTION} STATUS [LIKE patrn]

Ejemplo:

Ilustracin 8. Informacin sobre los procedimientos creados

Utilizando un patrn de bsqueda:

Ilustracin 9. Informacin sobre determinados procedimientos

El comando:

SHOW CREATE [PROCEDURE | FUNCTION] nombre

permite ver informacin de los procedimientos y funciones as como las lneas de


cdigo (al igual que con vistas seleccionar columna Create Procedure botn derecho
del ratn Comando View Field in Popup Editor):

Pgina 30
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 10. Informacin sobre el cdigo de los procedimientos

La otra forma de ver toda la informacin de un procedimiento es consultando


directamente el DICCIONARIO DE DATOS.

Instruccin 1

La tabla (vista) ROUTINES aporta ms informacin que la suministrada por los


comandos SHOW PROCEDURE STATUS y SHOW CREATE.

2.5.2 Modificacin de procedimientos


Sintaxis:

ALTER PROCEDURE nombre_procedimiento


{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA}
| SQL SECURITY {DEFINER|INVOKER}
|COMMENT comentario

Se debe poseer el privilegio de ALTER ROUTINE para poder modificar


procedimientos y funciones. El uso de las distintas opciones se ha expuesto en el
apartado de creacin.

2.5.3 Borrado de procedimientos


Sintaxis:

DROP PROCEDURE [IF EXISTS] nombre_procedimiento

Al igual que para la modificacin, se debe poseer el privilegio de ALTER ROUTINE


para poder borrar procedimientos y funciones.

Pgina 31
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.5.4 Utilizacin de instrucciones DDL y DML en


procedimientos almacenados
Junto con la recuperacin de datos (se expone en el siguiente punto), se trata de una de
las ms importantes ventajas del empleo de programas almacenados (en este caso
procedimientos).

En el ejemplo siguiente, en las lneas 7 a 11 nos encontramos con instrucciones DDL


(lenguaje definicin de datos) cuya finalidad es la creacin de una tabla. Por otro lado,
la instruccin DML (lenguaje de manipulacin de datos) de la lnea 14 inserta un
alumno de cdigo i y nombre alumno i cada vez que se ejecuta dicha instruccin
dentro del bucle (5 veces en total).

Procedimiento 26 procedimiento1

Puede verse el resultado de la ejecucin del procedimiento y la tabla


creada con sus 5 filas refrescando la base de datos test de la pestaa
Schemata (clic con el botn derecho del ratn ms opcin Refresh) y
haciendo doble clic sobre el nombre de la tabla dos veces
consecutivas (ilustracin de la derecha)

Ilustracin 11. Tabla alumnos

2.5.5 Utilizacin de instrucciones de consulta en


procedimientos almacenados
A diferencia de otros gestores de bases de datos, en MySQL se puede (slo en
procedimientos) devolver como resultado de la ejecucin del procedimiento un conjunto
de filas. La funcionalidad de esta forma de recuperacin se asemeja a la que se puede
conseguir con el empleo de vistas.

Pgina 32
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

En el siguiente procedimiento se recupera el conjunto de filas de los departamentos que


pertenecen al centro que se le pasa como argumento:

Procedimiento 27 resultset1

Ejemplo de ejecucin:

mysql> CALL test.resultset1(20);


+-------+---------------------+----------+
| numde | nomde | presu |
+-------+---------------------+----------+
| 110 | DIRECCION COMERCIAL | 15000000 |
| 111 | SECTOR INDUSTRIAL | 11000000 |
| 112 | SECTOR SERVICIOS | 9000000 |
+-------+---------------------+----------+
3 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)

El inconveniente es que el programa que recibe el conjunto de filas resultantes no puede


ser un procedimiento MySQL sino otro programa escrito en otro lenguaje como Java o
PHP (ver anexo de este tema). Para salvar este problema y poder enviar a un
procedimiento MySQL un conjunto de registros se utilizan las tablas temporales:

Procedimiento 28 resultset2
El tiempo de vida de las tablas temporales es el tiempo de duracin de la sesin.

2.5.6 Almacenar en variables el valor de una fila de


una tabla
No slo las instrucciones SQL de definicin y manipulacin de datos (DDL y DML)
pueden intercalarse en los procedimientos almacenados, la recuperacin de los datos
almacenados en la base de datos permitir su posterior proceso o tratamiento.

El siguiente ejemplo almacena una fila entera de la tabla creada en el procedimiento


anterior (tabla alumnos) en las variables declaradas en las lneas 6 y 7, mostrando su
contenido en la lnea 12. En la lnea 8 se sealan las columnas a recuperar (en este caso

Pgina 33
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

las dos) y en la lnea 9 se indica donde (variables) se guardarn los valores de dichas
columnas segn el orden en que aparecen (el valor de la columna id se guardar en la
variable v_id, el de la columna alumno en la variable v_alumno).

Procedimiento 29 - procedimiento2

En el anterior ejemplo no ha habido ningn problema de ejecucin pues cada una de las
dos variables almacena slo un tem (el de la correspondiente columna), pues la
consulta slo devuelve una fila.

Ilustracin 12. Resultado de la ejecucin del procedimiento anterior

Pero Qu ocurrira si la consulta devolviera ms de una fila?


Puedes probarlo a partir del anterior procedimiento, creando otro denominado
procedimiento3: Opcin de men File / Save As, cambia procedimiento2 por
procedimiento3 en las filas 3 y 4, elimina la lnea 11 y coloca un punto y coma al
final de la lnea 10 (por lo que la consulta pasar a devolver las 5 filas de la tabla al no
existir clusula WHERE); a continuacin ejecuta el procedimiento3. Te encontrars en
la parte inferior de la pantalla de resultado con este error:

Ilustracin 13. Error: La consulta slo debera devolver una fila


recordndote que no se puede obtener como resultado de la ejecucin de una instruccin
SELECT INTO ms de una fila. Dicho error provoca que la ejecucin del
procedimiento finalice y no contine por lo que las siguientes instrucciones a la que
provoc el error (SELECT INTO) no se ejecutaran (en este caso solo una, la
instruccin que visualiza las dos variables).

Otra cuestin importante Qu ocurrira si no devolviera ningn valor la instruccin


SELECT como es el caso del procedimiento4 que viene a continuacin

Pgina 34
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 30 - "procedimiento4"

A diferencia de otros lenguajes de programacin de bases de datos no provocara


situacin de error severa similar a la anterior del procedimiento3. El procedimiento4
finalizara mostrando los contenidos de las variables v_id y v_alumno, nulo, puesto que
no existe el alumno de id 100 y por tanto la sentencia SQL no recupera ninguna fila.

Ilustracin 14. La consulta no devuelve filas de la tabla

2.5.7 Sentencias preparadas. SQL dinmico


De la misma manera que otros grandes gestores de bases de datos, MySQL soporta la
capacidad de preparar sentencias SQL para que estas pueden ser ejecutadas varias veces
de manera eficiente y segura debido a que disminuye considerablemente el tiempo de
anlisis y preparacin de la instruccin que va a ser ejecutada as como puede prevenir
el problema de seguridad del SQL inyectado mediante la utilizacin de los parmetros o
variables BIND (ver ejemplos en PHP en anexo). La sintaxis para crear una sentencia
preparada es:

PREPARE nombre_sentencia FROM texto_sql

donde el texto_sql contiene marcadores (carcter ?) para representar los valores que se
utilizarn en el momento de ejecutar la instruccin SQL.
La sentencia preparada anterior se ejecuta mediante:

EXECUTE nombre_sentencia [USING variable [,variable...]]

donde la clusula USING se encarga de suministrar los valores para los marcadores
especificados en la sentencia PREPARE. La forma de especificar estos valores es en
forma de variables de usuario (prefijo @ ).
La sentencia preparada se puede eliminar mediante:

DEALLOCATE PREPARE sentencia_preparada

Pgina 35
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ejemplo de sentencias preparadas

mysql> USE TEST


Database changed
mysql> PREPARE alumnos_insert_dinamic FROM "INSERT INTO alumnos
VALUES(?,?)";
Query OK, 0 rows affected (0.00 sec)
Statement prepared

mysql> SET @id='1000';


Query OK, 0 rows affected (0.00 sec)

mysql> SET @nombre='Alumno 1000';


Query OK, 0 rows affected (0.00 sec)

mysql> EXECUTE alumnos_insert_dinamic USING @id,@nombre;


Query OK, 1 row affected (0.02 sec)

mysql> SET @id='1001';


Query OK, 0 rows affected (0.00 sec)

mysql> SET @nombre='Alumno 1001';


Query OK, 0 rows affected (0.00 sec)

mysql> EXECUTE alumnos_insert_dinamic USING @id,@nombre;


Query OK, 1 row affected (0.02 sec)

mysql> DEALLOCATE PREPARE alumnos_insert_dinamic;


Query OK, 0 rows affected (0.00 sec)

Los procedimientos almacenados no necesitan el mecanismo de sentencias preparadas


puesto que las instrucciones que se contienen ya se encuentran listas y preparadas para
su ejecucin. De todas formas su utilizacin dentro de procedimientos puede tener
sentido si de desea ejecutar SQL dinmico dentro de ellos (no puede hacerse ni dentro
de funciones ni de triggers). Una instruccin SQL es dinmica si es construida en
tiempo de ejecucin a diferencia de las instrucciones estticas que se construyen
cuando se compila el procedimiento. El empleo por tanto de SQL dinmico tiene
sentido cuando no se conoce por completo la sentencia SQL en el momento de la
compilacin y necesita ser completada mediante algn dato procedente de una entrada
del usuario o de otra aplicacin.

Al siguiente procedimiento, que utiliza SQL dinmico, se le puede pasar cualquier


instruccin SQL como argumento:

Procedimiento 31 sql_dinamico1

Pgina 36
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

La ejecucin del procedimiento anterior eliminar los dos alumnos introducidos:

Ilustracin 15. Ejecutando el procedimiento sql_dinamico1


Nota: Cuidado con el ejemplo del procedimiento anterior, su finalidad es totalmente
ilustrativa pero no debe utilizarse y mucho menos ser llamado desde otras aplicaciones
PHP, ASP, Visual Basic pues garantiza al 100% que ocurra el grave problema del
SQL inyectado, permitiendo al que invoca el procedimiento cualquier operacin de
cualquier tipo sobre la base de datos.

El SQL dinmico no se usa muy a menudo y debe utilizarse slo en los casos en que sea
necesario debido a que es ms complejo y menos eficiente que el SQL esttico. Debe
emplearse para realizar tareas o implementar utilidades que no pueden realizarse de otra
manera. Un ejemplo muy caracterstico de su empleo por su grado de optimizacin y
rapidez es para encontrar filas a partir de mltiples criterios de bsqueda:

Procedimiento 32 sql_dinamico2
Para averiguar los empleados del departamento 121 contratados antes de la dcada de
los 60 cuyo salario sea superior a 300:

Ilustracin 16. Ejecucin del procedimiento sql_dinamico2

Pgina 37
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

o la misma consulta anterior pero sin importar la fecha de ingreso:

Ilustracin 17. Ejecucin del procedimiento sql_dinamico2


Observa cmo el procedimiento anterior espera tres parmetros, dos de tipo numrico y
otro de tipo fecha aunque alguno de ellos pueda ser nulo. La posibilidad de poder
inyectar una instruccin SQL utilizando los procedimientos de esta manera es
prcticamente nula.

2.6 Cursores
Es el instrumento que se utiliza cuando la sentencia SQL dentro del programa devuelve
ms de una fila como hemos visto en el apartado anterior.
De esta manera, un cursor es una zona de memoria que contiene un conjunto de filas
resultantes de una sentencia SQL con la ventaja de que podremos recorrer, visualizar y
manipular una a una cada una de esas filas.

Sintaxis:

DECLARE nombre_del_cursor CURSOR FOR sentencia_select;

Un aspecto muy importante a tener en cuenta es que los cursores se declaran en los
procedimientos u otros programas almacenados despus de la declaracin de variables,
no hacerlo as producir una situacin de error.

2.6.1 Sentencias utilizadas con cursores. Ejemplos


Veamos antes el siguiente ejemplo:

Procedimiento 33 cursor1

Pgina 38
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Las sentencias asociadas al cursor se describen a continuacin. Ms adelante


encontrars una explicacin grfica que te ayudar a comprender mejor el
funcionamiento de los cursores as como del procedimiento anterior:

Declaracin del cursor: En las lneas 8 a 11 se declara el cursor.

Apertura del cursor: Clusula OPEN (lnea 12). El cursor se inicializa


trayendo a memoria las filas (conjunto activo) que cumplen la condicin del
cursor, en este caso slo 2 filas por la clusula WHERE de la lnea 11. El
puntero del cursor se queda apuntando al comienzo del conjunto de filas
recuperadas.

Recuperacin de las filas del cursor. Clusula FETCH (lnea 14): Cada vez
que se ejecuta recupera la siguiente fila a la que apunta el cursor y avanza el
puntero una posicin. Cada fila recuperada la va almacenando en las variables
v_id y v_alumno.

Cierre del cursor: Clusula CLOSE (lnea 19). Desactiva el cursor liberando la
zona de memoria ocupada por el cursor.

Puedes ver una ejecucin ms clara del procedimiento anterior abriendo una ventana
cliente sin salir de la herramienta MySQL Query Browser mediante la opcin de men
Tools / MySQL Command Line Client:

mysql> call cursor1();


+------+----------+
| v_id | v_alumno |
+------+----------+
| 1 | alumno 1 |
+------+----------+
1 row in set (0.00 sec)

+------+----------+
| v_id | v_alumno |
+------+----------+
| 2 | alumno 2 |
+------+----------+
1 row in set (0.01 sec)
ERROR 1329 (02000): No data - zero rows fetched, selected, or
processed

Qu ha ocurrido realmente?

Pgina 39
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Si observas en el esquema anterior, el recorrido de un cursor se asemeja mucho al


recorrido de un fichero secuencial, producindose error cuando se intenta leer un
registro y previamente se ha alcanzado el final del fichero (ERROR 1329 (02000): No
data - zero rows fetched, selected, or processed)

La ejecucin, reflejada paso a paso en el esquema, es la siguiente:

Recuadro 1: Apertura del cursor (lnea 12 del procedimiento anterior). Se traen a


memoria las dos filas que cumplen la declaracin del cursor. El puntero (flecha) del
cursor apunta al comienzo de la primera fila.

Recuadro 2: Primera vez que se ejecuta en el bucle la sentencia FETCH (lnea 14). Se
recupera la siguiente fila apuntada por el cursor, en este caso la primera, almacenando el
valor de la primera columna (1) en la variable v_id y el valor de la segunda columna
(alumno1) en la variable v_alumno. El puntero del cursor avanza una posicin.
Antes de finalizar el bucle se muestran los valores de las variables (lnea 17)

Ilustracin 18. Visualizacin de la primera fila del cursor


Recuadro 3: Segunda vez que se ejecuta en el bucle la sentencia FETCH (lnea 14). Se
recupera la siguiente fila apuntada por el cursor, en este caso la segunda, almacenando
el valor de la primera columna (2) en la variable v_id y el valor de la segunda columna
(alumno2) en la variable v_alumno. El puntero del cursor avanza una posicin.
Antes de finalizar el bucle se muestran los valores de las variables (lnea 17):

Ilustracin 19. Visualizacin de la segunda fila del cursor

Recuadro 4: Tercera vez que se ejecuta en el bucle la sentencia FETCH (lnea 14). Se
intenta recuperar la siguiente fila apuntada por el cursor pero no hay ninguna fila que
recuperar pues el cursor ha finalizado, por tanto se produce la situacin de error:

Ilustracin 20. Error en el cursor


Para tratar este error, el intento de recuperar una fila habiendo llegado ya al final del
cursor, definiremos un manejador de error; los manejadores de error y el tratamiento de
errores se estudiarn en un apartado prximo pero ya se avanza una pequea parte del
mismo para poder trabajar este punto:

DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_ultima_fila=1;

Pgina 40
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 34 cursor2

Explicacin del procedimiento cursor2:

1. Con la instruccin de la lnea 13 estamos declarando un manejador de error para


el error NOT FOUND, que es el que se produce cuando se intenta recuperar una
fila habiendo llegado al final del cursor. Se utiliza una variable (v_ultima_fila)
que tomar el valor 1 en el momento que se produzca el error. Dentro de nuestro
programa podremos preguntar por su valor.

2. Cuando se produzca la situacin de error, la ejecucin del programa continuar


(debido a la clusula CONTINUE de la lnea 13) y no finalizar en el punto
donde se encuentra el error. En el momento en que la instruccin de la lnea 16
(FETCH INTO) falle por no poder leer ninguna fila del cursor, la variable
v_ultima_fila tomar el valor 1 y la ejecucin del procedimiento continuar por
la lnea siguiente al error (en este caso por la lnea 17) para abandonar el bucle.

Puedes ejecutar el programa desde la lnea de comandos y comprobars que ya no se


finaliza el programa tras el error como en el procedimiento cursor1, puesto que en el
momento en que se intente leer una fila del cursor posterior a la ltima, la condicin de
la lnea 17 ser cierta y por tanto la ejecucin del programa abandonar el bucle (lnea
18) y continuar por la lnea 22 (recuerda que si el error no se hubiera tratado el
programa hubiera finalizado y terminara en el mismo punto en que se produjera el
error).

Pgina 41
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 21. Ejecucin correcta del cursor

El mismo recorrido del cursor pero con la instruccin iterativa REPEAT UNTIL:

Procedimiento 35 cursor2 utilizando la instruccin REPEAT UNTIL

y con la instruccin WHILE:

Procedimiento 36 cursor2 utilizando la instruccin WHILE

Es muy habitual que las filas a recuperar del cursor dependan de un dato que se pasa
como argumento. El procedimiento siguiente es el mismo que el procedimiento cursor2
salvo que el nmero de filas a recuperar viene determinado por el parmetro p_id que se
le pasa al procedimiento (lnea 4) y por tanto tambin se modifica la lnea 12:

Pgina 42
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 37 cursor2_1
La llamada al procedimiento:

mysql> CALL cursor2_1(4);

producir como resultado la visualizacin de las 4 primeras filas de la tabla alumnos.

2.6.2 Cursores anidados


En algunas ocasiones, del mismo modo que ocurre con bloques de instrucciones, ser
necesario incluir un cursor dentro de otro cursor. P.ej un cursor que recorriera las filas
de todos los Centros y a su vez, por cada centro, otro cursor que recorriera todos los
departamentos del mismo:

Centro: 10 - SEDE CENTRAL


Departamento: DIRECCCION GENERAL
Departamento: ORGANIZACION
Departamento: PERSONAL
Departamento: PROCESO DE DATOS
Departamento: FINANZAS
+--------------------------------+
TOTAL DEPARTAMENTOS DEL CENTRO
+--------------------------------+
| 5 |
+--------------------------------+
Centro: 20 - RELACION CON CLIENTES
Departamento: DIRECCION COMERCIAL
Departamento: SECTOR INDUSTRIAL
Departamento: SECTOR SERVICIOS
+--------------------------------+
| TOTAL DEPARTAMENTOS DEL CENTRO
+--------------------------------+
| 3 |
+--------------------------------+

Pgina 43
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

El procedimiento quedara:

Procedimiento 38 cursor5
Es muy importante sealar en el procedimiento anterior y cuando se utilicen cursores
anidados que antes de leer una nueva fila del cursor externo, hay que resetear la variable
utilizada para controlar el final del cursor (lnea 38) para que el final del cursor interno
no provoque un final del cursor externo sin haber terminado este ltimo.

Antes de terminar este apartado indicar que existe un tipo especial de cursores de
actualizacin que se tratan en el tema siguiente de transacciones.

2.7 Manejo de errores


2.7.1 Introduccin a la gestin de errores
En general, si una sentencia SQL falla dentro de un programa almacenado se produce
una situacin de error, se interrumpe la ejecucin del programa en ese punto y finaliza
salvo en el caso de que el programa que falla hubiera sido llamado por otro; en ese caso
la ejecucin continua por el programa que llam a este programa que ha causado el

Pgina 44
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

error; ocurrira lo mismo si en lugar de un programa que es llamado desde otro


programa nos encontrramos con un bloque interno dentro de otro bloque ms externo;
si se produjera error en el bloque anidado interno, la ejecucin del programa se
interrumpira en el bloque interno para continuar por el externo.

Como hemos visto anteriormente, este comportamiento se puede controlar definiendo


los manejadores de error o handlers.

Una handler es un bloque de instrucciones SQL que se ejecuta cuando se verifica una
condicin tras una excepcin (error) generada por el servidor.

Sintaxis:

DECLARE {CONTINUE | EXIT | UNDO} HANDLER FOR


{SQLSTATE sqlstate_code | MySQL error code | nombre_condicin}
instrucciones_del_manejador

Deben declararse despus de las declaraciones de variables y cursores ya que


referencian a estos en su declaracin.

El manejador puede ser de tres tipos:

CONTINUE: La excepcin o error generado no interrumpe el cdigo del


procedimiento.
EXIT: Permite poner fin al bloque de instrucciones o programa en el que se
genera la excepcin.
UNDO: No est soportada por el momento.

La condicin de error del manejador puede expresarse tambin de 3 formas:

Con un cdigo estndar ANSI SQLSTATE.


Con un cdigo de error MySQL.
Una expresin.

El manejador de error indica mediante un conjunto de instrucciones lo que hay que


hacer en caso de que se produzca ese error.

Ejemplos de situaciones de error:

Procedimiento 39 error1

El anterior procedimiento recibe dos argumentos, identificador de alumno y nombre del


alumno y los inserta en la base de datos. Como modifica la base de datos se intercala la
clusula de la lnea 5.

Pgina 45
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Si hacemos una llamada al anterior procedimiento desde la ventana cliente o desde la


propia herramienta MySQL Query Browser:

Instruccin 2. Llamando al procedimiento error1

se puede comprobar viendo la tabla o realizando una consulta que aade al anterior
alumno.

Qu ocurre si intentamos insertar un alumno de clave primaria repetida?

CALL error1(6, 'Raquel M. Carrera');

Aparecer un error con un nmero de error (1062) y una descripcin (entrada


duplicada) advirtindonos de tal situacin; evidentemente no se realiza la insercin y la
ejecucin del programa se detiene y finaliza (pues no retorna a ningn otro programa ya
que no hay ningn otro que lo llam).

Tomando nota del nmero de error y modificando el anterior procedimiento para aadir
dicho control de error:

Procedimiento 40 error2

Una llamada al programa como:

CALL error2(6, 'Raquel M. Carrera');

Producir la siguiente salida:

Resaltar que el error ha sido tratado y por tanto no finaliza la ejecucin del programa en
el momento en que se produce y si hubiera ms lneas de cdigo detrs de la lnea 9,
stas se ejecutaran debido al tipo de manejador, CONTINUE; esto no ocurrira si el
error no fuera tratado como hemos comprobado en el procedimiento error1 anterior

Qu ocurre si intentamos introducir un alumno de clave primaria nula?

CALL error2(NULL, 'Mario A. Carrera');

Habr que considerar y tratar tambin el error 1048 que se produce:

Pgina 46
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 41 error3
Llegados a este punto podemos utilizar un tercer argumento en el procedimiento, un
parmetro de tipo OUT, para que acte como flag o indicador de cmo ha ido la
ejecucin del procedimiento:

Procedimiento 42 error4
Llamando al anterior procedimiento error4 desde el interior de otro (lnea 7 de error5):

Procedimiento 43 error5
La primera ejecucin del procedimiento: CALL error5( ) funcionar correctamente,
apareciendo como fila/columna resultante : Alumno dado de alta.
La segunda ejecucin del mismo procedimiento anterior tambin funcionar
correctamente pues la situacin de error de cdigo repetido es tratada y por tanto el
programa finaliza correctamente y visualiza por pantalla que el nmero de matrcula ya
existe.

Volviendo otra vez a la declaracin de un manejador, hemos visto que tiene 3 partes que
pasaremos a ver a continuacin ms en detalle:

1. Tipo de manejador: CONTINUE o EXIT.


2. Condicin del manejador: SQLSTATE sqlstate_code | MySQL error code |
nombre_condicin.
3. Acciones o instrucciones que realiza el manejador.

Pgina 47
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.7.2 Tipos de manejador


EXIT: El programa que causa el error finaliza, devolviendo el control al
programa que le llam. Si se produce en un bloque interno, entonces el control
de la ejecucin pasa al externo.
CONTINUE: La ejecucin contina por la siguiente lnea de cdigo que caus
el error.

En ambos casos el error es tratado por lo que la ejecucin del programa puede
considerarse correcta y antes de realizarse la opcin CONTINUE o EXIT se ejecuta el
conjunto de instrucciones asociados al manejador.

Ejemplo de utilizacin del manejador EXIT:

Procedimiento 44. error6


Observa como en este caso hay dos bloques de instrucciones BEGINEND. El
manejador de errores con la opcin EXIT est integrado con el bloque ms interno.

La llamada al procedimiento anterior:

CALL error6(8, 'Fernando Carrera');


Dar como resultado la insercin del alumno y su mensaje correspondiente de alumno
dado de alta.

CALL error6(8, 'Conchita Martn');

dar como resultado un aviso indicando que el nmero de matrcula 8 ya existe. La


instruccin de la lnea 11 falla (no se lleva a cabo por tanto la insercin) puesto que no
puede haber dos filas distintas con el mismo valor en el campo id (al ser clave primaria)
provocando el error de clave repetida que hace que se ejecute la instruccin asociada al
manejador (lnea 10). Como el tipo de manejador es de tipo EXIT, se interrumpe la
ejecucin del programa y se sale del bloque interno (si slo hubiera habido un bloque se
hubiera salido del programa); por tanto la instruccin de la lnea 12 no llega a ejecutarse
y la ejecucin del programa sigue por la lnea 15.

Ejemplo de utilizacin del manejador CONTINUE. Observa que a diferencia del


anterior caso, en este procedimiento no hay ms que un bloque:

Pgina 48
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Procedimiento 45. error7

CALL error7(9, 'Pablo Martnez') ;

dar como resultado la insercin del alumno Pablo y su mensaje correspondiente de


alumno dado de alta. Como no falla la instruccin la expresin de la lnea 11 es falsa y
se ejecutar la lnea 14.

CALL error7(9, 'Luis Hueso') ;

dar como resultado una aviso indicando que ese nmero de matrcula ya existe. Al
intentar hacer la insercin de la lnea 10 el procedimiento provoca una situacin de error
(igual que para el caso anterior de la clusula EXIT) que es tratada inmediatamente
despus en el manejador de error poniendo a 1 el valor de la variable v_clave_repetida;
por tanto, al ser tratado el error y de forma continua (clusula CONTINUE lnea 8) la
ejecucin del procedimiento continua por la lnea 11 haciendo cierta la condicin y
avisando de la duplicidad de dicha matrcula.

Qu tipo de manejador usar? EXIT o CONTINUE? Algunos lenguajes de


programacin de otros sistemas gestores de bases de datos incluyen por defecto slo la
opcin EXIT (de todas maneras en estos casos se puede simular una opcin
CONTINUE colocando la instruccin propensa a causar error dentro de un bloque
BEGINEND).
Si la lgica del programa obliga a abandonar la ejecucin del mismo si se produce un
error entonces se puede utilizar EXIT. En caso de que est contemplado en la lgica del
programa que se pueden ejecutar otras alternativas si se produce error podemos utilizar
la clusula CONTINUE.

2.7.3 La condicin del manejador


Has 3 formas posibles de indicar cuando debe ser invocado el conjunto de instrucciones
del manejador de error para tratarlo.

1) Nmero de error de MySQL.


2) Cdigo SQLSTATE estndar ANSI.
3) Condiciones de error con nombre.

Pgina 49
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

1) Nmero de error de MySQL, propio de MySQL, que dispone de un conjunto


particular de nmeros de error. Ej.:

DECLARE CONTINUE HANDLER FOR 1062


SET v_clave_repetida=1;

El nmero de error 1062 est asociado en MySQL al intento de almacenar un registro de


clave duplicada.

2) Cdigo SQLSTATE estndar ANSI: A diferencia del anterior, SQLSTATE no es


definido por MySQL sino que es un estndar y por tanto el mismo error tiene asociado
el mismo SQLSTATE independientemente del gestor de bases de datos utilizado:
MySQL, Oracle, DB2, Microsoft SQL Server. En los anteriores gestores de bases de
datos el SQLSTATE 23000 est asociado al error de clave duplicada. Por tanto en
MySQL el anterior manejador de error y el siguiente son similares:

DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'


SET v_clave_repetida=1;

Cul utilizar de los dos anteriores? En teora el segundo permitira una mayor
portabilidad a otros gestores de bases de datos pero en la prctica los lenguajes de
programacin para Oracle y Microsoft SQL Server son incompatibles con el de MySQL
por lo que no hace muy atractivo el uso de esta segunda opcin (DB2 de IBM es algo
compatible pues tanto DB2 como MySQL estn basados en el estndar SQL:2003).
Adems hay cdigos SQLSTATE que no corresponden con un cdigo de error de
MySQL sino a varios (para estos casos se utiliza el SQLSTATE genrico HY000)

Por lo anteriormente expuesto seguiremos utilizando en este curso los cdigos propios
de error de MySQL.

Para saber el cdigo de un error, se puede averiguar de varias formas:


1. Como en otros lenguajes, provocarlo para obtener su nmero de error que luego
utilizaremos en el manejador del error. Ej.: Hemos visto en pginas anteriores de
este curso el error que se produce cuando se intenta leer una fila de un cursor y
ya existen ms:

El 1329 indica el nmero de error de MySQL y entre parntesis, en este caso


02000, el nmero SQLSTATE correspondiente.

2. Mirando el manual, suele venir una tabla detallada de errores con su nmero y
descripcin en un apndice dedicado.

3) Condiciones de error con nombre

3.1) Predefinidas de MySQL

SQLEXCEPTION
SQLWARNING
NOT FOUND

Pgina 50
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ejemplos:

/* Si ocurre cualquier condicin de error (salvo la excepcin NOT FOUND tratada en el


apartado de cursores) la variable v_error valdr 1 y continuar la ejecucin del
programa */

DECLARE CONTINUE HANDLER FOR SQLEXCEPTION


SET v_error=1;

/* Si ocurre cualquier condicin de error (excepto la condicin NOT FOUND) el


procedimiento finaliza ejecutando antes la instruccin ROLLBACK (deshacer
operaciones que se hubieran realizado) y otra de advertencia de la situacin de error */

DECLARE EXIT HANDLER FOR SQLEXCEPTION


BEGIN
ROLLBACK;
SELECT 'Ocurri un error. Procedimiento terminado';
END;

/* Si la instruccin FETCH en un cursor no recupera ninguna fila*/


/* De 3 formas distintas: Condicin de error con nombre, SQLSTATE y cdigo de error
de MySQL /*

DECLARE CONTINUE HANDLER FOR NOT FOUND


SET v_ultima_fila=1;

DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'


SET v_ultima_fila=1;

DECLARE CONTINUE HANDLER FOR 1329


SET v_ultima_fila=1;

SQLWARNING es un subconjunto de los cdigos SQLSTATE, representando


todos los cdigos que empiezan por 01.
NOT FOUND es un subconjunto de los cdigos SQLSTATE, representando
todos los cdigos que comienzan por 02.
SQLEXCEPTION es un subconjunto de los cdigos SQLSTATE, representando
todos los cdigos que no comienzan ni por 01 ni por 02.

3.2) Definidos por el usuario

Facilitan lectura del cdigo y por tanto el fcil mantenimiento de la aplicacin.

Consiste en bautizar o darle un nombre a un cdigo de error MySQL o SQLSTATE.

Si intentas ejecutar el siguiente cdigo que forma parte de un procedimiento:

Pgina 51
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Bloque 1

Vers que produce una situacin de error pues la tabla ya existe:

En la imagen siguiente, al mismo tiempo que tratamos el error MySQL anterior le


asociamos un nombre (lnea 6) para facilitar su comprensin a la hora de realizar tareas
como la de mantenimiento de la aplicacin como se ha sealado. De esta manera ya se
puede realizar la declaracin del manejador de error de la lnea 7.

Bloque 2

2.7.4 Orden de actuacin del manejador


Ante varias posibilidades de ejecutarse un manejador Quin se ejecuta?
Siempre el manejador asociado al error MySQL en primer lugar (en este caso lneas 11-
12 del bloque 3 siguiente). Si ste no estuviera declarado y definido entonces se
ejecutara el manejador asociado al cdigo SQLSTATE. En ltimo lugar el manejador
asociado al manejador SQLEXCEPTION.

Bloque 3

El orden de actuacin de los manejadores puede facilitarnos una forma de trabajo en


nuestros programas. Los errores de cdigo MySQL ms habituales podrn ser tratados
en manejadores especficos y aqullos que no sean considerados podrn ser atrapados en
un manejador SQLEXCEPTION (equivaldra a la parte ELSE de una sentencia CASE
que atrapara todo lo que no ha sido filtrado).

En el siguiente ejemplo bloque 4 se declara un manejador para el tratar la excepcin de


clave duplicada (lneas 9 a 10), lo que ocurre es que el error producido es el cdigo de

Pgina 52
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

error MySQL 1048 (el primer campo de la tabla no puede ser nulo) y no el 1062 como
consecuencia de la lnea 13.

En esta situacin se ejecuta el manejador SQLEXCEPTION (lneas 11-12)

Bloque 4
Se podra hacer uso de una funcin del tipo err_code( ) que indique el cdigo de error
que se ha producido o una variable que almacenara el cdigo de error y su mensaje. Esta
funcin est incluida en los lenguajes de otros gestores de bases de datos y en otros (p.ej
PHP) pero todava no en MySQL (se espera para la versin 5.2, la especificacin
SQL:2003 si que lo incluye). Tambin se echa en falta por el momento (aparecer en la
versin 5.2) la posibilidad de que el usuario provoque intencionadamente situaciones de
error (sentencias del tipo SIGNAL, RAISE) y poder atenderlas dentro de manejadores
de error propios.

2.7.5 mbito de actuacin del manejador


Como hemos visto los manejadores actan en todos aquellos bloques donde se han
declarado; su alcance llega tambin a los bloques anidados.
Ej.:

Bloque 5

Aunque la excepcin se produce en el bloque interno (lneas 14 a 16), ser atrapada por
el manejador declarado en un nivel superior (bloque externo):

Si se atrapa en un bloque interno, ya no se propaga a la del bloque superior externo:

Pgina 53
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Bloque 6

El comportamiento de los manejadores de error entre bloques internos y externos es


similar al comportamiento de un procedimiento que llama a otro (el primer
procedimiento actuara como externo y el llamado como interno)

2.7.6 Ejemplo de tratamiento de errores


El siguiente ejemplo resume todo lo tratado en este apartado 2.7. No se incluye el
manejador de errores NOT FOUND pues en el procedimiento no se utilizan cursores.

Procedimiento 46 error8

Pgina 54
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 22. Probando el procedimiento 44 anterior

2.8 Funciones
2.8.1 Creacin de funciones. Diccionario de Datos
La sintaxis completa de creacin de funciones es:

CREATE FUNCTION nombre_funcin ([parametro1[,...]])


RETURNS tipo_de_datos
[LANGUAGE SQL]
[ [NOT] DETERMINISTIC]
[ {CONTAINS SQL | MODIFIES SQL DATA | READS SQL DATA | NO SQL} ]
[SQL SECURITY {DEFINER | INVOKER} ]
[COMMENT comentario]
bloque_de_instrucciones_de_la_funcin

Pgina 55
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Como se ha visto en el tema de procedimientos, las funciones almacenadas son


similares a los procedimientos salvo que slo devuelven un valor. Por tanto gran parte
de lo que se ha expuesto en el apartado de procedimientos es vlido aqu. Los permisos
que hay que poseer para crear funciones as como la informacin que proporciona el
diccionario de datos se han expuesto en el apartado de procedimientos.

La devolucin del valor se produce utilizando la clusula RETURN y no mediante


variables OUT o INOUT.

La ventaja que presentan las funciones es que pueden ser utilizadas dentro de
instrucciones SQL y por tanto aumentan considerablemente las capacidades de este
lenguaje.

La utilizacin de funciones posibilita realizar una cdigo ms fcil de leer y mantener,


pues agrupa en una funcin un conjunto de operaciones relacionadas lgicamente entre
s (frmulas, reglas de negocio). Adems consultas SQL complejas pueden abreviarse
con el empleo de funciones.

La mayora de las opciones de la sintaxis anterior son idnticas a las vistas en el tema de
procedimientos. Adems de ello:

- La clusula RETURN es obligatoria e indica el tipo de dato que devuelve la


funcin.
- No aparece en la sintaxis, pues no se puede utilizar, los parmetros IN, OUT,
INOUT. Todos los parmetros pasados a la funcin se definen implcitamente
del tipo IN.
- Dentro del cuerpo de la funcin debe aparecer por lo menos una instruccin
RETURN, que devuelve el resultado de la funcin al programa llamado y
finaliza su ejecucin. Si la ejecucin de la funcin llegara al final de la misma
sin haber encontrado una instruccin RETURN, causara un error. El
seguimiento y mantenimiento de la funcin ser mucho ms simple si slo
aparece una sentencia RETURN dentro de la funcin; en ese caso habr que
utilizar una variable para conseguirlo.

2.8.2 Ejemplos de utilizacin de funciones


La funcin funcion1 devuelve en euros las pesetas que se le pasan como argumento.

Funcin 1 "funcion1"

A continuacin se indica cmo llamar a la funcin anterior creada en la base de datos


test:

Pgina 56
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

mysql> SELECT test.funcion1(10000);


+----------------------+
| test.funcion1(10000) |
+----------------------+
| 60.10 |
+----------------------+
1 row in set, 1 warning (0.00 sec)

dem de antes pero dejando el resultado de la funcin en una variable:

mysql> SET @v_euros=test.funcion1(10000);


Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> SELECT @v_euros;


+----------+
| @v_euros |
+----------+
| 60.10 |
+----------+
1 row in set (0.00 sec)

En el siguiente ejemplo la funcin realiza la conversin de pesetas a euros y viceversa:


Si el primer argumento es un 1 entonces convertir a euros, si es un 2 convertir a
pesetas:

Funcin 2 "funcion2"

Ejemplo de ejecucin de la funcin anterior;

mysql> SELECT test.funcion2(2, 6);


+---------------------+
| test.funcion2(2, 6) |
+---------------------+
| 998.32 |
+---------------------+
1 row in set, 1 warning (0.00 sec)

La funcin 2 puede mejorarse dejando un solo RETURN (se ejecutar siempre al ser la
ltima instruccin de la funcin) y controlando que los dos nicos parmetros que se le
pasen son 1 o 2 (si no es as la funcin devolver NULL):

Pgina 57
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Funcin 3 "funcion3"
Llamando a la funcin anterior desde dentro de un procedimiento:

Procedimiento 47 f1
Si en lugar del cuerpo de un procedimiento se utiliza una instruccin SELECT para
llamar a la funcin:

Instruccin 3
La anterior sentencia se puede simplificar a partir de la siguiente funcin:

Funcin 4 "funcion4"

Pgina 58
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Por lo que quedara reducida a:

Instruccin 4

Si se desea obtener el nmero de empleados de un departamento que se le pasa como


argumento:

Funcin 5 "funcion5"

La funcin 5 anterior ms simplificada:

Funcin 6 "funcion6"

Para obtener el nombre de un alumno cuyo nmero de identificacin se pasa como


parmetro (devuelve nulo si este no existe) puede emplearse la siguiente funcin:

Funcin 7 "funcion7"

Pgina 59
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.8.3 Modificacin de funciones


Sintaxis:

ALTER FUNCTION nombre_funcin


{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA}
| SQL SECURITY {DEFINER|INVOKER}
|COMMENT comentario

Se debe poseer el privilegio de ALTER ROUTINE para poder modificar


procedimientos y funciones. El uso de las distintas opciones se ha expuesto en el
apartado de creacin de procedimientos.

2.8.4 Borrado de funciones


Sintaxis:

DROP FUNCTION [IF EXISTS] nombre_funcin

Al igual que ocurre para la modificacin, se debe poseer el privilegio de ALTER


ROUTINE para poder borrar procedimientos y funciones.

2.9 Triggers
2.9.1 Creacin de triggers. Diccionario de Datos
Los triggers, tambin llamados desencadenadores o disparadores, son programas
almacenados que se ejecutan (disparan) automticamente en respuesta a algn suceso
que ocurre en la base de datos. En MySQL ese tipo de suceso se corresponde con alguna
instruccin DML (INSERT, UPDATE, DELETE) sobre alguna tabla.

Suponen un mecanismo para asegurar la intregridad de los datos. Se emplean tambin


como un mtodo para realizar operaciones de auditora sobre la base de datos. No hay
que abusar de su utilizacin pues ello puede traer consigo una sobrecarga del sistema y
por tanto un bajo rendimiento del mismo. Al final de este apartado vers ejemplos de su
utilizacin que ayudarn a comprender su utilizacin.
Sintaxis:

CREATE [DEFINER={cuenta_usuario | CURRENT_USER}] TRIGGER


nombre_trigger
{BEFORE | AFTER}
{UPDATE | INSERT | DELETE}
ON tabla
FOR EACH ROW
cuerpo_del_trigger

Pgina 60
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Para poder crear triggers, en versiones anteriores a la 5.16 se necesita el privilegio


SUPER. A partir de esta el privilegio es el de TRIGGER. No se pueden crear ni sobre
una tabla temporal ni sobre una vista, solamente sobre tablas.

Aspectos a comentar de la sintaxis anterior:

- DEFINER ={cuenta_usuario | CURRENT_USER } indica con qu privilegios se


ejecutan las instrucciones del trigger. La opcin por defecto es CURRENT_USER, que
indica que las instrucciones se ejecutan con los privilegios del usuario que lanz la
instruccin de creacin del trigger. La opcin cuenta_usuario por otro lado hace que el
trigger se ejecute con los privilegios de dicha cuenta.

- Nombre del trigger. Sigue las mismas normas que para nombrar cualquier objeto de la
base de datos.

- BEFORE | AFTER. Seala cuando se ejecuta el trigger, antes (before) o despus


(alter) de la instruccin DML que lo provoc.

- UPDATE | INSERT | DELETE. Define la operacin DML asociada al trigger.

- ON tabla. Define la tabla base asociada al trigger.

- FOR EACH ROW. Indica que el trigger se ejecutar por cada fila de la tabla afectada
por la operacin DML. Esto es, si tenemos asociado un trigger a la operacin de borrado
de una tabla y se eliminan con una sola instruccin 6 filas de sta ltima, el trigger se
ejecutar 6 veces, una por cada fila eliminada. Otros gestores de bases de datos (as
como futuras implementaciones de MySQL) consideran tambin el otro estndar de
ANSI, la clusula FOR EACH STATEMENT. Con esta segunda opcin, el trigger se
ejecutara por cada operacin DML realizada; en el ejemplo anterior, la instruccin de
borrado dara lugar a que slo se ejecutara el trigger una sola vez en lugar de 6 (filas
afectadas) con esta futura clusula.

- Cuerpo del trigger: El conjunto de instrucciones que forman este programa


almacenado. Si son ms de una irn en un bloque BEGIN END. No pueden contener
una instruccin CALL de llamada a un procedimiento almacenado.

Referencias a las columnas afectadas

Dentro del cuerpo del trigger se puede hacer referencia a los valores de las columnas
que estn siendo modificadas con una sentencia DML (la que provoc que se disparara
el trigger), incluso pueden cambiarse si as se considerara. Para ello se utilizan los
objetos NEW y OLD. De esta manera, en un trigger de tipo BEFORE UPDATE
afectando a una columna micolumna, se utilizar la expresin OLD.micolumna para
conocer el valor de la columna antes de ser modificado y NEW.micolumna ser el nuevo
valor de la columna despus de la modificacin. Estos dos valores slo tienen sentido
los dos juntos en una modificacin, pues ante una insercin (INSERT) no existe valor
antiguo (OLD) y ante un borrado (DELETE) no existe un valor nuevo (NEW) pues el
que existe se elimina. Ver ejemplo trigger1 del apartado 2.9.3.

Pgina 61
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Dentro de un trigger de tipo BEFORE, puede cambiarse el nuevo valor mediante una
sentencia de asignacin SET por lo que anulara por completo el efecto de la instruccin
DML que provoc el trigger.

Eventos de disparo

Como se ha indicado, un trigger se ejecuta automticamente (dispara) antes o despus


de una instruccin DML: INSERT, UPDATE o DELETE.
Adems de la forma explcita anterior, pueden tambin ejecutarse las instrucciones del
cuerpo del trigger si la modificacin se produce de forma implcita, como sera el caso
de una instruccin REPLACE pues en realidad equivale a una instruccin DELETE
seguida de una INSERT (por lo tanto aqu se ejecutaran los triggers asociados a estas
dos operaciones).

Triggers de tipo before o after?

Prcticamente no hay diferencia. La ventaja que


puede suponer utilizar los triggers de tipo BEFORE
es que pueden cambiarse los valores modificados
inicialmente por una instruccin UPDATE o
INSERT mientras que con los triggers de tipo
AFTER dara un error de ejecucin.

Teniendo en cuenta los 2 tipos de triggers y las tres


operaciones DML distintas, podramos tener tener 6
triggers por tabla:

BEFORE INSERT, AFTER INSERT, BEFORE


UPDATE, AFTER UPDATE, BEFORE DELETE y
AFTER DELETE.

La vista TRIGGERS del diccionario de datos


contendr toda la informacin sobre cada trigger,
existiendo una entrada (fila) en esta vista por cada
trigger en el servidor; la informacin que aparece
en las columnas es la que se ha tenido en cuenta a la
hora de crearlo.

Ilustracin 193. Vista triggers del diccionario de datos

2.9.2 Borrado de triggers


Sintaxis:

DROP TRIGGER nombre_trigger

Se necesita el privilegio SUPER para borrar triggers.

Pgina 62
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.9.3 Utilizacin de triggers


Ejemplos de utilizacin de triggers:

Copias o rplicas de datos:

Finalidad: Mantener sincronizada una copia de seguridad de unos datos.

Para probar el ejemplo que viene a continuacin lanzar antes la tabla:

CREATE TABLE alumnos_replica


(id INT PRIMARY KEY,
alumno VARCHAR(30))
ENGINE=innodb;

Puedes volver a crear si as lo deseas la tabla alumnos mediante el procedimiento1.sql.

Si creas el siguiente trigger:

Trigger 1 "trigger1"

podrs comprobar que la siguiente instruccin:

Instruccin 5

crea una fila nueva en la tabla alumnos como consecuencia de la instruccin 5 anterior y
otra tambin idntica en la tabla alumnos_replica debido a la ejecucin automtica del
conjunto de instrucciones del trigger.

Otras situaciones en las que se pueden emplear triggers podran ser para llevar el
mantenimiento del stock de artculos: En el momento en que se haga un pedido, se
decrementa automticamente el nmero de unidades pedidas de las existencias de ese
artculo.

Auditora:

Finalidad: Auditar las operaciones que se realizan sobre una tabla.

Antes de lanzar el siguiente ejemplo, utilizaremos la tabla:

Pgina 63
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

CREATE TABLE AUDITA (mensaje VARCHAR(200));

Si editas el siguiente trigger:

Trigger 2 trigger 2

la instruccin siguiente:

Instruccin 6

modificar la columna alumno de la tabla alumnos dejando a Mario como nuevo


nombre y por efecto del trigger crear la siguiente entrada en la tabla audita:

Ilustracin 204. Utilizacin de triggers para auditar las operaciones en la tabla

En relacin a este ltimo trigger Qu pasara si el nombre del alumno fuera nulo?

Validacin de datos

Adems de las constrainsts o restricciones que se definen en el momento de crear las


tablas, podemos utilizar los triggers para validar los datos a almacenar en una tabla y de
esta manera mantener la consistencia de los mismos.

Ejemplos de control de entrada de datos:

- El valor de una columna no debe ser negativo y estar comprendido entre 1 y 80.
- Un empleado que no sea vendedor no puede tener comisin.
- Un empleado no puede ser jefe de si mismo.
-
Siempre que nos encontremos con alguna situacin similar a las anteriores no debemos
dejar que se realice la modificacin de la tabla.

Un aspecto importantsimo a tener en cuenta es que la combinacin operacin DML +


trigger asociado o trigger asociado + operacin DML. Si el trigger falla, entonces falla
la operacin DML que intenta modificar la tabla de la base de datos. Esto puede ser til
para evitar entradas indeseadas en las tablas de la base de datos. De esta manera si en el

Pgina 64
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

cuerpo del trigger se detecta que el dato no es correcto podra provocarse una situacin
de error que anulara y abortara la operacin DML. El problema es que hasta la versin
5.2 por lo menos no aparecer la instruccin SIGNAL (RAISE) que permita provocar
errores como en otros gestores de bases de datos.
No se puede utilizar SQL dinmico para simular una instruccin SIGNAL pues los
triggers no admiten SQL dinmico. Otra estrategia a utilizar en este caso para forzar una
situacin de error y echar atrs el resultado de la sentencia DML que intenta modificar
los datos es realizar una SELECT en el cuerpo del trigger que no recupere ninguna fila.
Al ocurrir un error en el trigger, la combinacin trigger + operacin DML falla y por
tanto aborta la ejecucin de la operacin DML (dem si la combinacin es operacin
DML + trigger).
Supongamos que no se permite altas de alumnos con identificador negativo o cero como
primera columna de la tabla alumnos. Bastar con que codifiquemos un trigger del tipo:

Trigger 3 "trigger3"
Si intentamos realizar primera de las dos siguientes inserciones que vienen a
continuacin, la de una alumna con clave negativa, el trigger fallar y por tanto no se
llevar a cabo la operacin DML que se ejecuta despus (la insercin de la fila) como
puede comprobarse despus de listar la tabla alumnos. En cambio una insercin de un id
superior a 0 har que el trigger finalice correctamente no provocando situacin de error
y por tanto permitiendo despus lanzar la operacin DML de insercin:

Ilustracin 25. Prueba del comportamiento de los triggers

Pgina 65
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Anexo: MySQL desde otras aplicaciones


1 Introduccin: Ventajas y desventajas
Aunque sea de manera demostrativa, no se poda terminar este tema sin exponer cmo
MySQL puede ser utilizado desde otros entornos y lenguajes de programacin externos
a l como son Access, PHP, Perl, Java, Python, C, C++ o .NET (C#, Visual Basic .NET)
entre otros. Para ilustrar alguno de ellos se ha utilizado PHP 5.14 y la versin de Visual
Basic Express 2005, por ser ampliamente utilizados entre la comunidad de
programadores y por poderse obtener y utilizar de manera gratuita. La instalacin y
utilizacin de estos lenguajes as como los controladores de datos queda fuera del
mbito de este curso por lo que al alumno nunca se le va a exigir ninguno de los
conceptos aqu expuestos en ningn tipo de ejercicio o prueba.

Utilizar los elementos de MySQL desde otros entornos externos facilita:

- En entornos cliente / servidor, la carga en el lado del cliente se reduce


considerablemente.
- Mantener actualizadas constantemente las versiones de las aplicaciones cliente
es mucho ms costoso que hacerlo si estas se encuentran centralizadas.
- Las aplicaciones cliente son ms pequeas al encontrarse desarrolladas muchas
de las tareas en forma de programas almacenados.
- Una sola unidad de programa puede ser empleada por entornos totalmente
diferentes como Java o .NET.
- Ms facilidad en la portabilidad / escalabilidad de las aplicaciones cambiando la
lgica solamente desde dentro de los programas almacenados.
- Disminucin del trfico de red al ejecutarse las operaciones en el servidor.
- Mecanismos de mayor seguridad evitando que el usuario acceda a la base de
datos salvo a aquellos programas almacenados para los que est autorizado.

Por otro lado, tambin puede dar lugar a la aparicin de las siguientes desventajas:

- El rendimiento algunas operaciones (bsquedas con patrn, manejo de


cadenas) puede llegar a ser menor si se utilizan programas almacenados en
lugar de hacerlo en otros lenguajes como PHP, Java o Perl.
- Pueden provocar una fragmentacin lgica si una parte de la tarea se implementa
con programas almacenados y la otra reside dentro de los programas cliente.
Adems depurar una aplicacin puede llegar a ser dificultoso pues no existe la
posibilidad de un solo depurador entre distintos entornos.
- Gran dificultad a la hora de portar o migrar los programas almacenados a otros
gestores de bases de datos (slo DB2 de IBM y MySQL cumplen el estndar
ANSI para programas almacenados).

Pgina 66
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2 Ejecucin de procedimientos almacenados.


Tratamiento de errores
Los ejemplos que vienen a continuacin realizan una llamada al procedimiento
resultset1 visto en el apartado 2.5.5 de este tema (este ltimo enva nmero de
departamento, nombre y presupuesto de los departamentos cuyo centro se le pasa como
argumento).

2.1 En PHP
El script de este apartado recoge un
nmero de Departamento mediante un
formulario de entrada y manda el dato
(id_centro) al procedimiento almacenado
resulset1 para que este le enve el nmero
de departamento, nombre y presupuesto
de los departamentos. Ej. de ejecucin:
Ilustracin 216 (dcha.). Resultado de llamar al
procedimiento resultset1 desde PHP

Pgina 67
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 227. LLamando a un procedimiento desde PHP

El mismo ejemplo pero sin hacer


uso del procedimiento y
ejecutando la sentencia Select
directamente:
Ilustracin 238 (dcha). El mismo
resultado pero sin hacer uso del
procedimiento

Pgina 68
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

2.2 En Visual Basic Express 2005


La llamada al procedimiento vbnet1 que viene a continuacin se realiza desde la
ventana inmediato, los resultados se visualizan por la ventana de resultados:

Ilustracin 249. Ejecucin del procedimiento y resultados


Imports MySql.Data.MySqlClient
Module Module1

Sub vbnet1(ByVal centro As Integer)


'Procedimiento que llama al procedimiento MySQL resultset1
'envindole el argumento centro que recibe
Dim host As String = "192.168.0.5"
Dim usuario As String = "root"
Dim clave As String = "------"
Dim base_datos As String = "test"
Dim cadena_conexion As String = "Database=" & base_datos & _
" ;Data Source=" & host & _
";User Id=" & usuario & ";Password=" & clave
'Creacin de la conexin
Dim MiConexion As New MySqlConnection(cadena_conexion)
Try
MiConexion.Open()
'Creacin de un nuevo comando
Dim instruccion As MySqlCommand = New MySqlCommand("resultset1", MiConexion)
'El nuevo comando es de tipo procedimiento almacenado
instruccion.CommandType = CommandType.StoredProcedure
'Creacin del parmetro que se enviar como argumento
Dim parametro1 As MySqlParameter
'Asociando el parmetro creado de VB 2005 al argumento p_numce
'del procedimiento resultset1 de MySQL y definiendo su tipo (entero)
parametro1 = instruccion.Parameters.Add("p_numce", MySqlDbType.Int32)
'Asignando al parmetro el valor recibido en el procedimiento VB 2005
parametro1.Value = centro
'El conjunto de filas y columnas resultantes de llamar
'al procedimiento se almacenar en un objeto de tipo DataReader
Dim MyReader As MySqlDataReader = instruccion.ExecuteReader
'Recorrido de las filas del DataReader
While MyReader.Read
'Impresin de cada una de sus columnas
Console.Write(MyReader.GetInt32(0))
Console.Write(" - " & MyReader.GetString(1))
Console.WriteLine(" - " & MyReader.GetInt32(2))
End While
MyReader.Close()
Catch Exception As MySqlException
Console.WriteLine("Ocurri un error: ")
Console.WriteLine(Exception.Message)
End Try
MiConexion.Close()
End Sub

Pgina 69
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

El mismo ejemplo pero utilizando directamente la instruccin Select


'......
Try
MiConexion.Open()
Dim cadenaselect As String
cadenaselect = "Select numde, nomde, presu from departamentos where numce= " & centro
Dim instruccion As MySqlCommand = New MySqlCommand(cadenaselect, MiConexion)
Dim MyReader As MySqlDataReader = instruccion.ExecuteReader
While MyReader.Read
Console.Write(MyReader.GetInt32(0))
Console.Write(" - " & MyReader.GetString(1))
Console.WriteLine(" - " & MyReader.GetInt32(2))
End While
MyReader.Close()
Catch Exception As MySqlException
' .............

3 Ejecucin de funciones
Los dos ejemplos que vienen a continuacin realizan una llamada a la funcin funcion7
vista en el apartado 2.8.2 de este tema (devuelve el nombre de un alumno a partir de un
identificador de alumno que se le pasa como parmetro). Se utilizan sentencias
preparadas tanto en este apartado como en el 2.10.5.

3.1 En PHP
El fragmento de script de este apartado recoge un nmero matrcula de alumno
mediante un formulario de
entrada y enva el dato
(alumno_id) a la funcin
funcion7 para que este
devuelva el nombre. Ej. de
ejecucin
Ilustracin 30. Resultado de llamar a la funcin funcion7 desde PHP

Ilustracin 25. Llamando a una funcin desde PHP

Pgina 70
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

3.2 En Visual Basic Express 2005


Sub vbnet2(ByVal alumno As Integer)
'Llamar a la funcin MySQL enviando como argumento el alumno
'y recibiendo (return) de sta en el objeto parametro_devuelto el nombre del mismo
Dim host As String = "192.168.0.5"
Dim usuario As String = "root"
Dim clave As String = "------"
Dim base_datos As String = "test"
Dim cadena_conexion As String = "Database=" & base_datos & _
" ;Data Source=" & host & _
";User Id=" & usuario & ";Password=" & clave
Dim MiConexion As New MySqlConnection(cadena_conexion)
Try
MiConexion.Open()
Dim instruccion As MySqlCommand = New MySqlCommand("funcion7", MiConexion)
instruccion.CommandType = CommandType.StoredProcedure
Dim parametro_enviado As MySqlParameter
parametro_enviado = instruccion.Parameters.Add("p_id", MySqlDbType.Int32)
parametro_enviado.Value = alumno
Dim parametro_devuelto As MySqlParameter = _
instruccion.Parameters.Add("parametro_devuelto", MySqlDbType.String)
parametro_devuelto.Direction = ParameterDirection.ReturnValue
instruccion.ExecuteNonQuery()
Console.WriteLine("Nombre del alumno =" + parametro_devuelto.Value)
Catch Exception As MySqlException
'..............

Ilustracin 32. Ejecucin del procedimiento y resultados

4 Ejecucin de sentencias DDL


Los dos ejemplos que vienen a continuacin crean una sencilla tabla en MySQL.

4.1 En PHP
Nota: En el siguiente apartado 5.1 tambin se utilizan instrucciones DDL dentro del
script PHP.

El resultado de realizar dos llamadas al script PHP de creacin de la tabla viene a


continuacin en las dos prximas ilustraciones:

Ilustracin 263. Resultado de ejecutar el script PHP de creacin de una tabla

Pgina 71
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 34. Resultado de ejecutar por segunda vez el script PHP de creacin de una tabla

Ilustracin 275. Creando una tabla desde PHP

4.2 En Visual Basic Express 2005


'..........
Dim MiConexion As New MySqlConnection(cadena_conexion)
Dim cadena_sql As String = "CREATE TABLE tabla1 (id INTEGER)"
Dim instruccion As MySqlCommand = New MySqlCommand(cadena_sql, MiConexion)
Try
MiConexion.Open()
instruccion.ExecuteNonQuery()
Console.WriteLine("Tabla creada")
Catch Exception As MySqlException
Console.WriteLine("Error en la creacin: ")
Console.WriteLine(Exception.Message)
End Try
'..........

5 Ejecucin de sentencias preparadas


Relacionado con el apartado 2.5.7 de este tema.

5.1 En PHP
El siguiente script tiene la misma funcionalidad que el procedimiento1 visto en este
tema (apartado 2.5.4) en el que es creaba la tabla alumnos y se rellenaba con 5 de ellos.

Pgina 72
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Ilustracin 286. Sentencias preparadas en PHP

Pgina 73
Lenguaje Sql con MySQL avanzado Parte II. Programas almacenados

Llamando directamente al procedimiento 1:

Ilustracin 297. Insercin de datos en la tabla sin utilizar sentencias preparadas

5.2 En Visual Basic Express 2005


'Insercin en la tabla alumnos de los alumnos:
'alumno 10, alumno 11... alumno 20 mediante SENTENCIAS PREPARADAS
'..........
Dim MiConexion As New MySqlConnection(cadena_conexion)
Dim cadena_sql As String = "INSERT INTO ALUMNOS VALUES (?nuevo_id,?nuevo_alumno)"
'Creando un nuevo comando asociado a la cadena Select anterior
Dim instruccion As MySqlCommand = New MySqlCommand(cadena_sql, MiConexion)
Try
MiConexion.Open()
'Creando el parmetro parametro_id
Dim parametro_id As MySqlParameter
'Asocindolo con el comando creado
parametro_id = instruccion.Parameters.Add("?nuevo_id", MySqlDbType.Int32)
'Creando el segundo parmetro parametro_alumno
Dim parametro_alumno As MySqlParameter
'Asocindolo con el comando creado
parametro_alumno = instruccion.Parameters.Add("?nuevo_alumno", MySqlDbType.String)
Dim contador As Integer
For contador = 10 To 20
parametro_id.Value = contador
parametro_alumno.Value = "alumno " + contador.ToString
'En cada iteracin, insercin del alumno "contador"
instruccion.ExecuteNonQuery()
Next
Catch Exception As MySqlException
Console.WriteLine("Error en la insercin: ")
Console.WriteLine(Exception.Message)
End Try
'..........

Pgina 74
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

PARTE III.
TRANSACCIONES

Tema 3. Transacciones en MySQL

3.1 Introduccin...........................................................................................2
3.2 Niveles de aislamiento...........................................................................3
3.3 Instrucciones de manejo de transacciones .............................................9
3.4 Transacciones y bloqueos....................................................................13
3.4.1 Deadlock. Cursores de actualizacin..............................15
3.5 Estrategias de bloqueo .........................................................................19
3.6 Aspectos a tener en cuenta a la hora de utilizar transacciones ............21

Anexo. Manejo de transacciones desde otras aplicaciones

1. Desde PHP.............................................................................................21
2. Desde Visual Basic Express 2005 .........................................................22

Pgina 1
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

TEMA 3.
Transacciones en MySQL
Alberto Carrera Martn

3.1 Introduccin
TRANSACCIN en SQL: Conjunto de instrucciones SQL, agrupadas lgicamente,
que o bien se ejecutan todas sobre la base de datos o bien no se ejecuta ninguna.
Una transferencia bancaria entre dos cuentas supone un ejemplo claro para ilustrar el
concepto de transaccin. La transferencia se compone de dos operaciones
(instrucciones):
1) Descontar de la libreta origen a transferir la cantidad fijada.
2) Aumentar el saldo de la libreta destino con el importe de la cantidad transferida
de la cuenta origen.

Est claro que la transaccin bancaria anterior no se puede quedar a medias. O bien se
aplican las dos operaciones lgicas que componen la transaccin o bien no se realiza
ninguna por mantener una consistencia contable. Si slo llegara a realizarse una
operacin de las dos anteriores, esta se deshara.

Las transacciones sobre la base de datos deben ajustarse al principio ACID (ATOMIC +
CONSISTENT + ISOLATED + DURABLE), lo que implica que una transaccin debe
ser:

- ATMICA (ATOMIC): Todas las operaciones que componen la transaccin


deben aplicarse sobre la base de datos o no aplicarse ninguna. Una transaccin
es indivisible.

- CONSISTENTE (CONSISTENT): Se mantiene la consistencia de los datos


antes y despus de ejecutar la transaccin.

- AISLADA (ISOLATED): Cuando muchas transacciones se pueden llevar a


cabo simultneamente por uno o varios usuarios, la realizacin de una no debe
afectar jams a las otras y por tanto no llevar a una situacin de error.

- PERMANENTE (DURABLE): Una vez que la transaccin ha sido confirmada


(COMMIT) y por tanto los cambios de la base de datos guardados, stos deben
perdurar en el tiempo aun cuando el gestor de base de datos o el propio equipo
fallen.

La capacidad transaccional de MySQL depende del sistema de almacenamiento


empleado. Los dos ms importantes son el MyISAM y el InnoDB.

MyISAM: No admite transacciones. Utilizada para aplicaciones que en su mayora son


de slo lectura. Si las aplicaciones casi no necesitan el empleo de las transacciones, es
la forma de almacenamiento a utilizar pues el rendimiento es ptimo. Si el nmero de

Pgina 2
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

transacciones y accesos concurrentes para modificar los datos de la base de datos es


importante, entonces es mejor utilizar el siguiente modelo de almacenamiento.

InnoDB: El modelo de almacenamiento de transaccin segura de MySQL ms popular.


Soporta transacciones ACID as como bloqueos a nivel de fila y concurrencia (a costa
de sacrificar un poco la velocidad de proceso). Es el tipo predeterminado de modelo de
almacenamiento si se ha instalado el gestor de bases de datos utilizando el asistente y
por tanto no se ha especificado a la hora de crear las tablas en la sentencia CREATE
TABLE. Puedes comprobar dicho modelo de almacenamiento con la instruccin SHOW
CREATE TABLE alumnos y ver la columna CREATE TABLE:

Ilustracin 1. Almacenamiento InnoDB

3.2 Niveles de aislamiento


Una sesin de base de datos es una conexin nica a la base de datos que comienza
cuando se entra en el sistema (login) y termina cuando se desconecta de ella.

Cada vez que abrimos el programa cliente MySQL Query Browser estamos abriendo
una sesin (podemos tener varias instancias de este programa abiertas varias
sesiones). Para averiguar el identificador de conexin que nos asigna el servidor,
consultaremos la funcin que nos lo proporciona:

Pgina 3
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

Ilustracin 2. Identificador de conexin I

La sesin termina cuando cerramos el programa MySQL Query Browser.

Tambin en alguna ocasin hemos creado alguna sesin para trabajar con la base de
datos desde la ventana de lnea de comandos de Windows:

Ilustracin 3 Identificador de conexin II

En el momento de conectarnos de esta manera a MySQL se visualiza el identificador de


conexin (4 lnea en la ilustracin 3) que ste nos asigna. La conexin dura hasta que la
abandonemos (comando EXIT).

Cada sesin trabaja en su propia zona de memoria pudiendo llegar incluso a bloquear
los datos de la base de datos con los que trabaja.
Los niveles de aislamiento determinan la manera en que las transacciones de una sesin
pueden afectar a los datos recuperados o accedidos por otra sesin. Hay por tanto dos
conceptos interrelacionados: por una lado la concurrencia (varios sesiones realizando
transacciones al mismo tiempo) y por otro el grado de consistencia de los datos.
Determinan tambin el grado en que las transacciones se ajustan al principio ACID.
Cuanto mayor es el grado de aislamiento, menor es el nmero de transacciones que se
pueden realizar concurrentemente pero tambin es menor la posibilidad de que
interfieran las transacciones. Por otro lado, cuanto menor es el grado de aislamiento,
mayor es el nmero de transacciones que se pueden realizar concurrentemente pero el
riesgo de conflicto entre transacciones es elevado.

Pgina 4
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

El estndar ANSI define 4 modelos de aislamiento, soportados todos ellos por el


sistema de almacenamiento InnoDB de MySQL:

- Lectura no confirmada (tambin conocido como lectura sucia): Es el nivel ms


bajo. Permite que una transaccin pueda leer filas que todava no han sido
confirmadas (COMMIT). El hecho de que utilizando este nivel se aumente el
rendimiento del proceso no justifica que pueda permitirse que un usuario lea
datos modificados por otro usuario que todava puede deshacer dichas
modificaciones.

- Lectura confirmada. Slo se permite lectura de datos que han sido


confirmados. Si un usuario est ejecutando un procedimiento que recorre las
filas recuperadas de una tabla mediante una sentencia SELECT, si otro usuario
en ese momento realiza una modificacin de la tabla, el primero no ver las
modificaciones realizadas por este ltimo.

- Lectura repetible. Es el nivel por defecto. Las lecturas repetidas de la misma


fila por la misma transaccin dan los mismos resultados. Ningn cambio hecho
en la base de datos por otros usuarios ser visto por la transaccin lanzada hasta
que esta se confirme o deshaga, es decir, si se repite dentro de una transaccin
una instruccin SELECT, esta devolver siempre los mismos resultados
(excepto cuando dentro de la misma transaccin pudieran realizarse cambios en
esa filas recuperadas por la SELECT).

- Secuenciable. Mayor nivel de aislamiento. Las transacciones se aslan


completamente dando la impresin de que se ejecutan secuencialmente, una
detrs de otra. Para conseguir esto, los sistemas gestores de bases de datos
bloquean cada fila leda para que otras sesiones no puedan modificar estos datos
hasta que la transaccin finalice. El bloqueo dura hasta que la transaccin es
confirmada o deshecha. Este nivel disminuye mucho el rendimiento del sistema
y puede provocar situaciones de abrazo mortal como se ver ms adelante.

El nivel de aislamiento por defecto se podra cambiar (no aconsejable):

SET TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ


COMMITTED | REPEATABLE READ | SERIALIZABLE}

Los siguientes ejemplos nos ayudarn a comprender el manejo de transacciones:

Nota: Recordar que se mantiene siempre la consistencia en lectura Los datos que ven
las transacciones son los ltimos que se validaron.

Antes de comenzar estos ejemplos ejecuta el procedimiento1 ya


visto en temas anteriores y que crea la tabla alumnos con 5 alumnos:

Abre dos sesiones nuevas de cliente de lnea de comandos de


MySQL (lo puedes hacer mediante la opcin de men Tools /
MySQL Command Line Client).
Ilustracin 4. Tabla alumnos

Pgina 5
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

En este momento tendrs 3 sesiones abiertas, una del programa MySQL Query Browser
y dos clientes de lnea de comando de MySQL (ilustracin 5). Puedes comprobar el
identificador de cada sesin con la funcin vista anteriormente (SELECT
CONNECTION_ID();):

Ilustracin 5. Sesiones abiertas en la barra de tareas


Utilizaremos para la prctica las dos sesiones de clientes de lnea de comando MySQL.

Como los nmeros de identificacin de conexin sern distintos a los que utilices,
llamaremos A a la conexin de cliente de lnea de comando de identificador ms bajo y
B a la otra conexin de cliente cuyo identificador es ms alto.

Dejaremos la propiedad autocommit a 0 en las dos sesiones abiertas por lo que los
cambios en la base de datos no se almacenan despus de cada instruccin sino cuando
se confirman explcitamente con la instruccin COMMIT.
A
> SET autocommit = 0;
> USE TEST

B
> SET autocommit = 0;
> USE TEST

A
> INSERT INTO alumnos VALUES (6, 'alumno6');
> SELECT * FROM alumnos WHERE id=6;
+----+---------+
| id | alumno |
+----+---------+
| 6 | alumno6 |
+----+---------+

B
> SELECT * FROM alumnos WHERE id=6;
Empty set (0.00 sec)

A
> COMMIT;

B
> SELECT * FROM alumnos WHERE id=6;
Empty set (0.00 sec)
> COMMIT;
Query OK, 0 rows affected (0.00 sec)
> SELECT * FROM alumnos WHERE id=6;
+----+---------+
| id | alumno |
+----+---------+
| 6 | alumno6 |
+----+---------+
1 row in set (0.00 sec)

Pgina 6
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

A
> UPDATE alumnos SET alumno = 'Alberto Carrera' WHERE id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
> SELECT * FROM ALUMNOS WHERE id=1;
+----+-----------------+
| id | alumno |
+----+-----------------+
| 1 | Alberto Carrera |
+----+-----------------+
1 row in set (0.00 sec)

B
> SELECT * FROM ALUMNOS WHERE id=1;
+----+----------+
| id | alumno |
+----+----------+
| 1 | alumno 1 |
+----+----------+
1 row in set (0.00 sec)

> UPDATE alumnos SET alumno = 'Raquel Carrera' WHERE id = 2;


Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0

l> SELECT * FROM ALUMNOS WHERE id=2;


+----+----------------+
| id | alumno |
+----+----------------+
| 2 | Raquel Carrera |
+----+----------------+
1 row in set (0.00 sec)

A
mysql> SELECT * FROM ALUMNOS WHERE id=2;
+----+----------+
| id | alumno |
+----+----------+
| 2 | alumno 2 |
+----+----------+
1 row in set (0.00 sec)

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

mysql> SELECT * FROM ALUMNOS;


+----+-----------------+
| id | alumno |
+----+-----------------+
| 1 | Alberto Carrera |
| 2 | alumno 2 |
| 3 | alumno 3 |
| 4 | alumno 4 |
| 5 | alumno 5 |
| 6 | alumno6 |
+----+-----------------+
6 rows in set (0.00 sec)

Pgina 7
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

B
mysql> SELECT * FROM ALUMNOS;
+----+----------------+
| id | alumno |
+----+----------------+
| 1 | alumno 1 |
| 2 | Raquel Carrera |
| 3 | alumno 3 |
| 4 | alumno 4 |
| 5 | alumno 5 |
| 6 | alumno6 |
+----+----------------+
6 rows in set (0.00 sec)

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

mysql> SELECT * FROM ALUMNOS;


+----+-----------------+
| id | alumno |
+----+-----------------+
| 1 | Alberto Carrera |
| 2 | Raquel Carrera |
| 3 | alumno 3 |
| 4 | alumno 4 |
| 5 | alumno 5 |
| 6 | alumno6 |
+----+-----------------+
6 rows in set (0.00 sec)

mysql> SELECT * FROM ALUMNOS;


+----+-----------------+
| id | alumno |
+----+-----------------+
| 1 | Alberto Carrera |
| 2 | alumno 2 |
| 3 | alumno 3 |
| 4 | alumno 4 |
| 5 | alumno 5 |
| 6 | alumno6 |
+----+-----------------+
6 rows in set (0.00 sec)

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

mysql> SELECT * FROM ALUMNOS;


+----+-----------------+
| id | alumno |
+----+-----------------+
| 1 | Alberto Carrera |
| 2 | Raquel Carrera |
| 3 | alumno 3 |
| 4 | alumno 4 |
| 5 | alumno 5 |
| 6 | alumno6 |
+----+-----------------+
6 rows in set (0.00 sec)

Nota, como no se ha utilizado la instruccin de comienzo de transaccin START


TRANSACTION la transaccin ha empezado de manera implcita.

Pgina 8
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

3.3 Instrucciones de manejo de transacciones


- START TRANSACTION: Comienzo de una nueva transaccin. Si ya existe
una iniciada, esta ltima finaliza con confirmacin de datos (COMMIT).
Cuando comienza una nueva transaccin, la propiedad autocommit
automticamente pasa a estado 0 (OFF apagado) hasta que finaliza la
transaccin.

- COMMIT: Termina la transaccin guardando en la base de datos todos los


cambios realizados por la transaccin. Cualquier tipo de bloqueo que se
mantuviera durante la transaccin queda liberado.

- ROLLBACK: Termina la transaccin deshaciendo todos los cambios que


hubiera realizado sobre la base de datos. Libera los bloqueos que hubiera
realizado la transaccin.

- SAVEPOINT punto de salvaguarda: Crea un punto de salvaguarda al que se


puede retroceder mediante la instruccin ROLLBACK TO SAVEPOINT.

- ROLLBACK TO SAVEPOINT punto de salvaguarda. Realiza un


ROLLBACK de todas las sentencias ejecutadas desde que se cre el punto de
salvaguarda.

- SET TRANSACTION: Permite cambiar el nivel de aislamiento de la


transaccin como se ha visto anteriormente.

- LOCK TABLES: Permite bloquear explcitamente una o varias tablas. A la vez


cierra todas las transacciones abiertas.

Por defecto, MySQL efecta un COMMIT implcito despus de ejecutar cada


instruccin DML SQL por lo que los cambios se guardan despus de cada instruccin.

La propiedad (variable) autocommit que controla este comportamiento est puesta a 1.

Ilustracin 6. Propiedad autocommit

Pgina 9
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

Para poder trabajar con transacciones:

1) Cambiar la propiedad autocommit para que no se haga un COMMIT


automticamente despus de cada instruccin SQL:

SET autocommit=0

De todas formas, aunque es un buen mtodo de trabajo en este y otros gestores, es


redundante esta primera accin pues como se ha indicado la instruccin START
TRANSACTION ya deja a 0 el valor de la propiedad anterior.

2) Indicar el comienzo de la transaccin con la instruccin START TRANSACTION

Nota: Independientemente del valor de la propiedad anterior, todas las sentencias DDL
del lenguaje: ALTER, CREATE, DROP tienen el COMMIT o confirmacin de
manera implcita o automtica.

Un ejemplo sencillo de todo lo visto anteriormente:

Procedimiento 1 transac1

La llamada al procedimiento:

CALL transac1(7,'Mario A. Carrera')

insertar al alumno Mario.

En cambio:

CALL transac1(7, 'Carmen Bailin')

dar error de clave duplicada. Como el error no es tratado al no existir manejador de


errores, la instruccin 10 no llegar a ejecutarse pero la transaccin finalizar sin haber
realizado la insercin.

Pgina 10
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

Otro ejemplo de cdigo y ejecucin:

Procedimiento 2 transac2
mysql> USE TEST
Database changed
mysql> call transac2(7, 'Blanca Bailin', @n_error, @texto_error);
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @n_error, @texto_error;


+----------+-----------------+
| @n_error | @texto_error |
+----------+-----------------+
| 1062 | Clave duplicada |
+----------+-----------------+
1 row in set (0.00 sec)

mysql> call transac2(8, 'Irene Yera', @n_error, @texto_error);


Query OK, 0 rows affected (0.03 sec)

mysql> SELECT @n_error, @texto_error;


+----------+--------------------------+
| @n_error | @texto_error |
+----------+--------------------------+
| 0 | Alta de alumno realizada |
+----------+--------------------------+
1 row in set (0.00 sec)

/* Borrado de la tabla alumnos*/

mysql> call transac2(9, 'Ana Escalada', @n_error, @texto_error);


Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @n_error, @texto_error;


+----------+-------------------+
| @n_error | @texto_error |
+----------+-------------------+
| -1 | Ocurri un error |
+----------+-------------------+
1 row in set (0.00 sec)

Pgina 11
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

Antes de ejecutar el tercer ejemplo asegrate que tienes la tabla:

CREATE TABLE AUDITA (mensaje VARCHAR(200))

Procedimiento 3 transac3

Nota, probar diferentes opciones. El punto de ruptura se utiliza para deshacer


grabaciones en la tabla audita. Se deja al alumno averiguar su funcionamiento.

La ejecucin de la llamada al procedimiento:

CALL transac3 (30, 'Centro 30', 'Los Olivos - Huesca', 140, 'Departamento 140', 40000)

Insertar la ltima fila en la tabla centros y departamentos:

Ilustracin 7. Tabla centros despus de la ltima insercin

Pgina 12
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

Ilustracin 8. Tabla departamentos despus de la ltima insercin


y las siguientes entradas en la tabla audita:

Ilustracin 9. Tabla audita despus de la ltima insercin


Si volvemos a ejecutar la misma llamada, se aaden nuevas lneas:

Ilustracin 10. Tabla audita despus de la ltima operacin

3.4 Transacciones y bloqueos


Observa la siguiente secuencia de operaciones que se realizan en dos sucursales
bancarias sobre una misma cuenta. La primera columna tiempo (T) indica el orden en
que se efectan:

T SUCURSAL A SUCURSAL B SALDO INICIAL CUENTA


0 1.000
1 Disminuye 200 800
2 Aumenta 300 1.300
3 Confirma operacin 800
4 Confirma operacin 1.300

La situacin anterior no debe aceptarse. Solucin:

T SUCURSAL A SUCURSAL B SALDO INICIAL


CUENTA
0 1.000
1 Disminuye 200 800
2 La cuenta est bloqueada hasta que la
Sucursal A confirme la operacin
3 Confirma 800
operacin
4 Aumenta 300 1.100
5 Confirma operacin 1.100

Pgina 13
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

En la anterior solucin cada transaccin de programas, usuarios espera a que acabe la


transaccin ya iniciada en un fila y que por tanto est bloqueada (no hay espera si la
operacin simplemente es una lectura de datos de la fila y no una modificacin de los
mismos).

Las situaciones en que se producen estos bloqueos son:

- Sentencia UPDATE: Las filas afectadas se bloquean hasta que se confirme o


deshaga la transaccin.

- Sentencia INSERT: Si existe clave primaria, las filas insertadas quedan


bloqueadas para prevenir que otra transaccin pueda introducir otra fila con la
misma clave.

- Sentencia LOCK TABLES: Bloquea la tabla entera. No es muy eficiente pues


reduce la concurrencia.

- Si dentro de una sentencia SELECT se utilizan las clausulas FOR UPDATE o


LOCK IN SHARE MODE, todas las filas devueltas por la sentencia SELECT
sern bloqueadas:

SELECT opciones de la sentencia SELECT


[FOR UPDATE | LOCK IN SHARE MODE]

La clusula LOCK IN SHARE MODE suele utilizarse para garantizar la integridad


referencial. Imagina que ests intentando introducir los datos de un nuevo
departamento al que le asignars un determinado centro. Existe la clave ajena:
departamentos.numce centros.numce. Puede ocurrir que durante el proceso, otra
transaccin elimine la fila correspondiente del centro en la tabla Centros. Al intentar
introducir el departamento aparecer una situacin de error. Por eso, en estos casos,
en primer lugar hay que asegurarse de que nadie modificar el Centro:

SELECT * FROM CENTROS WHERE NUMCE=XX LOCK IN SHARE MODE

Por otro lado la solucin anterior no es buena para garantizar la clave nica en una
tabla. Imagina que cada departamento se obtiene sumando 10 al nmero ltimo
asignado. Si utilizamos la clusula LOCK IN SHARE MODE puede ocurrir que dos
transacciones lean al mismo tiempo el ltimo id asignado, le sumen 10 y a la hora de
modificarlo provoquen una situacin de clave duplicada o como se ve ms adelante
una situacin de abrazo mortal (deadlock). La solucin para este caso:

SELECT id FROM tablax FOR UPDATE


UPDATE tablax SET id

en la que primero se intenta leer con intencin de modificar, utilizando adems un


bloqueo FOR UPDATE y luego, si ha sido posible realizar la operacin anterior,
entonces incrementarlo (UPDATE tablax).
El nivel de bloqueo es el mismo que para un UPDATE.
Los bloqueos son levantados cuando la transaccin se confirma o deshace.

Pgina 14
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

3.4.1 Deadlock. Cursores de actualizacin


Tambin conocido como abrazo mortal. Esta situacin ocurre cuando una transaccin A
intenta modificar los datos que estn siendo modificados por una transaccin B y a su
vez esta ltima intenta modificar los datos que estn siendo modificados por la primera
transaccin A.

T SUCURSAL A SUCURSAL B SITUACION


1 Modifica saldo La cuenta X queda bloqueada por
cuenta X A
2 Modifica saldo La cuenta Y queda bloqueada por
cuenta Y B
3 Modifica saldo A queda a la espera pues Y est
cuenta Y bloqueada
4 Modifica saldo B queda a la espera pues X est
cuenta X bloqueada

Como consecuencia del abrazo mortal anterior, una de las dos transacciones realizar un
ROLLBACK provocando una situacin de error.

Volvemos a la situacin inicial de la tabla alumnos, 5 filas con 5 alumnos de ids 1 al 5


y de nombres alumno1alumno5.

Abrimos 2 sesiones A y B:

A
mysql> USE TEST;
Database changed
mysql> SET autocommit = 0;

B
mysql> USE TEST;
Database changed
mysql> SET autocommit = 0;

A
mysql> UPDATE ALUMNOS SET alumno='Alberto Carrera' WHERE id=1;
/* A ya puede ver el cambio producido pero B seguir viendo alumno1
como nombre en lugar de Alberto Carrera */

Ilustracin 11. Tiempo de espera superado


Despus de un tiempo de espera de B, aparece el error de la
ilustracin anterior, al intentar modificar una fila que est
siendo modificada y todava no ha sido confirmada o deshecha por
A.

mysql> UPDATE ALUMNOS SET alumno='Mario Carrera' WHERE id=2;

Pgina 15
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

A
mysql>UPDATE ALUMNOS SET alumno='Carmen Bailin' WHERE id=2;

B
Si ejecutamos rpidamente:
mysql>UPDATE ALUMNOS SET alumno='Raquel Carrera' WHERE id=1;

Ilustracin 12. Abrazo mortal


A

Ilustracin 13. Tabla alumnos vista por el usuario A


B

Ilustracin 14. Tabla alumnos vista por el usuario B


A
mysql>COMMIT;

B
Sigue viendo la pantalla anterior ltima (ilustracin 14).

mysql>COMMIT;

Ve la pantalla penltima (ilustracin 13)

Para probar el cuarto ejemplo hay que lanzar antes otra vez el procedimiento
procedimiento1 para crear de nuevo la tabla alumnos.

Pgina 16
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

Procedimiento 4transac4
A
USE TEST;
SET autocommit = 0;

B
USE TEST;
SET autocommit = 0;

A
UPDATE alumnos SET alumno= 'Mario Carrera'
WHERE id=1;

B
CALL transac4 (1, 'Carmen Bailin', @p_n_error, @p_text_error);
/* Despus de un tiempo de espera 50 segundos en el que parece que se ha quedado
colgado el programa */
Query OK, 0 rows affected (51.55 sec)
SELECT @p_n_error, @p_text_error;
+------------+---------------------------+
| @p_n_error | @p_text_error |
+------------+---------------------------+
| 1205 | Tiempo de espera excedido |
+------------+---------------------------+

Pgina 17
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

A continuacin una solucin para evitar el DEADLOCK: Cursores de actualizacin


(CURSOR FOR UPDATE)

Procedimiento 5 transac5

Antes se finalizar este apartado sealar que, como hemos comprobado al intentar
escribir en una fila bloqueada (sin ser deadlock), se ha producido un tiempo de espera
de 50 segundos tras el que se ha producido un error de tiempo de espera excedido
(1205) y la transaccin se ha deshecho.

La variable de sistema innodb_lock_wait_timeout es la que almacena el tiempo


mximo de espera. Dicha variable puede modificarse en caso de aplicaciones con largas
transacciones. Tambin se puede como se ha hecho antes crear un manejador de error.

Pgina 18
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

3.5 Estrategias de bloqueo


Si una transaccin tiene necesidad de leer datos que posteriormente se pueden ver
afectados por operaciones de manipulacin (INSERT, UPDATE, DELETE), hay que
tomar precauciones para que otra transaccin no pueda modificar unos datos despus de
que hayan sido ledos por la primera transaccin y antes de ser modificados por esta. Ej.

T SUCURSAL A SUCURSAL B SITUACION


0 SALDO INICIAL
CUENTA X =
1.000
1 LEE SALDO CON INTENCIN DE 1.000
DISMINUIRLO EN 900 SI ES
POSIBLE PARA EVITAR DEJAR LA
CUENTA EN NMEROS ROJOS
2 DISMINUYE LA 500
CUENTA EN
500
3 COMO EL SALDO PARA LA - 400
SUCURSAL A ES DE 1.000, A
CONTINUACIN DISMINUYE EL
SALDO EN 900

Posibles soluciones:

- Estrategia de bloqueo pesimista: El planteamiento para este caso es que las


transacciones concurrentes es muy fcil de que ocurran y por tanto hay que estar
prevenido. Por tanto habr que bloquear las filas despus de leerlas. Otras
transacciones que quieran modificar los datos debern esperar.

- Estrategia de bloqueo optimista: Asume que es muy poco probable que el


valor de una fila que acabamos de leer cambie. Si asumimos este planteamiento
como mnimo deberemos asegurarnos de que la fila no ha sido modificada y si
as ha sido entonces la transaccin no debe llevarse a cabo aun pudindose
realizar.

A continuacin un ejemplo de implementacin de la estrategia pesimista:

Pgina 19
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

Procedimiento 6 transac6

Es una estrategia muy segura pues asegura la consistencia entre la lectura (SELECT) y
la operacin DML (UPDATE), pero limita mucho el rendimiento del sistema al obligar
a las transacciones a largas esperas para poder completarse.

En la estrategia optimista, como la transaccin no bloquea la fila que lee, antes de que
realice la modificacin sobre la misma debe asegurarse que el valor de la fila no ha
cambiado desde que la ley; en caso de que no se hubieran producido cambios se
realizar y confirmar la transaccin, en caso contrario no se llevar a cabo.

Para la eleccin de una estrategia u otra hay que tener en cuenta la concurrencia y
robustez: la estrategia pesimista supone menos errores y reintentos mientras que con la
optimista se reduce el tiempo de duracin de los bloqueos aumentando por tanto la
concurrencia y el rendimiento del sistema.
Generalmente suele utilizarse la estrategia pesimista y slo se recurre a la optimista si la
duracin de los bloqueos o el nmero de filas que se bloquean es elevado en la
estrategia pesimista. De todas formas el uso de una u otra depende mucho de las
caractersticas de la aplicacin.

Pgina 20
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

3.6 Aspectos a tener en cuenta a la hora de


utilizar transacciones
De todo lo visto anteriormente, podemos resumir:

- Siempre debe mantenerse la integridad de la base de datos para garantizar que


los datos que almacenan son precisos, fieles a la realidad y lo ms correctos
posibles.

- La duracin de los bloqueos debe ser lo mnimo posible. De igual manera debe
ser tambin mnimo el nmero de filas a bloquear por las transacciones.

- La operacin de ROLLBACK (deshacer) debe evitarse en los casos que se pueda


pues es una operacin costosa en el tiempo y en utilizacin de recursos. Por eso,
habr que adelantarse a las situaciones que pueden provocarlo como los intentos
de insercin de filas de clave duplicada, realizando en este caso bsquedas antes
de las inserciones. Relacionado con este punto, se evitarn en la medida en que
se pueda la creacin y utilizacin de puntos de salvaguarda; slo si el nmero de
situaciones posibles a controlar es elevado, quizs en ese caso sea rentable y
aceptable su utilizacin Indicar tambin que la operacin de confirmacin o
COMMIT al igual que la de ROLLBACK es una operacin costosa pues trae
consigo escritura fsica en disco de los datos de la cach de memoria del gestor
de bases de datos. Por eso hay que utilizar esta instruccin de confirmacin en
los sistemas en tiempo real de un negocio para mantener los datos ntegros pero
hay que minimizar su uso en situaciones de carga masiva de datos a travs de
scripts por ejemplo.

- Considerar siempre la estrategia de bloqueo pesimista salvo en los casos del


final del apartado anterior que podran llegar a causar frustracin en el usuario
por el bajo rendimiento del sistema.

- En cada programa debe especificarse explcitamente el comienzo y fin de


transaccin y no asumir que los programas externos son los que validarn o
desharn la transaccin.

Anexo: Manejo de transacciones desde otras


aplicaciones
De la misma manera y con la misma intencin con que se ha expuesto el anexo del
ltimo apartado del tema 2, a continuacin se indica cmo son manejadas las
transacciones MySQL desde otras aplicaciones externas a ella.

1 Desde PHP
En el siguiente ejemplo se intenta llevar a cabo una transaccin consistente en cambiar
el nombre del alumno de matrcula 1. Si todo va bien aparecer el mensaje Filas

Pgina 21
Lenguaje Sql con MySQL avanzado Parte III. Transacciones

afectadas: 1 Transaccin completada llevndose a cabo la transaccin y validndose


mediante la instruccin de la lnea 28. Si no puede realizarse es deshecha (lnea 23).

Ilustracin 15. Manejando transacciones desde PHP

2 Desde Visual Basic Express 2005


Idntica operacin que en el apartado 1 de este anexo:

'..................
Dim MiConexion As New MySqlConnection(cadena_conexion)
Dim cadena_sql As String = "UPDATE alumnos" + _
" SET alumno='Mario Carrera' " + _
" WHERE id=1"
Dim instruccion As MySqlCommand = New MySqlCommand(cadena_sql, MiConexion)
MiConexion.Open()
Dim MiTransaccion As MySqlTransaction = MiConexion.BeginTransaction
Try
Dim filas_afectadas As Integer
filas_afectadas = instruccion.ExecuteNonQuery()
Console.WriteLine("Filas afectadas: " + filas_afectadas.ToString)
MiTransaccion.Commit()
Console.WriteLine("Transaccin finalizada")
Catch Exception As MySqlException
Console.WriteLine("Error en la transaccin: ")
Console.WriteLine(Exception.Message)
Try
MiTransaccion.Rollback()
Console.WriteLine("Transaction no realizada")
Catch RollbackException As MySqlException
Console.WriteLine("Rollback fallido:")
Console.WriteLine(RollbackException.Message)
End Try
End Try
'..................

Pgina 22
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

PARTE IV.
OPTIMIZACIN DE MySQL.

Tema 4. Optimizacin de MySQL.

4.1 Introduccin.......................................................................................... 2
4.2 Los ndices............................................................................................ 4
4.2.1 ndices parciales ... 4
4.2.2 ndices multicolumna (ndices compuestos) 5
4.2.3 ndices full-text 5
4.2.4 Los ndices y el operador LIKE .. 7
4.2.5 Los ndices y la cardinalidad 8
4.3 Rendimiento y optimizacin de las consultas ... 8
4.3.1 Detectar las consultas lentas. Activar el slow query log .. 8
4.3.2 Optimizacin de consultas mediante la sentencia EXPLAIN .. 9
4.3.3 Ejemplos del uso de la sentencia EXPLAIN . 13
4.3.4 Comando ANALYZE TABLE .. 16
4.3.5 Comando OPTIMIZE TABLE 18
4.4 Gestin de la memoria en el servidor MySQL 20
4.4.1 Ajustar las propiedades de memoria de los threads que
genera MySQL 20
4.4.2 Utilizacin siempre que sea posible de las tablas tipo MEMORY
4.4.3 Descripcin y uso de la Query Cach de MySQL 21
4.4.4 Descripcin y uso de la Cach de claves (Key Cache) de
MySQL . 24
4.4.5 Mejoras en los join, sort, y escaneo completo de tablas 25
4.5 Herramientas relacionadas con el rendimiento................................... 28
4.5.1 Optimizacin de expresiones y funciones mediante la funcin
BENCHMARK() 28
4.5.2 Test de rendimiento (Benchmarking) . 29

Anexo 1. Uso de la herramienta DataGen para insertar mltiples filas en tablas de


prueba .. 32

Anexo 2. Instalar interprete de Perl para Windows (ActivePerl) .. 40

Anexo 3. MySQL Administrador 42


Introduccin a MySQL Administrador
Instalacin de MySQL Administrator
Establecer una conexin con el servidor
MySQL Administrator para administrar usuarios
MySQL Administrator para crear grficos customizados de monitorizacin
del sistema

Pgina 1
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

TEMA 4.
Optimizacin de MySQL.
Javier Robles Cascallar

4.1 Introduccin.
Para poder seguir este tema es necesario partir de unos conocimientos bsicos en la
arquitectura de MySQL y en tareas de administracin. En concreto al lector se le supone
familiarizado en los siguientes aspectos:

Estructura interna de MySQL. Conocer las distintas arquitecturas de almacenamiento (InnoDB,


MyISAM)
Configuracin a travs de la lnea de comandos y ficheros de opciones (my.ini). Conocer las
variables de estado y variables de sistema de MySQL.
ndices. Tipos de ndices y su creacin.
Administracin de cuentas de usuario y seguridad.
(Toda esta informacin est recogida en el Bloque III del curso de SQL con MySQL que oferta el Aula
Mentor)

El propsito de la optimizacin es ayudar a mejorar el rendimiento de la base de datos.


Hay muchas maneras de optimizar MySQL, desde aadir un ndice para acelerar el
tiempo de respuesta de una consulta hasta cambiar el valor de una variable de
configuracin para mejorar la gestin de la memoria. Este tema no pretende cubrir todos
los posibles aspectos que permiten mejorar el rendimiento de una base de datos como
MySQL, ya que se saldra de los objetivos del curso, simplemente trata de llevar al
lector, que ya conoce el lenguaje MySQL y las tareas bsicas de administracin, a dar
un paso ms, y comenzar a introducirle, mediante explicaciones y ejemplos, en los
aspectos ms destacables de las muchas posibilidades de optimizacin que un
administrador puede llevar a cabo.

Muchos de los ejemplos de este documento se han creado usando la base de datos llamada
curso cuya descripcin de las tablas libros, bibliotecas, ejemplares,
codigospostales es la siguiente:

mysql> use curso;


mysql> desc libros;

+-------------+------------------+------+-----+---------+---------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+---------+
| id | int(10) unsigned | NO | PRI | NULL | auto_inc|
| TITULO | varchar(200) | YES | | NULL | |
| AUTOR | varchar(100) | YES | | NULL | |
| COMENTARIOS | varchar(100) | YES | | NULL | |
| NUMPAGINAS | int(5) | YES | | NULL | |
+-------------+------------------+------+-----+---------+---------+
5 rows in set (0.00 sec)

Pgina 2
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

mysql> desc bibliotecas;


+-----------+--------------+------+-----+---------+--------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+--------------+
| id | int(10) | NO | PRI | NULL | auto_increme |
| nombre | varchar(100) | YES | | NULL | |
| direccion | varchar(60) | YES | | NULL | |
| ciudad | varchar(100) | YES | | NULL | |
+-----------+--------------+------+-----+---------+--------------+
4 rows in set (0.01 sec)

mysql> desc ejemplares;


+--------+------------------+------+-----+---------+--------------+
| Field | Type | Null | Key | Default | Extra |
+--------+------------------+------+-----+---------+--------------+
| id | int(10) | NO | PRI | NULL | auto_increme |
| id_tit | int(10) unsigned | YES | MUL | NULL | |
| id_bib | int(10) | YES | MUL | NULL | |
+--------+------------------+------+-----+---------+--------------+
3 rows in set (0.36 sec)

mysql> desc codigospostales;


+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| PROVINCIA | varchar(30) | YES | | NULL | |
| POBLACION | varchar(100) | YES | | NULL | |
| COD_POS | varchar(8) | YES | MUL | NULL | |
+-----------+--------------+------+-----+---------+-------+

Pgina 3
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4. 2 ndices.
Los ndices, como ya es bien conocido, permiten al servidor de base de datos localizar
rpidamente, en las consultas, los registros buscados sin tener que hacer un recorrido
completo de la tabla para su recuperacin. Los administradores, ante el hecho de crear un
ndice sobre un atributo de una tabla, siempre deben analizar si el aumento de la velocidad
en las respuestas a las consultas que afectan a esa tabla compensa el espacio en disco
requerido para almacenar el ndice y tambin la posible sobrecarga del procesador a la hora
de actualizar el ndice cuando se realizan modificaciones sobre los datos de la tabla.

En este apartado no nos vamos a detener, por lo tanto, en como se crean, se modifican, o se
eliminan ndices en MySQL pues esos conocimientos ya se suponen adquiridos. Lo que se
pretende es dar a conocer al lector determinados aspectos no tan conocidos sobre los ndices
y tambin posibles estrategias de indexacin.

4. 2. 1 ndices parciales.
En ocasiones es ms conveniente generar un ndice no sobre un atributo completo de una
tabla sino sobre una parte de ese atributo. La principal ventaja de esta medida es el ahorro
de espacio a la hora de almacenar dicho ndice.
Supongamos, por ejemplo, que tenemos una base de datos de libros donde se almacena el
catlogo de una biblioteca . En esa base de datos hay una columna de una tabla donde se
guarda el ttulo del libro. Si hacemos un ndice sobre esa columna ttulo sabiendo que la
longitud media de un ttulo son unos 30 bytes y que nuestro catlogo tiene unos cien mil
registros implica que el ndice puede ocupar unos 3Gbytes (probablemente no ocupara
tanto porque MySQL incorpora algoritmos de compresin para reducir el tamao de los
ndices).
Si en lugar de crear un ndice por todo el titulo lo creamos por los seis primeros caracteres
mediante un ndice parcial podemos hacer que el tamao final del ndice se reduzca en una
quinta parte.

ALTER TABLE libros ADD INDEX (titulo(6));

El inconveniente de los ndices parciales es que retornan a veces ms filas de las deseadas
que luego la consulta debe descartar porque no coinciden con lo que se esta buscando. Si
seguimos con el ejemplo anterior la siguiente consulta:

SELECT * FROM libros WHERE lower(titulo) =la casa encantada

Hace que el ndice retorne todos los ttulos que comiencen por la cas como la casa
de los espritus o la casqueria fina, filas que posteriormente la consulta debe
descartar.

Cuando queremos crear un ndice para una columna tipo TEXT o BLOB, en ese caso, es
necesario que el ndice sea de tipo parcial. Por ejemplo:

CREATE TABLE pruebablob (col_blob BLOB, INDEX(col_blob(15));

Pgina 4
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4.2.2 ndices multicolumna (ndices compuestos).


Este tipo de columnas mltiples se justifica cuando sobre una tabla es muy frecuente
realizar consultas sobre varios atributos simultneamente.
Por ejemplo si en mi tabla clientes son muy frecuentes las bsquedas por los atributos
apellido y nombre simultneamente podra crear un ndice multicolumna haciendo:

ALTER TABLE clientes ADD INDEX (apellido(6),nombre(6));

Para no hacer el ndice muy grande se indexan nicamente los seis primeros bytes de cada
atributo.

Si efecto consultas del tipo:

SELECT * FROM clientes WHERE apellido=Rodrguez and nombre=Ramiro;

Es cuando el resultado es generado muy rpidamente gracias al ndice multicolumna.

Es importante sealar que un ndice multicolumna no es equivalente a crear un ndice por


separado para cada una de las columnas ya que MySQL solo es capaz de usar en una
consulta un ndice por cada tabla empleada en dicha consulta.

Tambin es muy importante para este tipo de ndices el orden en que se colocan los
atributos. En el caso del ejemplo una consulta del tipo:

SELECT * FROM clientes WHERE apellido=Rodrguez;

Se beneficia del ndice creado, pero una consulta del tipo:

SELECT * FROM clientes WHERE nombre=Ramiro;

Requiere hacer un escaneo completo de la tabla puesto que todas las entradas del ndice se
inician con apellido y solo a continuacin de dicha entrada se incluye el contenido de
nombre.

4.2.3 ndices Full-Text.


Los ndices full-text solo son aplicables a tablas tipo MyISAM y sirven para localizar
rpidamente palabras en columnas tipo CHAR,VARCHAR o TEXT. Como estos ndices
almacenan un registro por cada palabra encontrada en cada campo indexado significa que
son ndices que consumen mucho espacio de almacenamiento pero que se ve compensado
por el gran aumento de velocidad en las bsquedas.

Vamos a ver con un ejemplo la diferencia de realizar una bsqueda de una palabra en un
texto largo primero sin tener definido un ndice full-text y posteriormente con un ndice
full-text aadido a la tabla.

En nuestra base de datos curso tenemos la tabla aforismos donde se han recopilado frases
celebres de distintos personajes. La tabla tiene cien mil registros creados con la utilidad
Datagen (ver Anexo 1. Uso de la herramienta DataGen para insertar mltiples filas en

Pgina 5
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

tablas de prueba), evidentemente hay un montn de frases repetidas pero para el ejemplo
no es relevante, y tiene la siguiente estructura:
CREATE TABLE `aforismos` (
`id` int(10) unsigned NOT NULL auto_increment,
`fecha` date default NULL,
`cuerpo_carta` varchar(350) default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM

Se desea localizar cuantos aforismos incluyen la palabra Unamuno como se supone que
no tenemos aun un ndice full-text definido sobre el atributo tenemos que usar el operador
LIKE

select count(*) from aforismos where lower(frase) LIKE '%unamuno%';


+----------+
| count(*) |
+----------+
| 458 |
+----------+
1 row in set (0.23 sec)

El siguiente paso es crear el ndice full-text para ver la diferencia:


mysql> alter table aforismos add fulltext index (frase);
Query OK, 100000 rows affected (9.23 sec)
Records: 100000 Duplicates: 0 Warnings: 0

Para usar un ndice full-text es necesario utilizar la funcin MATCH (campo) AGAINST
(palabras). En el ejemplo la consulta ser:

mysql> select count(*) from aforismos where match(frase)


against('unamuno');
+----------+
| count(*) |
+----------+
| 458 |
+----------+
1 row in set (0.01 sec)

Se observa la diferencia en el tiempo de respuesta. Esta diferencia se amplia si en lugar de


una palabra buscamos, por ejemplo, cuantas frases de la tabla son de Unamuno o Machado.

mysql> select count(*) from aforismos where lower(frase) LIKE '%unamuno%'


or lower(frase) LIKE '%machado%' ;
+----------+
| count(*) |
+----------+
| 758 |
+----------+
1 row in set (0.52 sec)
mysql> select count(*) from aforismos where match(frase) against('unamuno
machado');
+----------+
| count(*) |
+----------+
| 758 |
+----------+
1 row in set (0.02 sec)

Pgina 6
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4.2.4 Los ndices y el operador LIKE.


Las consultas con el operador LIKE pueden hacer uso del ndice creado sobre un atributo
siempre que el argumento que acompaa a LIKE sea una cadena que no empiece por un
carcter comodn. Es decir la siguiente consulta puede hacer uso del ndice creado sobre la
columna col1:

SELECT * FROM mi_tabla WHERE col1 LIKE hola%;

Por el contrario la siguiente consulta no puede hacer uso del ndice y requiere escanear por
completo la tabla:

SELECT * FROM mi_tabla WHERE col1 LIKE %hola%;

A partir de la versin MySQL 5 las consultas del tipo LIKE %cadena%, siempre que
cadena sea mayor de 3 caracteres, son resueltas mediante el algoritmo Turbo Boyer-Moore,
lo que permite incrementar la velocidad de respuesta en este tipo de consultas.

4.2.5 Los ndices y la cardinalidad.


La cardinalidad de un ndice indica el grado de duplicidad de valores repetidos que tiene el
atributo (simple o compuesto) sobre el que est construido. En los ndices de tipo
PRIMARY la cardinalidad coincide con el nmero de filas de la tabla puesto que no hay
valores repetidos ni valores nulos. En los ndices tipo UNIQUE la cardinalidad suele ser un
valor cercano al nmero de filas de la tabla puesto que estos ndices si que pueden indexar
valores nulos.
Es en los ndices ordinarios donde la cardinalidad puede ser muy variable. En muchos casos
es preferible no tener un ndice que tener uno con cardinalidad baja. Por ejemplo si tenemos
una tabla con una columna de tipo ENUM no tiene mucho sentido definir un ndice para
dicha columna puesto que su cardinalidad ser con toda probabilidad muy baja con respecto
al nmero de registros de la tabla.

Pgina 7
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4. 3 Rendimiento y optimizacin de las consultas.


4.3.1 Detectar las consultas lentas. Activar el slow query
log.
Uno de los pasos ms importantes a la hora de optimizar y afinar MySQL consiste en
localizar las consultas problemticas, es decir aquellas que llevan mucho tiempo de
procesamiento hasta generar la respuesta y se identifican como slow queries.
La forma ms cmoda, desde el punto de vista del administrador, que ofrece el servidor
MySQL para detectar este tipo de consultas es activar lo que se conoce como el slow query
log. Este tipo de log no viene activado en la configuracin por defecto de MySQL y es
necesario realizar las siguientes entradas en el fichero de configuracin (my.ini o my.cnf)
para que se active la prxima vez que se reinicie el servidor:
[mysqld]
long_query_time = 5
log-slow-queries = /var/log/mysql/mysql-slow.log

Una vez activado, y segn el ejemplo anterior, todas las consultas que superen los 5
segundos de tiempo de respuesta van a quedar registradas en el fichero mysql-slow.log y
por lo tanto fcilmente detectadas por el administrador.

Veamos el siguiente extracto del fichero slow query log:


.
.
.
# Time: 061122 23:19:37
# User@Host: root[root] @ localhost [127.0.0.1]
# Query_time: 790 Lock_time: 0 Rows_sent: 39 Rows_examined: 2123567
select descripcion,direccion,cant
from articulo,almacen,stock
where articulo.id_art=stock.id_art and
almacen.id_alm=stock.id_alm and almacen.telefono='029-8661395';
.
.
.

En este extracto se puede observar la consulta que ha quedado registrada, el nmero de


registros que han sido examinados (2123567 registros!), el tiempo empleado ha sido 790
segundos (algo ms de 13 minutos!) Es evidente que si esta consulta se puede repetir en
ms ocasiones requiere una urgente optimizacin.

El problema con este log es que si hay mucho trfico en el servidor es probable que tambin
haya muchas consultas consideradas lentas, entonces el log crece rpidamente y se hace
difcil extraer de l informacin til. Lo ms importante es extraer del log las consultas
lentas con mayor nmero de ocurrencias y tambin las consultas con ms tiempo de
procesamiento para proceder a su optimizacin (siempre que sea posible).

Hay un script de Perl que viene con la distribucin de MySQL que se llama mysqldumpslow
y que puede ser muy til para extraer informacin del slow query log cuando este ha crecido
y ha alcanzado un tamao considerable. Por ejemplo si queremos que mysqldumpslow

Pgina 8
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

extraiga del fichero log ordenado por nmero de ocurrencias las cinco consultas lentas que
ms veces aparecen en el fichero slow query log debemos lanzar el siguiente comando
desde la consola:
C:\wamp\mysql\scripts>perl mysqldumpslow -t 1 C:\wamp\logs\mysql-slow.log

Nota: Conviene observar que este ejemplo se esta realizando en un servidor MySQL bajo Windows por lo
que necesita tener instalado un interprete de Perl para Windows (ver Anexo 2 de este documento: Instalar
interprete de Perl para Windows ActivePerl).

4.3.2 Optimizacin de consultas mediante la sentencia


EXPLAIN.
La sentencia Explain es til para averiguar como MySQL va a proceder a ejecutar la
consulta mostrando al usuario el plan de ejecucin de dicha consulta.

El primer ejemplo del uso de Explain lo vamos a ver usando una consulta muy rpida sobre
la tabla libros de la base de datos curso para obtener informacin de un registro concreto
conocido el valor de su clave primaria:

mysql> select titulo,autor from libros where id=9012\G


*************************** 1. row ***************************
titulo: Mariposas nocturnas y diurnas : conocer y clasificar las
mariposas ms importantes de Europa
autor: Helga Hofmann, Thomas Marktanner Hoffmann, Helga
Marktanner, Thomas
1 row in set (0.00 sec)

MySQL sabe antes de ejecutar la consulta anterior que solo puede haber un registro de
salida a lo sumo, si es que existe ese valor de la clave primaria. La informacin que genera
Explain sobre esta consulta es la siguiente:

mysql> explain select titulo,autor from libros where id=9012\G


*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: libros
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: const
rows: 1
Extra:
1 row in set (0.00 sec)

Los campos de informacin que proporciona el comando Explain son, brevemente


comentados, los siguientes:
id: El identificador de la salida generada por Explain. Explain genera un registro
por cada tabla involucrada en la consulta.
select_type: Los posibles valores de este campo son SIMPLE, PRIMARY, UNION,
DEPENDENT UNION, SUBSELECT, y DERIVED. Dependiendo del tipo de

Pgina 9
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

construccin de la consulta llevar uno u otro. En el ejemplo anterior es SIMPLE


porque no se utilizan subconsultas ni uniones ni joins.
table: El nombre de la tabla de la que se estn leyendo los registros.
type: Indica el tipo de join de la consulta. Puede tomar los valores const,
eq_ref, ref, range, index, o ALL . Vamos a comentar brevemente estos
valores por la importancia que tiene una mala eleccin de join sobre el rendimiento
de la consulta:
ALL. Significa que es necesario recorrer y leer todos los registros de la tabla
para satisfacer la consulta. Suele suceder cuando no hay ningn tipo de
ndice asociado o buscamos subcadenas que no pueden ser indexadas. Es
importante evitar este tipo de consultas en tablas con elevado nmero de
registros y ms aun si en la consulta hay joins con otra tablas. Por ejemplo:
select titulo from libros
where autor like '%CERVANTES%'

const. Cuando es posible utilizar un nico valor de clave primaria o ndice


tipo UNIQUE para resolver la consulta. En el ejemplo anterior (select
titulo,autor from libros where id=9012) el type es const por que
hay solo un valor constante en la consulta que es el valor 9012.

range. Cuando para resolver la consulta se utilizan ndices para localizar


datos entre un determinado rango de valores. La eficiencia de este tipo de
consultas depende de la amplitud del rango y de la cardinalidad del ndice.
(Ya sabemos que un ndice de baja cardinalidad indica que muchos registros
estn asociados a un mismo valor del ndice):
select titulo from libros
where id between 6321 and 6327;

index. Cuando sobre una tabla hay un ndice asociado y MySQL detecta que
es capaz de responder la consulta nicamente a partir de la informacin del
propio ndice sin necesidad de recuperar datos de la tabla asociada al ndice.
Por ejemplo si suponemos que a la tabla libros se le ha aadido el siguiente
ndice:
mysql> ALTER TABLE libros ADD INDEX (titulo);
MySQL es ahora capaz de responder a la siguiente consulta solo accediendo
al ndice recin creado:
select titulo from libros
where UPPER(titulo) like'%QUIJOTE%';
La principal ventaja de este tipo de bsqueda es que el ndice es ms
pequeo que la tabla. Si la consulta se modifica aadiendo columnas que ya
no estn en el ndice MySQL esta obligado a leer la tabla completa y el type
pasara a ALL.
select titulo,autor from libros
where UPPER(titulo) like'%QUIJOTE%';

ref / ref_or_null. Cuando para la bsqueda se utiliza un ndice ordinario


que puede tener valores duplicados. En el siguiente ejemplo se hace una
bsqueda de poblacin introduciendo un cdigo postal concreto. La tabla
codigospostales tiene un ndice asociado a este campo que es un ndice
ordinario (el cdigo postal no puede ser clave primaria en la tabla por que
varias poblaciones pueden pertenecer al mismo cdigo).
select poblacion from codigospostales

Pgina 10
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

where cod_pos='300001';
Si la sentencia Explain retorna ref_or_null es porque tambin se estn
buscando registros asociados a los valores nulos del ndice. Por ejemplo:
select poblacion from codigospostales
where cod_pos='300001' or cod_pos is null;

eq_ref. Este tipo de busqueda indica que hay un join establecido y que la
tabla marcada como eq_ref por la sentencia Explain avisa que est usando
una clave primaria o ndice tipo unique para resolver la consulta de forma
mucho ms rpida. Por ejemplo si queremos saber los ttulos de las
bibliotecas que tienen como nombre Universidad Carlos III necesitamos
hacer un join de las tres tablas de esta forma.
select titulo from ejemplares e,bibliotecas b,libros l
where upper(nombre) like '%UNIVERSIDAD CARLOS III%' AND
e.id_bib=b.id and e.id_tit=l.id
Si se observa la salida generada por Explain para esta sentencia se ve que se
generan tres registros uno por cada tabla implicada:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: b
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 70
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: e
type: ref
possible_keys: fk_ejemplares1,fk_ejemplares2
key: fk_ejemplares2
key_len: 5
ref: curso.b.id
rows: 425
Extra: Using where
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: l
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: curso.e.id_tit
rows: 1
Extra: Using where
El primer registro marca el type para la tabla bibliotecas como ALL, es
decir debe leer toda la tabla bibliotecas para obtener los registros.
El segundo registro marca el type para la tabla ejemplares como ref
porque utiliza para el join, como referencia, la clave ajena que apunta a la
tabla bibliotecas. Ese ndice no es un ndice nico para la tabla ejemplares
puesto que esta repetido en muchos registros.

Pgina 11
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Por ltimo, y es a donde queramos llegar, el tercer registro marca el type de


la tabla libros como eq_ref porque el ndice usado es la clave primaria de
la tabla y por lo tanto es nico, es decir, solo hay un posible registro de la
tabla libros una vez determinado el ejemplar.
possible_keys: El listado de posibles ndices que pueden intervenir en la consulta,
o NULL si no hay ninguno posible.
key: El nombre del ndice que MySQL ha decidido usar para resolver la consulta.
Debe encontrarse entre el listado de possible_keys anterior y se supone que es el
ms adecuado.
key_len: El tamao en bytes del ndice seleccionado.
ref: El campo ref normalmente solo toma valor distinto de NULL cuando el campo
type indica que la consulta es ref o eq_ref (normalmente en las consultas tipo join
donde hay involucradas claves ajenas). En este caso el campo ref muestra que
columnas son usadas para seleccionar las filas de la tabla o bien si es un valor
constante el usado en su lugar. En el siguiente ejemplo vamos a obtener los cdigos
de los ejemplares que se encuentran en las bibliotecas de la ciudad de Salamanca.
select id_tit from bibliotecas b,ejemplares e
where upper(ciudad) like '%SALAMANCA%' and b.id=e.id_bib
Si se observa la sentencia Explain que se genera vemos que el registro debido a la
tabla ejemplares tiene un campo type con valor ref y el campo ref tiene el valor
correspondiente a la columna de la tabla biblioteca a la que apunta una de las
claves ajenas de esta tabla es decir biblioteca.id.
rows: Antes de ejecutar la consulta MySQL estima en este campo el nmero de filas
que va a generar, conviene prestar atencin cuando el valor de rows es muy alto,
normalmente esta indicando que la consulta requiere un escaneo total de tabla (suele
ir asociado con un valor del campo type como ALL). Para que las estadsticas que
genera MySQL y en las que se basa la estimacin del valor del campo rows sean
vlidas conviene que esten actualizadas,para ello es necesario ejecutar de forma ms
o menos periodica las sentencias ANALYZE TABLE o OPTIMIZE TABLE.
Extra: Informacin adicional que muestra MySQL sobre la consulta. Los valores
ms comunes de este campo suelen ser:
Using where. Cuando la consulta tiene una clusula WHERE.
Using filesort. Cuando la consulta requiere una salida ordenada mediante
ORDER BY sobre una columna no indexada o bien estando indexada pero
con un ndice que no ha sido usado para extraer las filas en dicha consulta.
Using index. Cuando los datos de la consulta se obtienen leyendo
nicamente informacin del ndice sin necesidad de leer registros de la tabla
(est muy asociado este comentario con el type index).
Using intersect (solo para versiones posteriores a MySQL 5.0). Una
mejora que incorpora MySQL 5.0 sobre las versiones anteriores es la
capacidad de usar ms de un ndice para resolver una consulta sobre una
tabla concreta que debe cumplir una doble condicin enlazada por el
operador AND. Cada uno de los ndices esta asociado a cada una de las
condiciones de la clausula WHILE. En el siguiente apartado de este
documento 4.3.3 Ejemplos del uso de la sentencia Explain se comenta una
consulta donde aparece Using intersect.
Using union (solo para versiones posteriores a MySQL 5.0). Similar a
using instersect con la diferencia que se emplea un OR para enlazar la
doble condicin que permite usar dos ndices simultneamente.

Pgina 12
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4.3.3 Ejemplos del uso de la sentencia EXPLAIN.


Ejemplo1. En el siguiente ejemplo de Explain vamos a realizar una consulta para obtener
informacin a partir de un rango de valores de la clave primaria.

mysql> select titulo from libros where id between 6321 and 6327;
+-----------------------------------+
| titulo |
+-----------------------------------+
| Psicologia social |
| Anatomia general |
| Viajes por Marruecos |
| Los siete pilares de la sabiduria |
| La Ilustracion en Espaa |
| Delphi 3.0 |
+-----------------------------------+
6 rows in set (0.25 sec)

mysql> explain select titulo from libros where id between 6321


and 6327\G

*************************** 1. row ***************************


id: 1
select_type: SIMPLE
table: libros
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 7
Extra: Using where
1 row in set (0.00 sec)

Podemos observar la diferencia entre buscar en un rango o buscar un valor nico. El campo
type pasa de valer de const a range, lo que indica que esta buscando no un nico valor
sino un rango de ellos. Tambin se observa que el campo rows vale 7, aunque la consulta
devuelve 6 filas. Esto se debe a que el campo rows es obtenido a partir de una estimacin
de las filas que puede generar esa consulta. Lo que hace MySQL es estimar cuantas filas en
una clave autonumrica puede haber entre los valores 6327 y 6321 y el resultado es 7
aunque su valor real almacenado sea 6 puesto que el registro con la clave 6325 no aparece
en la tabla.

Ejemplo 2. El siguiente ejemplo va a ser realizar una consulta buscando valores de una
columna que no tiene ndice asociado.
mysql> select count(*) from libros where numpaginas>700;
+----------+
| count(*) |
+----------+
| 3738 |
+----------+
1 row in set (0.41 sec)

Pgina 13
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

mysql> explain select count(*) from libros where numpaginas>700\G


*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: libros
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 10009
Extra: Using where
1 row in set (0.00 sec)

Se observa que el valor NULL en el campo key indica que no est utilizando ningn ndice
para resolver la cnsulta y el campo posible_keys ndica que no existe ningn ndice
posible para resolverla. Tambin conviene fijarse que el campo type indica ALL, es decir, el
rango de bsqueda de posibles valores se extiende a toda la tabla.

Vamos, a continuacin, a crear un ndice sobre la columna numpaginas para comprobar


como la consulta utiliza dicho ndice, debido a ello mejora considerablemente su velocidad
de respuesta y Explain nos va a confirmar que, efectivamente, el ndice creado es utilizado
por la consulta.

mysql> ALTER TABLE libros ADD INDEX (numpaginas);


Query OK, 9918 rows affected (0.77 sec)
Records: 9918 Duplicates: 0 Warnings: 0

mysql> select SQL_NO_CACHE count(*) from libros where


numpaginas>700;
+----------+
| count(*) |
+----------+
| 3738 |
+----------+
1 row in set (0.01 sec)

mysql> explain select count(*) from libros where numpaginas>700\G


*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: libros
type: range
possible_keys: NUMPAGINAS
key: NUMPAGINAS
key_len: 5
ref: NULL
rows: 5044
Extra: Using where; Using index
1 row in set (0.00 sec)

Se observa como la velocidad de ejecucin se ha incrementado de 0.41 segundos a 0.01


segundos, tambin Explain indica que ha utilizado el ndice NUMPAGINAS, ha cambiado el
campo type a range al estar la columna indexada. Es interesante fijarse en la diferencia
entre el nmero de filas estimadas 5044 y el nmero de filas reales 3738, es posible que
MySQL sea capaz de mejorar esas estimaciones una vez ejecutado ANALIZE TABLE sobre la
tabla libros.

Pgina 14
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Ejemplo 3. Vamos a ver otro ejemplo de Explain en el que se va a efectuar una consulta
basada en dos columnas indexadas. MySQL buscar seleccionar de los dos posibles ndices
aquel que menos filas tenga que examinar.

mysql> select count(*) from libros where numpaginas >998 and id <
6000;
+----------+
| count(*) |
+----------+
| 5 |
+----------+
1 row in set (0.03 sec)

mysql> explain select count(*) from libros where numpaginas >998


and id < 6000\G

*************************** 1. row ***************************


id: 1
select_type: SIMPLE
table: libros
type: range
possible_keys: PRIMARY,NUMPAGINAS
key: NUMPAGINAS
key_len: 5
ref: NULL
rows: 9
Extra: Using where; Using index
1 row in set (0.00 sec)

Se observa que en este caso MySQL ha optado entre PRIMARY y NUMPAGINAS por
NUMPAGINAS porque ha detectado que el valor 998 est casi en uno de los extremos
superiores del ndice y van a ser muy pocas filas las que necesite examinar.

Si cambiamos el valor de 998 por el de 220 vemos que MySQL opta por cambiar el ndice
elegido para la consulta porque detecta que prcticamente todas las filas de la tabla tienen
un valor superior a 220 en la columna NUMPAGINAS.

mysql> select count(*) from libros where numpaginas >220 and id <
6000;
+----------+
| count(*) |
+----------+
| 5857 |
+----------+
1 row in set (0.02 sec)

mysql> explain select count(*) from libros where numpaginas >220


and id < 6000\G

*************************** 1. row ***************************


id: 1
select_type: SIMPLE
table: libros
type: range
possible_keys: PRIMARY,NUMPAGINAS
key: PRIMARY
key_len: 4

Pgina 15
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

ref: NULL
rows: 5044
Extra: Using where
1 row in set (0.00 sec)

Ejemplo 4. Utilizar la combinacin de dos ndices simultneamente.


Por ejemplo suponemos que queremos lanzar la siguiente consulta:
select * from libros where id between 1000 and 2000
and numpaginas=300;
Vemos que en esta consulta se pueden usar dos ndices simultneamente para resolver la
consulta, la clave primaria id y la columna numpaginas que tambin esta indexada como se
vio en el ejemplo anterior. El resultado de lanzar la sentencia Explain a esta consulta
genera la siguiente salida:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: libros
type: index_merge
possible_keys: PRIMARY,NUMPAGINAS
key: NUMPAGINAS,PRIMARY
key_len: 5,4
ref: NULL
rows: 3
Extra: Using intersect(NUMPAGINAS,PRIMARY); Using where
1 row in set (0.34 sec)

Esta posibilidad, como ya se ha comentado, solo es posible a partir de la versin MySQL


5.0 cuando el campo Extra, como en este ejemplo, incluye el mensaje Using intersect
lo cual significa que MySQL esta usando un algoritmo index_merge utilizando los dos
ndices para resolver una consulta y al final estableciendo un merge con los resultados de la
interseccin generada por los registros devueltos por cada uno de los ndices asociados a
cada condicin WHERE de la sentencia select.

4.3.4 Comando ANALYZE TABLE.


Este comando sirve para actualizar las estadsticas de una tabla y todos sus ndices. Durante
el anlisis la tabla es bloqueada en modo read lock. El comando funciona solo para tablas
de tipo MyISAM, BDB e InnoDB. La informacin que genera este comando la usa MySQL
para decidir, por ejemplo, el orden de los joins entre varias tablas cuando se efecta una
consulta de este tipo.

Es aconsejable ejecutar este comando peridicamente sobre todo para tablas que tienen
frecuentes modificaciones en sus datos que afectan a las columnas indexadas.

Mediante el uso del comando SHOW INDEX podemos observar los cambios en la informacin
relativa a los ndices que genera el comando ANALYZE TABLE.

Veamos el siguiente ejemplo sobre una tabla concreta libros que tiene dos ndices
asociados, su clave primaria y numpaginas.

mysql> show index from libros \G

Pgina 16
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

*************************** 1. row ***************************


Table: libros
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: id
Collation: A
Cardinality: 9558
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
*************************** 2. row ***************************
Table: libros
Non_unique: 1
Key_name: NUMPAGINAS
Seq_in_index: 1
Column_name: NUMPAGINAS
Collation: A
Cardinality: 1593
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
2 rows in set (0.22 sec)

Ejecutamos el comando ANALYZE TABLE sobre esta tabla


mysql> analyze table libros;
+--------------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+--------------+---------+----------+----------+
| curso.libros | analyze | status | OK |
+--------------+---------+----------+----------+
1 row in set (0.04 sec)

Volvemos a lanzar de nuevo el comando SHOW INDEX para observar como ha cambiado la
informacin relativa a los ndices. El cambio afecta fundamentalmente al atributo
Cardinality, que se refiere al nmero estimado de valores distintos en el ndice (ojo con
la palabra estimado, porque no tiene por qu coincidir con el valor real en un momento
dado).
mysql> show index from libros \G
*************************** 1. row ***************************
Table: libros
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: id
Collation: A
Cardinality: 10182
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
*************************** 2. row ***************************
Table: libros

Pgina 17
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Non_unique: 1
Key_name: NUMPAGINAS
Seq_in_index: 1
Column_name: NUMPAGINAS
Collation: A
Cardinality: 1697
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
2 rows in set (0.37 sec)

4.3.5 Comando OPTIMIZE TABLE.

Este comando debe ser usado cuando se han eliminado gran parte de las filas de una tabla o
se han efectuado numerosas modificaciones en tablas con columnas tipo VARCHAR, BLOB o
TEXT. Podemos decir que este comando equivale a desfragmentar el espacio de datos de una
tabla. Solo funciona en tablas del tipo MyISAM, BDB e InnoDB.

OPTIMIZE TABLE tambin bloquea la tabla durante su ejecucin, como hace ANALYZE
TABLE, la diferencia es que OPTIMIZE TABLE es mucho ms lento puesto que tiene que
reallocar los datos de la tabla para hacer la desfragmentacin por lo tanto es importante
saber cuando se va a lanzar el proceso de optimizacin. Lo habitual seria una vez a la
semana o al mes y solo para ciertas tablas.

Veamos un ejemplo guiado paso a paso para ver los efectos de usar el comando OPTIMIZE
TABLE.

1. Se crea una tabla nueva para el ejemplo:


create table ejemplo_optimize (
campo1 int auto_increment primary key,
campo2 varchar(50),
campo3 varchar(100))engine=myisam;

2. Insertamos valores aleatorios en las columnas campo2 y campo3 hasta tener unos 100.000
registros en la tabla. (Se ha usado la utilidad Datagen para insertar mltiples filas en una
tabla. Para ver el uso de la utilidad Datagen consultar el Apndice 1: Uso de la
herramienta DataGen para insertar mltiples filas en tablas de prueba).

3. Se crea un ndice sobre la columna campo3:


create index indice1 on ejemplo_optimize(campo3);

4. Vemos los datos de tamao de la tabla mediante el comando show status:


mysql> show table status like '%ejemplo_opt%'\G
*************************** 1. row ***************************
Name: ejemplo_optimize
Engine: MyISAM
Version: 10
Row_format: Dynamic
Rows: 100000

Pgina 18
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Avg_row_length: 39
Data_length: 3928312
Max_data_length: 281474976710655
Index_length: 1029120
Data_free: 0
Auto_increment: 110001
Create_time: 2006-06-15 13:21:16
Update_time: 2006-06-15 13:25:56
Check_time: NULL
Collation: latin1_swedish_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)

5. Hacemos un borrado mltiple y discontinuo de registros. Para ello mediante la siguiente


sentencia podemos borrar uno de cada cuatro registros :
mysql> delete from ejemplo_optimize where mod(campo1,4)=0;
Query OK, 25000 rows affected (0.60 sec)

6. El tamao de la tabla es el siguiente despus del borrado:


Rows: 75000
Avg_row_length: 39
Data_length: 3928312
Max_data_length: 281474976710655
Index_length: 1029120
Data_free: 983608

Donde se observa que el tamao total no ha variado ni tampoco el tamao del ndice

7. Volvemos a insertar 25000 nuevos registros en la tabla nuevos usando la utilidad


Datagen. Volvemos a tener por lo tanto 100000 registros. El tamao de la tabla y el ndice
indican lo siguiente:

Rows: 100000
Avg_row_length: 42
Data_length: 4224160
Max_data_length: 281474976710655
Index_length: 1286144
Data_free: 0

8. Optimizamos la tabla
mysql>optimize table ejemplo_optimize;

9. Para finalizar observamos que los datos una vez optimizada son los siguientes:
Rows: 100000
Avg_row_length: 39
Data_length: 3927044
Max_data_length: 281474976710655
Index_length: 1022976
Data_free: 0

Ha disminuido el tamao total ocupado por los datos y el tamao ocupado por el ndice.

Pgina 19
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4.4 Gestin de la memoria en el Servidor.


Como ya se sabe toda la gestin de memoria en un servidor de base de datos se centra en
evitar lo ms posible el acceso a disco intentando realizar la mayor cantidad de operaciones
exclusivamente en accesos a RAM. Los siguientes apartados pretenden explorar algunos
aspectos que permiten optimizar el uso de la memoria RAM y minimizar en lo posible los
accesos a disco.

4.4.1 Ajustar las propiedades de memoria de los threads


que genera MySQL.
Un thread es un hilo de ejecucin que genera MySQL cada vez que un nuevo cliente se
conecta al servidor. Si lanzamos la siguiente sentencia podemos observar las dos variables
que el administrador puede manipular para optimizar la gestin de la memoria en lo que se
refiere a los hilos de ejecucin.

mysql> show variables like 'thread%';


+-------------------+--------+
| Variable_name | Value |
+-------------------+--------+
| thread_cache_size | 12 |
| thread_stack | 196608 |
+-------------------+--------+

La variable thread_stack indica la cantidad de memoria que cada thread tiene


garantizada por el sistema cuando se inicia. La variable thread_cache_size el nmero de
threads que se mantienen vivos en memoria cach esperando nuevas conexiones.

Por ejemplo, en un entorno de conexin a un servidor MySQL a travs de un servidor web


que genera muchas conexiones a la base de datos de muy corta duracin, puede ser
interesante para el administrador, incrementar el valor de thread_cache_size para ahorrar de
esta manera el tiempo de creacin de nuevos threads.

Para observar el nmero de trheads que est generando nuestro servidor MySQL
lo vemos fcilmente mediante SHOW STATUS.
mysql> show status like '%thread%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| Delayed_insert_threads | 0 |
| Slow_launch_threads | 0 |
| Threads_cached | 10 |
| Threads_connected | 2 |
| Threads_created | 14 |
| Threads_running | 1 |
+------------------------+-------+

En este ejemplo se ve que el servidor ha creado catorce threads, aunque solo hay dos
threads conectados y por lo tanto quedan diez esperando en cach.

Pgina 20
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4.4 2 Utilizacin siempre que sea posible de las tablas tipo


MEMORY.
MySQL incorpora la posibilidad de crear tablas de almacenamiento solo en RAM usando la
arquitectura de almacenamiento conocida como MEMORY (o HEAP en versiones anteriores).
Este tipo de tablas tambin son usadas internamente por el servidor MySQL para la
creacin de tablas temporales en la resolucin, por ejemplo, de consultas complejas.

El administrador puede modificar el valor de la variable max_heap_table_size, que


determina el tamao mximo que puede tener una tabla de tipo MEMORY . Si alguna
aplicacin genera un error del tipo
The table 'message_buffer_mem' is full,S1000,1114
es cuando el administrador debera plantearse incrementar el valor de dicha variable.

4.4.3 Descripcin y uso de la Query Cache de MySQL.

Es importante entender las diferencias existentes entre los cuatro mecanismos de cach que
administra el servidor MySQL:

Buffer pool. Este cach almacena datos e ndices de aquellas tablas con arquitectura
de almacenamiento InnoDB.
Key cache. Este tipo de cach se utiliza para almacenar informacin de los ndices
de las tablas tipo MyISAM.
Memory pool. Este cach lo utiliza MySQL fundamentalmente para almacenar
informacin del diccionario de datos. Solo se benefician de este tipo de cach las
tablas tipo InnoDB.
Query cache. Este tipo de cach almacena consultas y resultados de consulta
realizada sobre cualquier tabla independientemente de la arquitectura de
almacenamiento de la misma.

La query cache de MySQL es un elemento opcional que el administrador puede o no tenerla


activada. Si est activada el servidor primero consulta la query cache para localizar tanto la
consulta lanzada como los resultados de la consulta sin necesidad de hacer ningn acceso a
disco. Localizar los datos en la query cache supone, por lo tanto, una importante mejora en
el rendimiento.

Tambin es importante saber que tipo de sucesos obligan a actualizar los datos de la query
cache:

Sentencias de consultas diferentes. MySQL no consulta la query cache si una


consulta no es exactamente igual a una ejecutada anteriormente. Para MySQL la
consulta:
SELECT * from mitabla;
Es diferente de la consulta:
select * from mitabla;

Pgina 21
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Finalizacin de transacciones. Cuando se lanza un commit (en tablas tipo InnoDB)


MySQL anula todos los datos de la query cache que hacen referencia a cualquiera de
las tablas que han sido afectadas por la transaccin.

Modificaciones en los datos o en las estructuras de tablas y/o ndices. En tablas con
alta carga transaccional esto sucede constantemente. Adems no solo se refrescan
las posibles filas afectadas por las modificaciones, es necesario refrescar todas las
filas de la query cache que hacen referencia a la tabla modificada, incluso las
posibles filas que no han sido modificadas.

Generacin de tablas temporales. Si la consulta realizada se ve obligada a generar


tablas temporales para obtener el resultado final en estos casos MySQL no puede
hacer uso de la query cache.

Teniendo en cuenta los puntos anteriores se evidencia que activar la Query cache es tanto
ms efectivo desde el punto de vista del rendimiento cuanto ms estables son las tablas
afectadas por las consultas.

Activacin de la Query cache.

Para activar la query cache basta con reservar un espacio de memoria para alojarla. Esto se
hace dando un valor a la variable query_cache size, bien usando el comando SET
GLOBAL o mejor desde el fichero de configuracin my.cnf (o my.ini en Windows).
query_cache_size=8M

Tambin es conveniente indicar mediante la variable query_cache_type cmo las


consultas van a acceder a la query cache. Hay tres posibilidades.

query_cache_type=0/OFF. Significa que ninguna consulta accede a la query cache.


Est reservada la memoria para la query cache pero ninguna consulta la utiliza.
query_cache_type=1/ON. Significa que el acceso a la query cache es implcito para
todas las consultas, a no ser que explcitamente se indique en la consulta que no se
desea usar la query cache, es decir colocando la opcin SQL_NO_CACHE en la
sentencia SELECT correspondiente.
SELECT SQL_NO_CACHE * FROM mitabla;

Esta opcin es la habitual y la que viene por defecto en la configuracin del servidor
MySQL. Es la ms adecuada en bases de datos con ms tablas estticas que
dinmicas.
query_cache_type=2/DEMAND. Significa que el acceso a la query cache debe ser
explcito para que se produzca, es decir, solo se beneficiarn del uso de la query
cache las sentencias que lleven la opcin SQL_CACHE.

SELECT SQL_CACHE * FROM mitabla;

Esta opcin es la adecuada en bases de datos con un elevado nmero de tablas muy
dinmicas sobre las que se hacen frecuentes consultas.

Pgina 22
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Monitorizar y optimizar la Query cache.


Para poder acceder al estado de la query cache en nuestro servidor MySQL lanzamos la
siguiente sentencia:

mysql> show status like '%qcache%';


+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 36 |
| Qcache_free_memory | 1379888 |
| Qcache_hits | 9570 |
| Qcache_inserts | 4312 |
| Qcache_lowmem_prunes | 14 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 1073 |
| Qcache_total_blocks | 2289 |
+-------------------------+---------+

El valor Qcache_hits indica el nmero de consultas que ha suministrado la query cache.


Un importante dato lo suministra Qcache_lowmem_prunes, que cuenta las consultas que
han debido ser eliminadas de la query cache para liberar espacio que permita aadir nuevas
consultas. Si este valor es habitualmente alto indica que la memoria asignada a la cach es
escasa. Los valores del ejemplo anterior muestran un estado de la query cache bastante
ptimo, el valor de Qcache_hits es bastante elevado mientras que Qcache_lowmem_prunes
es pequeo y por lo tanto no significativo.
En el siguiente ejemplo de datos se observa un escenario distinto donde la situacin se ha
degradado bastante:
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 215 |
| Qcache_free_memory | 1822323 |
| Qcache_hits | 7543 |
| Qcache_inserts | 19226 |
| Qcache_lowmem_prunes | 6211 |
| Qcache_not_cached | 2810 |
| Qcache_queries_in_cache | 398 |
| Qcache_total_blocks | 1603 |
+-------------------------+---------+

Se observa como ha descendido el ratio de hits sobre el total de las consultas que han
pasado por la query cache al tiempo que ha aumentado considerablemente el nmero de
consultas eliminadas por falta de espacio. Quiz el administrador en esta situacin debera
plantearse la necesidad de asignar ms espacio a la query cache al tiempo que debera tratar
de determinar que consultas, probablemente, no se van a beneficiar de la query cache para
aadir la opcin SQL_NO_CACHE en sus sentencias SELECT.

Pgina 23
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4.4.4 Descripcin y uso de la Cach de claves (Key


Cache) de MySQL.
En la arquitectura de tablas MyISAM se emplea un mecanismo para almacenar en cach los
bloques de ndices ms utilizados. Esta cach es accesible concurrentemente por distintas
conexiones de clientes (distintos threads).

Existe la posibilidad de crear varias cachs de claves distintas en lugar de la nica cach de
claves por defecto que configura MySQL. Se puede crear, por ejemplo, una cach de claves
para soportar los ndices de las tablas con elevada carga transaccional y alto contenido
dinmico y otro cach de claves distinto para las tablas ms estticas. Separar las cachs de
claves significa que distintos usuarios o aplicaciones no se interferirn con otros en el uso
concurrente del espacio de cach.

MySQL usa la cach de claves como todo mecanismo de cach. Cuando necesita acceder a
un bloque de ndice lo intenta localizar en la cach de claves antes de acceder a disco. Si lo
localiza lo lee, o escribe sobre l, dependiendo de la operacin que fuera a realizar. Si no lo
encuentra el servidor escoge un bloque de la cach conteniendo ndices de otra tabla y los
sustituye copiando sobre ellos los bloques de ndice de la tabla que ha tenido que cargar del
disco. Tan pronto como el bloque de ndice est en la cach de claves los datos de los
ndices ya pueden ser accedidos. La estrategia que utiliza MySQL para sustituir los bloques
es la llamada por sus siglas LRU (Last Recently Used), es decir mantiene una estructura de
lista en la que entra por la cola el ltimo bloque accedido, de tal manera que cuando
necesita sustituir un bloque siempre selecciona el que indica la cabeza de la lista.

Los bloques de ndices pueden ser modificados dentro de la cach de claves. Cuando un
bloque es modificado ya no se permite el acceso concurrente a ese bloque. El resto de
usuarios que necesiten usar ese bloque deben esperar hasta que el bloque modificado sea
escrito en disco.

Mltiples cachs de claves.


Como ya se ha comentado MySQL viene por defecto con una sola cach de claves, pero el
administrador tiene la opcin de asignar distintos ndices de tablas a distintas cachs de
claves para reducir conflictos de concurrencia a los mismos segmentos de datos entre
distintos threads.

Lo ms recomendable en servidores de datos de mucha carga y con muchos accesos de


clientes es establecer tres cachs de claves:
Una primera cache (hot_cache) reservada para los ndices de las tablas
MyISAM que generan muchas consultas pero muy poca modificacin. No debe
ocupar ms del 20% del espacio total reservado para las cachs de claves.
Una segunda cach (cold_cache) reservada para las tablas MyISAM que sufren
intensas modificaciones en sus datos. Tampoco debe superar ms de 20% del
espacio total reservado para las cachs de claves.
Una tercera que ser la cach de claves por defecto, que se usar para alojar los
ndices del resto de las tablas de tipo MyISAM de la base de datos.

Pgina 24
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Una de las formas de habilitar mltiples caches de claves es indicndoselo al servidor en el


fichero my.ini (o my.cnf). En el siguiente ejemplo se ve el extracto del fichero my.ini donde
se crean las tres cachs comentadas anteriormente:
# Size of the Key Buffer, used to cache index blocks for MyISAM tables.
# Do not set it larger than 30% of your available memory, as some memory
# is also required by the OS to cache rows. Even if you're not using
# MyISAM tables, you should still set it to 8-64M as it will also be
# used for internal temporary disk tables.
key_buffer_size=6M
hot_cache.key_buffer_size = 2M
cold_cache.key_buffer_size = 2M

La sentencia CACHE INDEX es la que permite asignar los ndices de distintas tablas a
distintas cachs de claves. Se van a asignar tres tablas de la base de datos curso a la cach
que hemos nombrado como hot_cache en el ejemplo:

mysql> CACHE INDEX clientes,reservas,viajes IN hot_cache;

+----------------+--------------------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+----------------+--------------------+----------+----------+
| curso.clientes | assign_to_keycache | status | OK |
| curso.reservas | assign_to_keycache | status | OK |
| curso.viajes | assign_to_keycache | status | OK |
+----------------+--------------------+----------+----------+
3 rows in set (0.01 sec)

Si se hace la asociacin de las tablas con las distintas cachs lanzando el comando CACHE
INDEX desde la consola lo que sucede es que, si reinicia el servidor, se pierde esta
asociacin. Lo ideal para que la asociacin se mantenga es crear un script de sql (llamado
por ejemplo mis_caches.sql) donde se incluyen las sentencias CACHE INDEX anteriores.

Script mis_caches.sql
CACHE INDEX curso.clientes,curso.reservas,curso.viajes IN hot_cache;
CACHE INDEX bd2.tabla1,bd2.tabla2 IN cold_cache;

Lo que se hace es incluir en el fichero de opciones my.ini la opcin init_file que permite
cargar el script anterior al arrancar el servidor.
.
key_buffer_size=6M
hot_cache.key_buffer_size = 2M
cold_cache.key_buffer_size = 2M
init_file=/path_a_la_carpeta_del_script/mis_caches.sql

4.4.5 Mejoras en los join, sort, y escaneo completo de


tablas
MySQL maneja unas variables que permiten asignar ms recursos de memoria para realizar
escaneos completos de tablas, para realizar joins sobre columnas no indexadas y para
realizar ordenaciones en clusulas GROUP BY o ORDER BY. Dichas variables son

Pgina 25
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

read_buffer_size, join_buffer_size y sort_buffer_size respectivamente. Asignar


ms valor a estas variables puede cambiar de forma sustancial los resultados, sobre todo
cuando se esta manejando gran cantidad de informacin. Normalmente lo ms aconsejable
es mantener para estas variables un valor medio/bajo para las sesiones por defecto y
modificar el valor de estas variables solo para aquellas sesiones que vayan a realizar alguna
operacin de este tipo que requiera condiciones especiales de memoria (mediante el uso de
SET SESSION). De esta manera no se desperdician grandes cantidades de memoria que, a la
larga, puedan perjudicar el rendimiento global de todo el sistema.

mysql> set session sort_buffer_size=20786400;


Query OK, 0 rows affected (0.00 sec)

En el siguiente ejemplo comentado se ve como puede variar el rendimiento de una


operacin modificando el valor de una de estas variables. Lo que se pretende es volcar la
informacin de una tabla sobre otra de idntica estructura pero realizando la insercin de los
registros ordenados por una determinada columna. Esto supone realizar un sort masivo de
datos y se puede observar como, cambiando el valor de la variable sort_buffer_size,
podemos disminuir considerablemente el tiempo de ejecucin de la sentencia.

1. Tenemos una tabla llamada ejemplosort que tiene la siguiente estructura:

CREATE TABLE pruebasort


(
id INT UNSIGNED NOT NULL,
fecha DATE NOT NULL,
id_cliente INT NOT NULL,
cantidad DECIMAL(5,2) NOT NULL,
descripcion varchar(100)
) ENGINE = MYISAM;

2. A esta tabla le insertamos, para poder realizar el ejemplo, cinco millones de registros.
(Ver el apndice Uso de la herramienta DataGen para insertar mltiples filas en tablas de
prueba ).

3. Creamos una nueva tabla llamada pruebasort2 con estructura de datos idntica a
pruebasort.

CREATE TABLE pruebasort2 LIKE pruebasort;

4. El siguiente paso es volcar la informacin de la tabla pruebasort a la tabla pruebasort2


pero realizando las inserciones ordenadas por la columna cantidad.

mysql> INSERT INTO pruebasort2 SELECT * FROM pruebasort ORDER BY


cantidad;
Query OK, 5000000 rows affected (15 min 35.28 sec)
Records: 5000000 Duplicates: 0 Warnings: 0

Se han necesitado ms de 15 minutos para completar la operacin.

5. Es interesante ver ahora las variables de status que afectan a la ordenacin para analizar
como se ha realizado el algoritmo de sort.

Pgina 26
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

mysql> show status like '%sort%';


+-------------------+---------+
| Variable_name | Value |
+-------------------+---------+
| Sort_merge_passes | 498 |
| Sort_range | 0 |
| Sort_rows | 5000000 |
| Sort_scan | 1 |
+-------------------+---------+

Lo que llama la atencin es que se han necesitado 498 procesos merge para completar el
sort (es una cifra considerablemente alta). Cuando esta cifra es elevada es cuando el
administrador debe considerar el incrementar la variable sort_buffer_size.

6. Se reinicia el servidor mysqld para inicializar los valores de estatus.

7. Se elimina y vuelve a crear la tabla para repetir el proceso de nuevo.

drop table pruebasort2;


CREATE TABLE pruebasort2 LIKE pruebasort;

8. Se cambia el tamao de la variable sort_buffer_size incrementndolo 100 veces.

mysql>set session sort_buffer_size=20786400;

9. Lanzamos de nuevo el INSERT con los cinco millones de registros ordenados.

mysql> INSERT INTO pruebasort2 SELECT * FROM pruebasort ORDER BY


cantidad;
Query OK, 5000000 rows affected (5 min 47.49 sec)
Records: 5000000 Duplicates: 0 Warnings: 0

El tiempo se ha divido prcticamente por tres, ha pasado de ms de 15 minutos a ms de 5


minutos.

10. Por ltimo, un vistazo a las variables de estatus para ver como se ha realizado el sort:

mysql> show status like '%sort%';


+-------------------+---------+
| Variable_name | Value |
+-------------------+---------+
| Sort_merge_passes | 5 |
| Sort_range | 0 |
| Sort_rows | 5000000 |
| Sort_scan | 1 |
+-------------------+---------+

Aqu es donde se ve el cambio ms drstico, que es el paso de 498 procesos merge a 5


procesos merge.

Pgina 27
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4. 5 Herramientas relacionadas con el


rendimiento.
4.5.1 Optimizacin de expresiones y funciones mediante
la funcin BENCHMARK().
Esta funcin es til para verificar el tiempo que emplea MySQL en calcular una
determinada funcin o expresin cuando puede ser utilizada de forma masiva.
La sintaxis es:

BENCHMARK(num_veces,expresin_funcion);

El uso de esta funcin se ve muy bien con un ejemplo sencillo. Vamos a calcular el tiempo
empleado en usar cien veces la funcin SYSDATE().
mysql> select benchmark(100,sysdate());
+--------------------------+
| benchmark(100,sysdate()) |
+--------------------------+
| 0 |
+--------------------------+
1 row in set (0.00 sec)

Se observa un tiempo despreciable. Ahora se llama cien mil veces a la misma funcin.

mysql> select benchmark(100000,sysdate());


+-----------------------------+
| benchmark(100000,sysdate()) |
+-----------------------------+
| 0 |
+-----------------------------+
1 row in set (0.38 sec)

El tiempo empleado ha sido de 0.38 segundos, y por ultimo un milln de veces.


mysql> select benchmark(1000000,sysdate());
+------------------------------+
| benchmark(1000000,sysdate()) |
+------------------------------+
| 0 |
+------------------------------+
1 row in set (2.60 sec)

Genera un tiempo de 2,60 segundos. En este caso se aprecia con SYSDATE() que
incrementando el nmero de veces que se ejecuta aumenta el tiempo de ejecucin de forma
lineal.

La funcin BENCHMARK() puede ser muy til para encontrar problemas o cuellos de botella
en consultas que se ejecutan muchas veces y tienen expresiones complejas en su interior. Es
aconsejable ejecutar BENCHMARK() algunas veces a distintas horas del da e interpretar el
resultado en el sentido del nivel de carga que tiene la mquina del servidor.

Pgina 28
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

4.5.2 Test de rendimiento (Benchmarking).


MySQL incorpora como parte de su distribucin una serie de test de rendimiento del
sistema de base de datos (MySQL Benchmark Suite). Los test de rendimiento son muy
tiles cuando se quieren comparar varios servidores de bases de datos o cuando se est
estudiando realizar un cambio en la configuracin de nuestro servidor de base de datos. En
el primer caso se lanzan los test de rendimiento en los distintos servidores y se comparan
los resultados; en el segundo caso se lanzan los test antes del cambio en la configuracin, se
efecta el cambio, y se vuelven a lanzar los test.

Para lanzar los test de rendimiento incorporados en la MySQL Benchmark Suite es


necesario contar con un intrprete de Perl que tenga instalado el paquete DBI para el acceso
a las bases de datos y adems el driver del tipo de base de datos que queremos probar. Es
decir, que estos test no solo sirven para probar bases de datos MySQL sino cualquier otro
tipo como PostgreSQL, Solid, etc., siempre que dispongamos del driver correspondiente.
Los sistemas operativos Windows no vienen con intrprete de Perl, es necesario instalarlo
previamente antes de lanzar los test de rendimiento. Para la instalacin de un interprete de
Perl en Windows consultar el Apndice 2: Instalar interprete de Perl para Windows.
ActivePerl.

Los test de prueba estn localizados en una carpeta de nombre bench ( o sql-bench) que se
encuentra bajo la carpeta de instalacin de MySQL y son los siguientes:

compare-results
copy-db
crash-me
print-limit-table
run-all-tests
server-cfg
test-alter-table
test-ATIS
test-big-tables
test-connect
test-create
test-insert
test-select
test-wisconsin

Ejecutar todos ellos en bloque supone lanzar el script run-all-tests, siempre teniendo muy
en cuenta que el tiempo de lanzar todos los test en bloque puede ser bastante elevado (entre
hora y 1 hora dependiendo del procesador).

Veamos un ejemplo de ejecucin del test que mide el rendimiento de consultas sobre tablas
(test-select). Desde la consola de comandos nos situamos en la carpeta donde se encuentran
los test:

C:\Documents and Settings\Administrador>cd c:\wamp\mysql\bench

Lanzamos el test correspondiente.

Pgina 29
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

C:\wamp\mysql\bench>perl test-select --user=root

La salida generada por test-select se visualiza en la ventana y es la siguiente:


Testing server 'MySQL 5.0.18 nt' at 2006-09-07 10:01:31

Testing the speed of selecting on keys that consist of many parts


The test-table has 10000 rows and the test is done with 500 ranges.

Creating table
Inserting 10000 rows
Time to insert (10000): 377 wallclock secs ( 0.93 usr 0.31 sys + 0.00
cusr 0.00 csys = 1.24 CPU)

Test if the database has a query cache


Time for select_cache (10000): 2 wallclock secs ( 1.15 usr 0.32 sys +
0.00 cusr 0.00 csys = 1.47 CPU)

Time for select_cache2 (10000): 90 wallclock secs ( 1.43 usr 0.28 sys +
0.00 cusr 0.00 csys = 1.71 CPU)

Testing big selects on the table


Time for select_big (70:17207): 1 wallclock secs ( 0.10 usr 0.07 sys +
0.00 cusr 0.00 csys = 0.17 CPU)
Time for select_range (410:1057904): 11 wallclock secs ( 7.48 usr 2.80
sys + 0.00 cusr 0.00 csys = 10.29 CPU)
Time for min_max_on_key (70000): 13 wallclock secs ( 7.98 usr 1.44 sys +
0.00cusr 0.00 csys = 9.42 CPU)
Time for count_on_key (50000): 9 wallclock secs ( 5.83 usr 0.98 sys +
0.00 cusr 0.00 csys = 6.81 CPU)

Time for count_group_on_key_parts (1000:100000): 1 wallclock secs ( 0.71


usr 0.34 sys + 0.00 cusr 0.00 csys = 1.05 CPU)
Testing count(distinct) on the table
Time for count_distinct_key_prefix (1000:1000): 1 wallclock secs ( 0.13
usr 0.01 sys + 0.00 cusr 0.00 csys = 0.14 CPU)
Time for count_distinct (1000:1000): 0 wallclock secs ( 0.13 usr 0.00
sys + 0.00 cusr 0.00 csys = 0.13 CPU)
Time for count_distinct_2 (1000:1000): 0 wallclock secs ( 0.12 usr 0.05
sys + 0.00 cusr 0.00 csys = 0.17 CPU)
Time for count_distinct_group_on_key (1000:6000): 0 wallclock secs (
0.15 usr 0.03 sys + 0.00 cusr 0.00 csys = 0.18 CPU)
Time for count_distinct_group_on_key_parts (1000:100000): 1 wallclock
secs ( 0.78 usr 0.30 sys + 0.00 cusr 0.00 csys = 1.08 CPU)
Time for count_distinct_group (1000:100000): 2 wallclock secs ( 0.80 usr
0.25sys + 0.00 cusr 0.00 csys = 1.05 CPU)
Time for count_distinct_big (100:1000000): 9 wallclock secs ( 6.62 usr
2.49 sys + 0.00 cusr 0.00 csys = 9.11 CPU)
Total time: 518 wallclock secs (34.35 usr 9.68 sys + 0.00 cusr 0.00
csys = 44.03 CPU)

Es interesante observar las acciones que ha ido realizando el test y el tiempo medido en
pulsos de reloj del sistema que ha empleado para cada una de las acciones, adems de
establecer, al final, el tiempo total empleado. Un dato que destaca es el tiempo empleado en
crear la tabla e insertar las 10000 filas (377 wallclock secs), esto se debe a que el fichero de
configuracin my.ini, en el caso del ejemplo, tiene como opcin por defecto crear las tablas
en arquitectura InnoDB, donde las inserciones son muchsimo ms lentas que las tablas tipo
MyISAM debido fundamentalmente a las verificaciones de integridad referencial que

Pgina 30
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

necesitan hacer las tablas InnoDB y que las tablas MyISAM ignoran. Es muy importante
conocer el tipo de tablas por defecto que genera nuestro servidor de base de datos antes de
lanzar los test de rendimiento.

Un test un poco especial es el llamado crash-me que consiste en probar todos las
caractersticas, capacidades y lmites del sistema de base de datos. Cuando se lance este test
es conveniente que el ordenador no est realizando ningn otro tipo de actividad. La salida
generada por crash-me es bastante extensa como para visualizarla desde consola, conviene
lanzar el test con la opcin config-file=n_fichero que permite almacenar la
informacin generada.

C:\mysql\bench>perl crash-me --user=root --config-file=test01-09-2006.txt

No se va a visualizar en este documento toda la salida generada por el test crash-me porque
es muy extensa, pero si, por ejemplo, podemos ver en un extracto, una vez editado el
fichero del ejemplo test01-09-2006.txt, como el test ha calculado que el tamao
mximo de un dato tipo varchar es de 1048543 bytes de longitud.

.
max_varchar_size=1048543 # max varchar() size
###We are trying (example with N=5):
###create table crash_q (q varchar(5))
###insert into crash_q values ('aaaaa')
###select * from crash_q
### 524287:OK 786431:OK 917503:OK 983039:OK 1015807:OK 1032191:OK
1040383:OK 1044479:OK 1046527:OK 1047551:OK 1048063:OK 1048319:OK
1048447:OK 1048511:OK 1048543:OK 1048559:FAIL 1048546:FAIL 1048544:FAIL
.

Para poder ver todas las opciones posibles a la hora de lanzar un test concreto usamos la
opcion --help.

C:\wamp\mysql\bench>perl test-select --help

Pgina 31
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Anexo 1. Uso de la herramienta DataGen para


insertar mltiples filas en tablas de prueba.
Esta herramienta es muy til cuando se necesita crear un juego de tablas con mltiples filas
para pruebas de optimizacin, tuning, rendimiento etc. en distintos servidores de bases de
datos.

DataGen se conecta va ODBC a cualquier base de datos y a partir de unos sencillos


asistentes permite realizar inserciones mltiples (de hasta cientos de miles de registros) a
partir de expresiones aritmticas, valores aleatorios, valores extrados de ficheros ASCII.

Vamos a ver la utilidad de DataGen mediante un sencillo ejemplo.

Paso 1. Instalar el controlador ODBC de MySQL.

En primer lugar y si no lo tenemos ya instalado tendremos que instalar el controlador


ODBC de MySQL para Windows. Este controlador permitir al software cliente que se
ejecuta en un equipo Windows conectarse a cualquier servidor de datos MySQL, el
software cliente en este caso va a ser DataGen pero tambin sirve para cualquier otro
software cliente como Access, Excel, etc.
El software del controlador ODBC (mysql-connector-odbc-3.xx.xx-win32.msi) lo
encontramos en la web de MySQL en la direccin:
http://dev.mysql.com/downloads/connector/odbc
(xx.xx depende de la ltima versin disponible en la Web de MySQL).
Una vez bajado el conector se instala sin ninguna dificultad siguiendo las instrucciones por
defecto que indica el asistente de instalacin.

Pgina 32
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Al finalizar los pasos de este asistente ya tenemos un conector ODBC para MySQL
instalado. Lo podemos comprobar si abrimos el listado de conectores ODBC que tiene
nuestro sistema operativo siguiendo la secuencia de mens:

InicioPanel de ControlHerramientas AdministrativasOrgenes de datos (ODBC)

Paso 2. Crear una conexin ODBC a una base de datos concreta de nuestro servidor
MySQL.

Para crear una conexin ODBC que permita conectar cualquier programa cliente con la
base de datos curso de mi servidor de base de datos MySQL se debe realizar lo siguiente:

Se sigue con la aplicacin Origenes de datos (ODBC) abierta y se


selecciona la pestaa DNS de usuario y se pulsa el botn Agregar

Seleccionamos a continuacin el driver MySQL ODBC 3.5X Driver que


acabamos de instalar.

Aparece a continuacin la siguiente ventana de dialogo:

Pgina 33
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Los campos necesarios a rellenar son los siguientes:

Data Source Name: es el nombre que queramos dar a la conexin.


Este nombre es el que tendremos que hacer referencia desde el
software de cliente (DataGen) para identificar la conexin.
Server: Nombre o direccin IP del servidor de base de datos MySQL,
en el ejemplo es el propio equipo.
User y password: El login de acceso de MySQL. En el ejemplo es el
usuario root sin password.
Database: Si los anteriores datos han sido correctamente
introducidos aqu ya debe aparecer el listado de bases de datos
accesibles por el usuario de MySQL. En el ejemplo seleccionamos
curso que ya estaba previamente creada en MySQL (mediante la
sentencia create database curso).

Paso 3. Crear desde MySQL la estructura de la tabla a la que se va posteriormente a


conectar DataGen para insertarle los mltiples registros.

Se lanza desde MySQL la siguiente sentencia que nos crear la tabla necesaria para el
ejemplo.

mysql> create table pruebadatagen (


clave int auto_increment primary key,
descripcion varchar(50),
fecha date,
valor int)engine=myisam;

Pgina 34
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Nota: La tabla puede ser tanto MyISAM como InnoDB. DataGen funciona muchsimo ms
rpido si la tabla es MyISAM, al eliminar todas las verificaciones de integridad.

Paso 4. Obtener e instalar DataGen.

El programa DataGen lo podemos bajar de la direccin:


http://www.daansystems.com/datagen/
La instalacin es inmediata ejecutando el fichero datagen_setup.exe.

Paso 5. Ejecutar DataGen y preparar la insercin mltiple de registros.

Una vez creada la estructura de la tabla ejecutamos el programa DataGen. Lo primero que
tenemos que hacer es indicarle al programa que debemos utilizar la conexin ODBC creada
en el paso 2. Pulsamos el botn para que aparezca la ventana que permite
seleccionar el vnculo de datos.

La opcin que debemos seleccionar es la que est resaltada en la imagen superior


Microsoft OLE DB Provider for ODBC Drivers.

Al pulsar Siguiente la siguiente ventana nos lista los nombres de los DSN de usuario de
los origenes de datos ODBC. Debemos seleccionar el creado en el paso 2 con el nombre
mi_conexion_mysql. Antes de pulsar Aceptar probamos la conexin con el botn Probar
Conexin.

Pgina 35
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

En la ventana principal de la aplicacin Datagen se puede ver, si la conexin con el


servidor MySQL ha sido satisfactoria, el listado de tablas de la base de datos curso.
Seleccionamos para nuestro ejemplo la tabla pruebadatagen y visualizamos los campos
que la componen.

Debemos decirle a la aplicacin cmo rellenamos cada una de las columnas de la tabla.
Existen diversas posibilidades y vamos a ver como se han aplicado en nuestro ejemplo.

Tal como muestra la siguiente ventana el campo clave [Integer], al estar definido como de
tipo auto_increment, va a ser el propio servidor MySQL el encargado de introducir sus
valores, por lo tanto DataGen no va a ocuparse de esta columna y dejamos desmarcada la
opcin Use Datasource asociada a este campo.

Pgina 36
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

El siguiente campo descripcin [varchar](50) se va a rellenar a partir de un fichero ASCII


que contiene nombres de calles inglesas. (El programa DataGen incorpora algunos de estos
listados que pueden ser muy tiles para rellenar campos de texto de forma aleatoria).

Pgina 37
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

El campo fecha [dbdate] tal como se ve a continuacin se va a rellenar con valores tipo
fecha generados aleatoriamente.

Por ltimo el campo valor [integer] se va a rellenar seleccionando valores de una columna
tambin de tipo entero pero de la tabla libros que se encuentra en la misma base de datos
curso, en concreto es la columna numpaginas.

Pgina 38
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Una vez definidos como se van a rellenar las columnas de la tabla fijamos el nmero de
filas que deseamos (en nuestro ejemplo 5000) y pulsamos el botn Go!.

Finalmente chequeamos la tabla como comprobacin de que todo ha ido bien.

C:\Documents and Settings\Administrador>mysql -uroot


Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 10 to server version: 5.0.18-nt

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

mysql> use curso


Database changed
mysql> select count(*) from pruebadatagen;
+----------+
| count(*) |
+----------+
| 5000 |
+----------+
1 row in set (0.00 sec)

mysql> select * from pruebadatagen limit 10;


+-------+------------------+------------+-------+
| clave | descripcion | fecha | valor |
+-------+------------------+------------+-------+
| 10001 | Mcconnell Rd | 1979-01-21 | 288 |
| 10002 | Contentment St | 2020-07-20 | 540 |
| 10003 | Ivalea Rd | 1982-04-12 | 582 |
| 10004 | Dunbar Cir | 1990-11-30 | 653 |
| 10005 | Sewell Rd | 2036-09-04 | 865 |
| 10006 | Coretta Cir | 2000-12-12 | 530 |
| 10007 | Trail Ride South | 2033-04-25 | 456 |
| 10008 | Timberwood Rd | 2012-01-11 | 843 |
| 10009 | King Ranch Rd | 1997-04-20 | 954 |
| 10010 | Mae Ln | 1976-11-21 | 434 |
+-------+------------------+------------+-------+
10 rows in set (0.01 sec)

mysql>

Pgina 39
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Anexo2. Instalar interprete de Perl para Windows


(ActivePerl).
Para poder realizar los test de rendimiento (benchmarking) que vienen incluidos en la
distribucin de MySQL es necesario contar con un intrprete de Perl en nuestro sistema que
tenga adems instalado el paquete DBD para MySQL. Los sistemas Windows no vienen
con intrprete de Perl y es necesario realizar la instalacin previa antes de poder lanzar los
test de rendimiento.

Los pasos a dar son los siguientes:

Acceder a la web http://www.activestate.com/Products/ActivePerl/ para bajarse el


producto y proceder a su instalacin. (Para Windows lo ms cmodo es bajarse el
archivo con extensin .msi). Se siguen en la instalacin todas las opciones que vienen
por defecto.

Desde el men Inicio ejecutamos el programa Perl Package Manager (PPM). PPM es un
gestor de paquetes similar a los gestores de paquetes .rpm o .deb del entorno linux.
Tenemos que comprobar que aparecen dentro de la lista de paquetes, como paquetes
instalados, los dos siguientes:

- DBI
- DBD-mysql

Tal como se ve en la siguiente imagen:

En el caso de que no estuviera alguno de los dos instalados es necesario visualizar todos
los paquetes disponibles [Ctrl 1], localizar en el listado el paquete concreto, por ejemplo
DBD-mysql, marcarlo para su instalacin pulsando la tecla [+] y por ltimo proceder a

Pgina 40
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

la instalacin [Ctrl Enter]. Lo que hace PPM es conectarse al repositorio de paquetes


va internet e instalarlo y configurarlo en el equipo.
Nota: A partir de la versin de ActivePerl 5.8.8.819 el programa PPM viene con interfaz grfico. Las
versiones anteriores de ActivePerl traen el programa PPM en modo consola de comandos.

Nuestro equipo ya est en condiciones de probar los test de rendimiento incluidos en la


MySQL Benchmark Suite.

Pgina 41
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Anexo3. MySQL Administrator.

Introduccin a MySQL Administrator.


MySQL Administrador es una interfaz grfica para llevar a cabo las principales operaciones
administrativas, como configurar, monitorizar y poner en marcha y detener los servidores
de MySQL, crear usuarios y conexiones, llevar a cabo copias de seguridad, y varias otras
tareas administrativas.
La mayora de las tareas pueden ser llevadas a cabo usando un entorno de lnea de consola
como mysqladmin, o mysql, pero MySQL Administrador tiene las siguientes ventajas:

Su interfaz grfica lo hace ms intuitivo.


Provee una mejor visin general de los ajustes que son cruciales para el
rendimiento, la fiabilidad, y seguridad de los servidores MySQL.
Muestra indicadores de rendimiento grficamente, haciendo ms fcil
determinar que ajustes y modificaciones se deben realizar en el servidor para afinar el
rendimiento.

El administrador de MySQL est diseado para trabajar a partir de la versin 4.0 o superior.
Entre las tareas que MySQL Administrator puede realizar se incluyen:

La configuracin de las opciones de inicio de los servidores.


Inicio y detencin de servidores.
Monitorizacin de conexiones al servidor.
Administracin de usuarios.
Monitorizacin del estado del servidor, incluyendo estadsticas de uso.
Visualizacin de los logs de servidor.
Gestin de copias de seguridad y recuperaciones.
Visualizacin de catlogos de datos.

Instalacin de MySQL Administrator.

La descarga del programa se hace desde la pgina web de MySQL, en


http://dev.mysql.com/downloads/administrator. Se debe seleccionar el fichero a bajar
dependiendo del SO sobre el que se vaya a realizar la instalacin.

Instalacin en Windows

Para instalar las herramientas GUI de MYSQL en Windows lo ms cmodo es bajar el


fichero mysql-gui-tools-version-win32.msi, donde versin indica la versin del producto
(por ejemplo, 5.0r4). Una vez en el disco duro ejecutar el fichero y seguir los pasos que
marca el instalador. Por defecto ya se crean las entradas en el men Inicio de Windows.

Pgina 42
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Instalacin en Linux.

El archivo de tar.qz genrico permite que se instale las herramientass de GUI de MySQL
en la mayora de las distribuciones de Linux. El archivo a bajar se llama mysql-gui-tools-
versio-.linux-i386.tar.gz, donde versin indica la versin del producto (por ejemplo, 5.0r4).

Para instalar las herramientas de GUI de MySQL se lanza desde la shell el siguiente
comando:

shell> tar --directory=/opt -xzvf mysql-gui-tools-version.tar.gz

Se instalan los archivos binarios de aplicacin en la carpeta


/opt/mysql-gui-tools-verrsin

Establecer una conexin con el servidor.


Al arrancar el programa aparece el siguiente cuadro de conexin:

En el que hay especificar los datos del servidor, nombre de usuario, contrasea y puerto de
la base de datos a la cual se desea conectar. Una vez introducidos de forma correcta,
aparece la pantalla principal del programa donde se visualiza por defecto la informacin
ms relevante sobre el servidor de MySQL y el cliente que ha establecido la conexin:

Pgina 43
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Algunas utilidades accesibles desde la ventana principal del programa son:

- Service control: inicio y detencin de servidores (solo accesible si se ha conectado


con un servidor MySQL en la mquina local).
- Startup variables: configuracin del servidor y las variables de inicio (solo accesible
si se ha conectado con un servidor MySQL en la mquina local).
- User Administration: para la gestin de usuarios y permisos.
- Server conections: visualiza y gestiona las conexiones abiertas con el servidor de
bases de datos.
- Health: informacin sobre la carga del servidor.
- Server Logs: el historial de logs del servidor.
- Replication Status: con informacin de los sistemas replicados.
- Backup: para hacer una copia de seguridad de las bases de datos.
- Restore: para restaurar las copias de seguridad.
- Catalogs: para mostrar las bases de datos, visualizar, crear y editar las tablas.

MySQL Administrador ofrece la posibilidad de arrancar saltndose el cuadro de conexin


para abrir directamente el modo Configuracin de servicio. Este tipo de inicio es til en
los dos siguientes casos:

Cuando se quiere iniciar el servidor MySQL desde dentro de MySQL


Administrator. Evidentemente no podemos conectar con el servidor si no est
levantado por lo tanto tenemos que saltarnos el inicio a travs del cuadro de
conexin normal e iniciar en el modo Configuracin de servicio. Es evidente

Pgina 44
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

que en este caso solo podemos arrancar servidores de datos en el mismo host
donde esta ejecutndose MySQL Administrador.

Cuando se tienen mltiples servidores ejecutndose en el mimo host. Si


arrancamos a travs del cuadro de conexin normal solo podemos configurar el
servicio del servidor al que nos hemos conectado. Si arrancamos en modo
Configuracin de servicio podemos configurar los servicios de todos los
servidores que MySQL Administrador detecte en el host.

La forma de arrancar en modo Configuracin de servicio es la siguiente. Se apreta la


tecla CTRL en la ventana de dialogo de conexin. El boton Cancel se convierte en Skip
mientras la tecla est apretada. Pulsando el botn Skip nos saltamos el cuadro de conexin
y aparece la ventana que permite arrancar, detener y configurar los servicios asociados a los
servidores de bases de datos MySQL.

Muchas de las opciones de configuracin de la base de datos son muy sencillas de entender
y de utilizar, por lo menos todas las opciones ms bsicas. En conjunto con MySQL Query
Browse, nos permite gestionar cualquier aspecto de una base de datos MySQL.

Este documento no trata de dar una visin exhaustiva de MySQL Administrador solo
pretende destacar con un poco ms de detalle un par tareas que posiblemente son de las que
MySQL Administrador puede ayudar ms a los usuarios que realizan labores de
administracin. Dichas tareas se centran en:
- Administracin de usuarios.
- Como generar grficos a medida para monitorizar la evolucin de las
variables de sistema (system variables) y el estado del sistema (status
variables).

Pgina 45
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

MySQL Administrator para administrar usuarios.

Los usuarios de MySQL se gestionan a travs de la base de datos llamada mysql,


concretamente con la tabla user. Sin embargo, el manejo de esa tabla es relativamente
complicado para un usuario que no sea del todo experto, por lo que ser mucho ms
cmodo usar la aplicacin MySQL Administrator para realizar la gestin de usuarios.

Entre las distintas opciones de MySQL Administrator tenemos una seccin para la gestin
de usuarios, sus permisos, host de acceso, etc. La podemos ver y seleccionar en la barra de
opciones de la izquierda.

Inicialmente solo hay un usuario creado, en muchas de las configuraciones tpicas, que es el
usuario root. La lista de usuarios creados aparece en la parte de abajo de la barra de la
izquierda. Podemos seleccionar cualquier usuario para editar sus propiedades.

Las propiedades de usuarios se dividen en tres pestaas:

- User information: con los datos de login (nombre de usuario y contrasea de acceso) y
otros datos personales del usuario, que es opcional completar.

Pgina 46
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

- Schema Privileges: con los permisos asociados a ese usuario en las distintas bases de datos
de MySQL.
- Resources: con los recursos disponibles para ese usuario.

En la parte de debajo de la ventana encontraremos tres botones, para crear un nuevo


usuario, para aplicar los cambios realizados en los formularios de edicin de un usuario y
para descartarlos.

Si queremos crear un usuario podemos pulsar el botn de New User y aparecer la ventana
de propiedades del usuario, en la pestaa de User Information, para que introduzcamos el
nombre para este usuario y la contrasea que vamos a asignar.

Podemos entrar en otras pestaas para terminar de introducir los datos del usuario. La
pestaa de Schema Privileges es especialmente interesante, ya que nos permite seleccionar
los permisos de este usuario para cada base de datos de nuestro sistema.

La ventana de Schema Privileges nos permite seleccionar una base de datos y entonces
accedemos a una lista con todos los privilegios posibles para permitir o denegar.
Inicialmente, para un usuario nuevo, todos los permisos estn denegados, as que tendremos
que seleccionar los que deseamos otorgar. Para ello simplemente seleccionamos entre los
avaliable privileges (permisos disponibles) y los pasamos a assigned privileges (permisos
asignados). En la siguiente imagen se observa como el usuario pedro tiene sobre la base de
datos concesionario asignados determinados privilegios (la mayor parte) y an tiene unos
cuantos disponibles que el administrador ha decidido no asignarle por el momento. El
usuario pedro podr realizar tareas de administracin si previamente el usuario root le ha
concedido todos los derechos sobre la base de datos mysql del servidor.

Pgina 47
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Vamos a ver como con MySQL Administrador podemos controlar los host desde los que se
pueden conectar los distintos usuarios.

Inicialmente cualquier usuario creado se puede conectar desde cualquier host o computador
posible. Para

Usuario Pedro desde


cualquier host

Si esta situacin no nos conviene, podemos definir nuevos host pulsando con el botn
derecho del ratn sobre el usuario al que queremos aadir ordenadores de acceso. Entonces
nos sale un formulario para indicar el nombre del host que queremos aadir a ese usuario, o
su direccin IP.

En cualquier momento, si hacemos doble clic sobre un usuario y nos sale debajo una lista
de hosts, es que ese usuario se puede conectar desde cualquier de esos hosts y adems,
podremos asignar permisos distintos al usuario cuando se conecte desde cada ordenador
posible. De modo que un usuario si se conecta, por ejemplo desde localhost, puede tener
ms permisos que si se conecta desde otros ordenadores de la red local o de Internet.

En la imagen anterior se entiende que el usuario pedro se puede conectar desde localhost (el
ordenador local) y desde otro equipo llamado con la direccin IP 192.168.1.101. Pero es
muy importante tener cuidado con esta lista de hosts, porque a lo mejor todava estamos
permitiendo que el usuario prueba se pueda conectar desde cualquier host y es posible que
no deseemos que eso se permita, sino que solo se deje acceder si se accede desde esos host
especificados.

Para darnos cuenta de este detalle podemos ver en la ventana de propiedades del usuario el
nombre del usuario que sale, para saber los hosts que estn permitidos.

Pgina 48
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Lo vemos con una imagen:

Usuario Pedro
desde
192.168.1.101

En esta imagen nos hemos situado en el host 192.168.1.101 del usuario pedro. Vemos que
en la parte de arriba, en la parte de propiedades del usuario aparece pedro@192.168.1.101.
Eso quiere decir que est definido el usuario pedro siempre que se conecte desde
192.168.1.101 y que estamos en la ventana de propiedades de ese usuario.

Pgina 49
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Ahora veamos la siguiente imagen:

Usuario Pedro
desde cualquier
host

Nos hemos situado encima del usuario pedro. Si nos fijamos en la parte de arriba de las
propiedades del usuario veremos como aparece "pedro", donde en la imagen anterior
apareca pedro@192.168.1.101. Por lo tanto en esta situacin el usuario pedro, como no
est acompaado de ningn nombre de host, significa que puede accederse desde cualquier
equipo.

Si queremos evitar que el usuario pedro pueda acceder desde cualquier equipo,
simplemente pulsamos con el botn derecho en el nombre de usuario y seleccionamos la
opcin que pone "Remove Host From Which The User Can Connect". Entonces, si hemos
pulsado sobre el nombre del usuario nos quitar el acceso del usuario desde cualquier host.
Si hacemos la misma accin sobre cualquier nombre de host, como localhost, nos quitar la
posibilidad de que el usuario se pueda conectar desde ese ordenador.

Pgina 50
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Si nos fijamos ahora de nuevo en la pantalla se aprecia el cambio realizado:

Usuario Pedro
desde
192.168.1.101

Apretando con el botn derecho en cualquier usuario o host nos dar una serie de opciones
para el mantenimiento de ese usuario o host que no hemos nombrado, como aadir un
usuario, borrarlo o duplicarlo.

Pgina 51
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

MySQL Administrator para crear grficos customizados


de monitorizacin del sistema.

MySQL Adminsitrator presenta una opcin de monitorizacin de variables de estado y


variables de sistema mediante grficos temporales que son muy tiles para medir la
evolucin de estas variables en tiempo real y en modo grfico.

En la configuracin por defecto aparecen dos pestaas con una serie de grficos
predefinidos. La pestaa Connectin Health muestra informacin sobre las conexiones de
los usuarios y el trfico y la pestaa Memory Health que indica el uso de la Query Cache y
de la Key Cache como muestra la siguiente imagen:

En concreto el grfico Query Cache Hitrate es un buen indicador del uso de la Query Cache
de MySQL.

Para ver como se han creado estos grficos predefinidos de MySQL Administrator basta
con poner el ratn sobre el grafico y con el botn derecho seleccionamos la opcin Edit
Graph del men contextual. Aparece una ventana que permite editar los parmetros de
configuracin del grfico tales como la formula empleada para dibujar la grafica, el tipo de
unidad de medida, el valor mnimo y mximo a alcanzar, etc. La formula se basa en una
expresin sobre los valores recogidos en la variables de estado de MySQL o bien en el
valor relativo de estos respecto a la ltima lectura dependiendo de lo especificado en la
expresin [variable] o ^[variable] :

Pgina 52
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

El grafico, por lo tanto, responde a la formula recogida en la caja de texto Value Formula.

Pgina 53
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Vamos a proceder a crear un grafico customizado desde el principio. Suponemos que al


administrador de la base de datos le interesa monitorizar y registrar el nmero de sentencias
de consulta (tipo SELECT) por un lado y tipo DML (INSERT, UPDATE y DELETE ) por
otro lado, que realizan los usuarios conectados a la base de datos.

El primer paso consiste en crear una nueva pgina o pestaa en la seccin Health. Para ello
basta con pulsar el botn derecho del ratn en el men contextual y seleccionar la opcin
Add a Page.

Ponemos como ttulo o Page Caption Sentencias SQL y a continuacin como Page
Description:

Como segundo paso es necesario crear algn grupo. Todos los grficos estn organizados
en torno a grupos. Seleccionamos la opcin Add a Group y creamos dos grupos tal como
aparece en esta imagen:

Pgina 54
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Como ltimo paso y ubicando el ratn dentro de cada grupo seleccionamos Add a Graph
para aadir los dos grficos. La ventana del primer grfico la rellenamos con los valores
que se ven en esta imagen:

La formula en este ejemplo es bien sencilla, nicamente visualizar el valor de la variable de


estado com_select que cuenta el nmero de Selects distintas que se han realizado en la
base de datos. Si la Select se repite se incrementa el valor de Qcache_hits pero no el de
com_select.

Pgina 55
Lenguaje SQL con MySQL avanzado Parte IV. Optimizacin

Para el segundo grafico que contabiliza las sentencias de actualizacin la formula ser la
siguiente:

Finalmente una vez creados ambos grficos deberamos visualizar algo parecido a esta
ventana:

Pgina 56