3.3. Crear y utilizar una base de datos

MySQL 5.0

3.3. Crear y utilizar una base de datos

Una vez que se sabe la forma de ingresar comandos, es el momento de acceder a una base de datos.

Suponga que en su hogar posee varias mascotas y desea registrar distintos tipos de información sobre ellas. Puede hacerlo si crea tablas para almacenar sus datos e introduce en ellas la información deseada. Entonces, podrá responder una variedad de preguntas acerca de sus mascotas recuperando datos desde las tablas. Esta sección le muestra como:

  • Crear una base de datos

  • Crear una tabla

  • Introducir datos en la tabla

  • Recuperar datos desde la tabla de varias maneras

  • Emplear múltiples tablas

La base de datos menagerie (en inglés significa "colección de animales") se ha hecho deliberadamente simple, pero no es difícil imaginar situaciones del mundo real donde podría usarse un tipo similar de base de datos. Por ejemplo, para un granjero que desee hacer el seguimiento de su hacienda, o para los registros de los pacientes de un veterinario. En el sitio web de MySQL pueden descargarse archivos de texto con datos de ejemplo y algunas de las sentencias empleadas en las siguientes secciones. Se encuentran disponibles en formato tar (http://www.mysql.com/Downloads/Contrib/Examples/menagerie.tar.gz) y Zip (http://www.mysql.com/Downloads/Contrib/Examples/menagerie.zip).

Mediante la sentencia se encuentran las bases de datos que existen actualmente en el servidor:

mysql> SHOW DATABASES;
+----------+
| Database |
+----------+
| mysql    |
| test     |
| tmp      |
+----------+

Probablemente la lista obtenida sea distinta en su ordenador, pero es casi seguro que tendrá las bases de datos y . La base de datos es necesaria porque es la que describe los privilegios de acceso de los usuarios. La base de datos se provee para que los usuarios hagan pruebas.

Tenga en cuenta que si no tiene el privilegio , no podrá ver todas las bases de datos que hay en el servidor. Consulte Sección 13.5.1.3, “Sintaxis de y .

Si la base de datos existe, intente acceder a ella:

mysql> USE test
Database changed

Advierta que, al igual que , no necesita que ponga un punto y coma al final (aunque puede hacerlo si lo desea). La sentencia tiene otra particularidad: debe escribirse en una sola linea.

Puede colocar los ejemplos siguientes en la base de datos , si tiene acceso a ella, pero si trabaja en un ambiente compartido, lo que deposite allí puede ser fácilmente borrado por alguien más que tenga el acceso. Por este motivo, debería pedirle a su administrador permiso para usar una base de datos propia. Suponga que quiere llamarla . El administrador necesitará ejecutar un comando como este:

mysql> GRANT ALL ON menagerie.* TO 'su_nombre_mysql'@'su_host_cliente';

Donde es el nombre de usuario que se le asignó, y es el host u ordenador desde donde se conectará.

3.3.1. Crear y seleccionar una base de datos

Si el administrador crea su base de datos en el mismo momento que le otorga privilegios, puede comenzar a utilizarla, de lo contrario necesitará crearla:

mysql> CREATE DATABASE menagerie;

En ambientes Unix, los nombres de las bases de datos son case sensitive (al contrario que las palabras clave), de modo que siempre debe referirse a su base de datos como , y no , , o una variante similar. Esto también se aplica a los nombres de tablas. Esta restricción no existe en Windows, aunque puede utilizar el mismo esquema de mayúsculas cuando se refiera a bases de datos y tablas en una consulta dada.

Al crear una base de datos, ésta no se selecciona para su uso, debe hacerlo explicitamente. Para convertir a en la base de datos actual, use este comando:

mysql> USE menagerie
Database changed

Las bases de datos sólo necesitan ser creadas una sola vez, pero deben ser seleccionadas cada vez que se inicia una sesión de mysql. Puede hacerse a través del comando como se muestra en el ejemplo, o puede indicar la base de datos en la linea de comandos al ejecutar mysql. Simplemente debe indicar el nombre de la base de datos a continuación de los parámetros que necesite ingresar. Por ejemplo:

shell> mysql -h  -u  -p menagerie
Enter password: ********

Advierta en el comando anterior que no es la contraseña. Si se quisiera suministrar la contraseña en la linea de comandos, después de la opción , debe hacerse sin dejar espacios en blanco (por ejemplo, , no ). De todos modos, colocar la contraseña en la linea de comandos no es recomendable porque lo expone a la vista de otros usuarios.

3.3.2. Crear una tabla

La creación de la base de datos ha sido una tarea sencilla, pero hasta ahora permanece vacía, como le muestra :

mysql> SHOW TABLES;
Empty set (0.00 sec)

La parte difícil es decidir cómo debería ser la estructura de su base de datos: qué tablas necesitará, y qué columnas habrá en cada tabla.

Querrá una tabla para contener un registro por cada mascota. Esta tabla puede llamarse , y debería contener, como mínimo, el nombre de cada animal. Dado que el nombre no es muy relevante por sí mismo, tendría que tener más información. Por ejemplo, si más de una persona en su familia tendrá mascotas, querrá listar también el dueño de cada animal. Y algunos otros datos descriptivos básicos, como especie y sexo.

¿Qué hacer con la edad? Podría ser de interés, pero no es un buen dato para almacenar en una base de datos. La edad cambia a medida que pasa el tiempo, lo cual significa que debería actualizar la base de datos a menudo. En lugar de esto, es mejor almacenar un valor fijo, como la fecha de nacimiento. De este modo, cada vez que requiera saber la edad, podrá calcularla como la diferencia entre la fecha de nacimiento y la fecha actual. MySQL provee funciones para realizar cálculos con fechas, por lo que no es dificultoso. Almacenar la fecha de nacimiento en lugar de la edad tiene otras ventajas:

  • Puede usar la base de datos para tareas como generar recordatorios para los próximos cumpleaños de mascotas. (Si piensa que este tipo de consultas no es importante, considere que es lo mismo que haría en un contexto de base de datos de negocios para identificar aquellos clientes a los que habrá que enviar una tarjeta por su cumpleaños, para conseguir ese toque personal con la asistencia del ordenador).

  • Puede calcular edades en relación a otras fechas además de la actual. Por ejemplo, almacenar la fecha de muerte de una mascota le posibilita calcular la edad que tenía a ese momento.

Probablemente pensará en otros tipos de información que resultarían útiles dentro de la tabla pero los identificados hasta ahora son suficientes: name (nombre), owner (propietario), species (especie), sex (sexo), birth (nacimiento) y death (muerte).

Debe usar la sentencia para especificar la estructura de una tabla:

mysql> CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20),
    -> species VARCHAR(20), sex CHAR(1), birth DATE, death DATE);

El tipo de dato es una buena elección para las columnas , , y porque los datos que allí se almacenan no son de longitud uniforme. En realidad no es necesario que todas estas columnas tengan la misma longitud ni que ésta sea . En MySQL 5.0.3 y versiones posteriores, normalmente se puede adoptar cualquier longitud entre y , según lo que se crea más razonable. (Nota: Anteriormente a MySQL 5.0.3, el límite de longitud era 255.) Si en el futuro debiera aumentar la longitud de estos campos, MySQL tiene la sentencia .

Hay varios tipos de datos que podrían usarse para representar el sexo en los registros de animales, tal como y , o (masculino) y (femenino). Lo más simple es usar los caracteres y .

Es obvio el uso del tipo de dato para las columnas y .

Luego de crear una tabla, debería producir una salida:

mysql> SHOW TABLES;
+---------------------+
| Tables in menagerie |
+---------------------+
| pet                 |
+---------------------+

Para verificar que la tabla ha sido creada en la forma esperada, utilice la sentencia :

mysql> DESCRIBE pet;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name    | varchar(20) | YES  |     | NULL    |       |
| owner   | varchar(20) | YES  |     | NULL    |       |
| species | varchar(20) | YES  |     | NULL    |       |
| sex     | char(1)     | YES  |     | NULL    |       |
| birth   | date        | YES  |     | NULL    |       |
| death   | date        | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

puede ser utilizada en cualquier momento, por ejemplo, si olvida los nombres o el tipo de dato de las columnas de la tabla.

3.3.3. Cargar datos en una tabla

Luego de crear la tabla, necesitará completarla con datos. Para esto, le serán de utilidad las sentencias e .

Suponga que los registros de mascotas fueran como los mostrados a continuación. (Observe que MySQL espera que las fechas tengan el formato , esto puede ser diferente a lo que acostumbra utilizar).

name owner species sex birth death
Fluffy Harold cat f 1993-02-04  
Claws Gwen cat m 1994-03-17  
Buffy Harold dog f 1989-05-13  
Fang Benny dog m 1990-08-27  
Bowser Diane dog m 1979-08-31 1995-07-29
Chirpy Gwen bird f 1998-09-11  
Whistler Gwen bird   1997-12-09  
Slim Benny snake m 1996-04-29  

Dado que está comenzando con una tabla vacía, una forma fácil de completarla es creando un fichero de texto que contenga una línea por cada animal, y luego insertando el contenido del fichero en la tabla mediante una sola sentencia.

Para esto, debería crear un fichero de texto llamado , conteniendo un registro por linea, con cada valor separado por un carácter de tabulación, y dispuestos en el orden en el cual se especificaron las columnas en la sentencia . Para valores ausentes (como sexo desconocido o fechas de muerte de animales con vida), puede usar valores . Para representar estos valores en el archivo de texto, utilice (barra diagonal y N mayúscula). Por ejemplo, el registro de Whistler se vería del modo siguiente (el espacio en blanco entre cada valor es un solo carácter de tabulación):

name owner species sex birth death

Para cargar el fichero dentro de la tabla , utilice este comando:

mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet;

Si trabaja en Windows, con un editor que emplea (retorno de carro + nueva linea) como caracteres de fin de línea, debería usar:

mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet
    -> LINES TERMINATED BY '\r\n';

(En un ordenador Apple bajo OS X, probablemente quiera utilizar .)

Opcionalmente puede especificar en la sentencia los caracteres que actuarán como separador de campo y fin de línea, pero los valores por defecto son tabulación y nueva línea. Estos son suficientes para que la sentencia lea correctamente el fichero

Si ocurre un error al ejecutar la sentencia, probablemente se deba a que su instalación de MySQL no tiene habilitada por defecto la capacidad de manejar archivos locales. Consulte Sección 5.5.4, “Cuestiones relacionadas con la seguridad y para obtener información sobre cómo cambiar esto.

Cuando lo que desea es agregar nuevos registros de a uno por vez, la sentencia resulta de utilidad. De esta sencilla manera, se suministran valores para cada columna, dispuestos en el orden en el cual se especificaron las columnas en la sentencia statement. Suponga que Diane obtiene un nuevo hamster llamado "Puffball". Se podría agregar un nuevo registro, usando la sentencia de este modo:

mysql> INSERT INTO pet
    -> VALUES ('Puffball','Diane','hamster','f','1999-03-30',NULL);

Observe que las cadenas alfanuméricas y las fechas son representados como cadenas delimitadas por apóstrofos. También, con , se pueden insertar valores directamente, para indicar un valor ausente. No se debe utilizar como se hace con .

A partir de este ejemplo queda demostrado que lleva mucho más trabajo realizar una carga inicial de registros empleando varias sentencias que si se hace mediante la sentencia .

3.3.4. Extraer información de una tabla

La sentencia es utilizada para traer información desde una tabla. La sintaxis general de esta sentencia es:

SELECT 
FROM 
WHERE ;

es lo que se quiere ver. Puede ser una lista de columnas, o para indicar “todas las columnas.indica la tabla donde están los datos a recuperar. La cláusula clause is optional. es opcional. Si está presente, representa las condiciones que cada registro debe cumplir para retornar como resultado.

3.3.4.1. Seleccionar todos los datos

La forma más simple de recupera todo lo que hay en la tabla:

mysql> SELECT * FROM pet;
+----------+--------+---------+------+------------+------------+
| name     | owner  | species | sex  | birth      | death      |
+----------+--------+---------+------+------------+------------+
| Fluffy   | Harold | cat     | f    | 1993-02-04 | NULL       |
| Claws    | Gwen   | cat     | m    | 1994-03-17 | NULL       |
| Buffy    | Harold | dog     | f    | 1989-05-13 | NULL       |
| Fang     | Benny  | dog     | m    | 1990-08-27 | NULL       |
| Bowser   | Diane  | dog     | m    | 1979-08-31 | 1995-07-29 |
| Chirpy   | Gwen   | bird    | f    | 1998-09-11 | NULL       |
| Whistler | Gwen   | bird    | NULL | 1997-12-09 | NULL       |
| Slim     | Benny  | snake   | m    | 1996-04-29 | NULL       |
| Puffball | Diane  | hamster | f    | 1999-03-30 | NULL       |
+----------+--------+---------+------+------------+------------+

Esta forma de es útil si se quiere revisar la tabla completa, por ejemplo, despues de haberla cargado con un conjunto de datos inicial. Por ejemplo, puede ocurrir que la fecha de nacimiento de Bowser no parezca correcta. Consultando los papeles de pedigri, se descubre que el año correcto de nacimiento es 1989, no 1979.

Existen al menos dos formas de solucionarlo:

  • Editando el fichero para corregir el error, vaciando la tabla y volviendola a llenar con los datos. Para esto se usan las sentencias y :

    mysql> DELETE FROM pet;
    mysql> LOAD DATA LOCAL INFILE 'pet.txt' INTO TABLE pet;
    

    No obstante, si opta por esto, deberá volver a cargar el registro de Puffball.

  • Corrigiendo únicamente el registro erróneo. Para esto se usa la sentencia :

    mysql> UPDATE pet SET birth = '1989-08-31' WHERE name = 'Bowser';
    

    modifica solo el registro en cuestión y no requiere que se vuelva a llenar la tabla.

3.3.4.2. Seleccionar registros específicos

Como se ha visto en la sección anterior, es fácil recuperar una tabla en su totalidad. Sólo debe omitir la cláusula en la sentencia . Pero, generalmente, no se desea ver la tabla completa, especialmente cuando alcanza un gran tamaño. En cambio, usualmente, se tiene interés en obtener una respuesta para una consulta en particular, en cuyo caso se especifican algunas restricciones para la información que se traerá. A continuación se verán algunas consultas que responden preguntas acerca de las mascotas.

Se pueden seleccionar sólo algunos registros de la tabla. Por ejemplo, si quisiera verificar los cambios realizados sobre la fecha de nacimiento de Bowser, seleccione el registro de Bowser de esta manera:

mysql> SELECT * FROM pet WHERE name = 'Bowser';
+--------+-------+---------+------+------------+------------+
| name   | owner | species | sex  | birth      | death      |
+--------+-------+---------+------+------------+------------+
| Bowser | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+-------+---------+------+------------+------------+

La salida confirma que el año fue correctamente registrado como 1989, ya no es 1979.

Normalmente, las comparaciones de cadenas no son case sensitive, por eso puede escribir el nombre como , , etc. El resultado de la consulta será el mismo.

Se pueden indicar condiciones a cumplir por cualquier columna, no solamente por . Por ejemplo, si quisiera saber qué animales han nacido luego de 1998, necesita evaluar la columna:

mysql> SELECT * FROM pet WHERE birth > '1998-1-1';
+----------+-------+---------+------+------------+-------+
| name     | owner | species | sex  | birth      | death |
+----------+-------+---------+------+------------+-------+
| Chirpy   | Gwen  | bird    | f    | 1998-09-11 | NULL  |
| Puffball | Diane | hamster | f    | 1999-03-30 | NULL  |
+----------+-------+---------+------+------------+-------+

Se pueden combinar condiciones, por ejemplo para localizar perros hembra:

mysql> SELECT * FROM pet WHERE species = 'dog' AND sex = 'f';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

La consulta anterior emplea el operador lógico . También existe el operador :

mysql> SELECT * FROM pet WHERE species = 'snake' OR species = 'bird';
+----------+-------+---------+------+------------+-------+
| name     | owner | species | sex  | birth      | death |
+----------+-------+---------+------+------------+-------+
| Chirpy   | Gwen  | bird    | f    | 1998-09-11 | NULL  |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL  |
| Slim     | Benny | snake   | m    | 1996-04-29 | NULL  |
+----------+-------+---------+------+------------+-------+

and pueden ser combinadas, si bien tiene mayor precedencia que . Si utiliza ambos operadores, es buena idea emplear paréntesis para indicar explicitamente la forma en que las condiciones deben agruparse:

mysql> SELECT * FROM pet WHERE (species = 'cat' AND sex = 'm')
    -> OR (species = 'dog' AND sex = 'f');
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

3.3.4.3. Seleccionar columnas concretas

Si no se quieren ver filas completas, solo hace falta indicar las columnas en las que se está interesado, separadas por comas. Por ejemplo, si desea saber cuándo nació cada animal, seleccione las columnas y :

mysql> SELECT name, birth FROM pet;
+----------+------------+
| name     | birth      |
+----------+------------+
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Buffy    | 1989-05-13 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Puffball | 1999-03-30 |
+----------+------------+

Para saber quien posee mascotas, utilice esta consulta:

mysql> SELECT owner FROM pet;
+--------+
| owner  |
+--------+
| Harold |
| Gwen   |
| Harold |
| Benny  |
| Diane  |
| Gwen   |
| Gwen   |
| Benny  |
| Diane  |
+--------+

Observe que esta sentencia retorna el campo de cada registro, y algunos de ellos aparecen más de una vez. Para reducir la salida, recupere solamente una vez cada registro repetido, agregando la palabra clave :

mysql> SELECT DISTINCT owner FROM pet;
+--------+
| owner  |
+--------+
| Benny  |
| Diane  |
| Gwen   |
| Harold |
+--------+

Puede emplearse una clásula para combinar la selección de ciertas filas y de ciertas columnas. Por ejemplo, para obtener únicamente la fecha de nacimiento de perros y gatos, ejecute esta consulta:

mysql> SELECT name, species, birth FROM pet
    -> WHERE species = 'dog' OR species = 'cat';
+--------+---------+------------+
| name   | species | birth      |
+--------+---------+------------+
| Fluffy | cat     | 1993-02-04 |
| Claws  | cat     | 1994-03-17 |
| Buffy  | dog     | 1989-05-13 |
| Fang   | dog     | 1990-08-27 |
| Bowser | dog     | 1989-08-31 |
+--------+---------+------------+

3.3.4.4. Ordenar registros

Quizá advirtió, en los ejemplos anteriores, que las filas resultantes se mostraron sin ningún orden en particular. A menudo es más fácil examinar la salida de una consulta cuando las filas se ordenan de algún modo significativo. Para ordenar un resultado, se usa la clásula .

Aqui tiene las fechas de cumpleaños de los animales, ordenadas por fecha:

mysql> SELECT name, birth FROM pet ORDER BY birth;
+----------+------------+
| name     | birth      |
+----------+------------+
| Buffy    | 1989-05-13 |
| Bowser   | 1989-08-31 |
| Fang     | 1990-08-27 |
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Slim     | 1996-04-29 |
| Whistler | 1997-12-09 |
| Chirpy   | 1998-09-11 |
| Puffball | 1999-03-30 |
+----------+------------+

Por lo general, cuando se trata de columnas de tipo carácter, la ordenación, — al igual que otras operaciones de comparación — no es case-sensitive. Significa que el orden permanece indefinido para las columnas que son idénticas excepto por sus mayúsculas y minúsculas. Puede no obstante forzar a que una columna se ordene en forma sensible a mayúsculas empleando el modificador : .

El sentido de ordenación, por defecto, es ascendente, con los valores más pequeños primero. Para ordenar en sentido inverso (descendente), agregue la palabra clave luego del nombre de la columna por la que ordena:

mysql> SELECT name, birth FROM pet ORDER BY birth DESC;
+----------+------------+
| name     | birth      |
+----------+------------+
| Puffball | 1999-03-30 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Claws    | 1994-03-17 |
| Fluffy   | 1993-02-04 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Buffy    | 1989-05-13 |
+----------+------------+

Puede ordenar basándose en varias columnas, y cada columna en un sentido diferente. Por ejemplo, para ordenar por tipo de animal en sentido ascendente y, dentro de cada tipo, ordenar por nacimiento en sentido descendente (los animales más jóvenes primero) utilice la siguiente consulta:

mysql> SELECT name, species, birth FROM pet
    -> ORDER BY species, birth DESC;
+----------+---------+------------+
| name     | species | birth      |
+----------+---------+------------+
| Chirpy   | bird    | 1998-09-11 |
| Whistler | bird    | 1997-12-09 |
| Claws    | cat     | 1994-03-17 |
| Fluffy   | cat     | 1993-02-04 |
| Fang     | dog     | 1990-08-27 |
| Bowser   | dog     | 1989-08-31 |
| Buffy    | dog     | 1989-05-13 |
| Puffball | hamster | 1999-03-30 |
| Slim     | snake   | 1996-04-29 |
+----------+---------+------------+

Advierta que la palabra clave se aplica sobre la columna inmediatamente anterior (); no afecta el sentido de ordenación de la columna .

3.3.4.5. Cálculos sobre fechas

MySQL provee varias funciones que se aplican a cálculos entre fechas, por ejemplo, para calcular edades u obtener partes de una fecha.

Para determinar cuántos años de edad tiene cada mascota, hay que calcular la diferencia entre el año de la fecha actual y el de la fecha de nacimiento, y luego restar 1 al resultado si el dia y mes actuales son anteriores al día y mes indicados por la fecha de nacimiento. La siguiente consulta devuelve, para cada mascota, el nombre, la fecha de nacimiento, la fecha actual, y la edad en años.

mysql> SELECT name, birth, CURDATE(),
    -> (YEAR(CURDATE())-YEAR(birth))
    -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
+----------+------------+------------+------+

En el ejemplo anterior, trae la parte correspondiente al año de una fecha, y trae los 5 primeros caracteres contando desde la derecha, que representan la parte de la fecha. La porción de la expresión que compara los valores devuelve 1 o 0, lo cual se corresponde con la diferencia de 1 año a restar de la edad si el dia de la fecha devuelto por ocurre antes que la fecha de nacimiento . La expresión completa es un tanto confusa para usar como encabezado, por lo que se emplea un alias () para que el encabezado sea más comprensible.

La consulta funciona bien, pero los resultados podrían revisarse más fácilmente si las filas se presentaran en algún orden. Esto puede hacerse agregando la cláusula para ordenar por nombre la salida:

mysql> SELECT name, birth, CURDATE(),
    -> (YEAR(CURDATE())-YEAR(birth))
    -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet ORDER BY name;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
+----------+------------+------------+------+

Para ordenar la salida por edad () en lugar de por nombre (), solo hay que utilizar una cláusula diferente:

mysql> SELECT name, birth, CURDATE(),
    -> (YEAR(CURDATE())-YEAR(birth))
    -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet ORDER BY age;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
+----------+------------+------------+------+

Una consulta similar se utiliza para determinar la edad a la fecha de muerte de los animales. Se determinan los animales que han muerto verificando si el valor de la columna es . Entonces, para todos los valores no calcula la diferencia entre las fechas de muerte () y nacimiento ():

mysql> SELECT name, birth, death,
    -> (YEAR(death)-YEAR(birth)) - (RIGHT(death,5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet WHERE death IS NOT NULL ORDER BY age;
+--------+------------+------------+------+
| name   | birth      | death      | age  |
+--------+------------+------------+------+
| Bowser | 1989-08-31 | 1995-07-29 |    5 |
+--------+------------+------------+------+

La consulta utiliza la expresión en lugar de porque es un valor especial, que no puede ser comparado mediante los operadores lógicos habituales. Este tema se trata más extensamente más adelante. Consultar Sección 3.3.4.6, “Trabajar con valores .

¿Qué tal si se quisiera saber qué animales cumplen años el próximo mes? Para esta clase de cálculos, el año y el día son irrelevantes; simplemente se desea extraer de la columna la parte correspondiente al mes. MySQL cuenta con varias funciones para extraer partes de fechas, como , , y . es la función apropiada para este caso. Para verla en funcionamiento, ejecute una consulta que muestra tanto el valor de como el de :

mysql> SELECT name, birth, MONTH(birth) FROM pet;
+----------+------------+--------------+
| name     | birth      | MONTH(birth) |
+----------+------------+--------------+
| Fluffy   | 1993-02-04 |            2 |
| Claws    | 1994-03-17 |            3 |
| Buffy    | 1989-05-13 |            5 |
| Fang     | 1990-08-27 |            8 |
| Bowser   | 1989-08-31 |            8 |
| Chirpy   | 1998-09-11 |            9 |
| Whistler | 1997-12-09 |           12 |
| Slim     | 1996-04-29 |            4 |
| Puffball | 1999-03-30 |            3 |
+----------+------------+--------------+

Encontrar los animales que cumplen años el mes siguiente es también sencillo. Suponga que el mes actual es abril. De modo que su número es , y se buscan los animales nacidos en Mayo (mes ), de esta forma:

mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+-------+------------+
| name  | birth      |
+-------+------------+
| Buffy | 1989-05-13 |
+-------+------------+

Esto se complica ligeramente cuando el mes actual es Diciembre. No se puede simplemente sumarle 1 al número del mes () y buscar animales nacidos en el mes , porque no existe tal mes. En lugar de eso, se debe buscar por animales nacidos en Enero (mes ).

Se puede incluso escribir la consulta de forma que funcione sin importar cual es el mes actual. Así, no se necesitará indicar un mes en particular en la consulta. sirve para sumar un intervalo de tiempo a una fecha dada. Si se adiciona un mes al valor de , y se extrae el mes mediante , el resultado será el mes en el que se buscarán cumpleaños:

mysql> SELECT name, birth FROM pet
    -> WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));

Una manera alternativa de alcanzar el mismo resultado es sumar al mes actual para obtener el mes siguiente (después de emplear la función módulo () para dejar el número de mes en si resultara ser :

mysql> SELECT name, birth FROM pet
    -> WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;

Advierta que devuelve un número entre and . Y devuelve un número entre y . La suma debe ser realizada luego de , en otro caso se estaría pasando de Noviembre () to Enero ().

3.3.4.6. Trabajar con valores

El valor puede resultar un poco desconcertante hasta que se comienza a utilizar. Conceptualmente, significa valor inexistente o desconocido, y es tratado de forma diferente a otros valores. Para verificar que un valor es , no se pueden emplear operadores de comparación aritmética como , , o . Para comprobar esto, intente la siguiente consulta:

mysql> SELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
+----------+-----------+----------+----------+
| 1 = NULL | 1 <> NULL | 1 < NULL | 1 > NULL |
+----------+-----------+----------+----------+
|     NULL |      NULL |     NULL |     NULL |
+----------+-----------+----------+----------+

Claramente, no se obtienen valores significtivos a partir de estas comparaciones. Use en su lugar los operadores y :

mysql> SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+
| 1 IS NULL | 1 IS NOT NULL |
+-----------+---------------+
|         0 |             1 |
+-----------+---------------+

Observe que en MySQL, o se intepretan como falso, y cualquier otro valor, como verdadero. El valor por defecto para una operación booleana es .

Este tratamiento especial de es debido a que, en la sección anterior, fue necesario determinar qué animales ya no estaban vivos utilizando en lugar de .

Dos valores son considerados iguales por la cláusula .

Cuando se realiza un , los valores se presentan en primer lugar si se emplea , y al final si se ordena con .

Un error muy común cuando se trabaja con valores es asumir que es imposible insertar un valor cero o una cadena vacía en una columna definida como , pero no es así. Los mencionados son efectivamente valores, mientras que significa "no hay un valor". Puede comprobar esto fácilmente empleando como se muestra aquí:

mysql> SELECT 0 IS NULL, 0 IS NOT NULL, '' IS NULL, '' IS NOT NULL;
+-----------+---------------+------------+----------------+
| 0 IS NULL | 0 IS NOT NULL | '' IS NULL | '' IS NOT NULL |
+-----------+---------------+------------+----------------+
|         0 |             1 |          0 |              1 |
+-----------+---------------+------------+----------------+

Por lo tanto, es totalmente posible insertar cadenas vacias o ceros en columnas marcadas como , ya que son valores . Consultar Sección A.5.3, “Problemas con valores .

3.3.4.7. Coincidencia de patrones

MySQL posee capacidades estándar para utilizar patrones así como también una forma de patrones basada en expresiones regulares extendidas similares a las que se encuentran en utilidades de UNIX, como ser , , y .

Los patrones SQL permiten emplear el caracter '' para representar coincidencia con un carácter individual y '' En MySQL, por defecto, los patrones SQL no son case-sensitive. Abajo se muestran algunos ejemplos. Advierta que no se emplean los operadores o para trabajar con patrones SQL, en lugar de eso se usan los operadores de comparación o .

Para encontrar nombres que comiencen con '':

mysql> SELECT * FROM pet WHERE name LIKE 'b%';
+--------+--------+---------+------+------------+------------+
| name   | owner  | species | sex  | birth      | death      |
+--------+--------+---------+------+------------+------------+
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL       |
| Bowser | Diane  | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+--------+---------+------+------------+------------+

Para encontrar nombres que terminen con '':

mysql> SELECT * FROM pet WHERE name LIKE '%fy';
+--------+--------+---------+------+------------+-------+
| name   | owner  | species | sex  | birth      | death |
+--------+--------+---------+------+------------+-------+
| Fluffy | Harold | cat     | f    | 1993-02-04 | NULL  |
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL  |
+--------+--------+---------+------+------------+-------+

Para encontrar nombres que contengan '':

mysql> SELECT * FROM pet WHERE name LIKE '%w%';
+----------+-------+---------+------+------------+------------+
| name     | owner | species | sex  | birth      | death      |
+----------+-------+---------+------+------------+------------+
| Claws    | Gwen  | cat     | m    | 1994-03-17 | NULL       |
| Bowser   | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL       |
+----------+-------+---------+------+------------+------------+

Para encontrar nombres que contengan exactamente 5 caracteres, use 5 veces el caracter patrón '':

mysql> SELECT * FROM pet WHERE name LIKE '_____';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

Los otros patrones que pueden emplearse con MySQL usan expresiones regulares extendidas. Cuando busque coincidencias con este tipo de patrones, use los operadores y (o bien los sinónimos y ).

Algunas características de las expresiones regulares extendidas:

  • '' detecta coincidencia con cualquier carácter individual.

  • Una clase de carácter '' detecta coincidencia con cualquier caracter entre los corchetes. Por ejemplo, '' coincidirá con '', '', o ''. Para hacer referencia a un rango de caracteres, use un guión. '' detecta coincidencia con cualquier letra, mientras que '' lo hace con cualquier dígito.

  • '' detecta coincidencia con cero o más apariciones de los caracteres que lo preceden. Por ejemplo, '' detecta cualquier número de caracteres '', '' detecta cualquier cantidad de dígitos, y '' coincidirá con cualquier número de cualquier carácter.

  • tendrá éxito si el patrón suministrado encuentra coincidencia en cualquier parte del valor examinado (esto difiere de en que este último solo tiene éxito si el patrón concuerda con todo el valor).

  • Para lograr que un patrón detecte coincidencias solamente al principio o al final del valor examinado, utilice '' al principio o '' al final del patrón.

Para demostrar el funcionamiento de las expresiones regulares extendidas, las consultas con expuestas anteriormente se han reescrito utilizando .

Para hallar nombres que comiencen con '', use '' para buscar coincidencia al principio del valor:

mysql> SELECT * FROM pet WHERE name REGEXP '^b';
+--------+--------+---------+------+------------+------------+
| name   | owner  | species | sex  | birth      | death      |
+--------+--------+---------+------+------------+------------+
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL       |
| Bowser | Diane  | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+--------+---------+------+------------+------------+

En MySQL 5.0, si realmente quiere forzar a que la comparación realizada por sea case sensitive, utilice la palabra clave para convertir a una de las cadenas en una cadena binaria. Esta consulta solamente encontrará coincidencia con '' minúsculas al comienzo de un nombre:

mysql> SELECT * FROM pet WHERE name REGEXP BINARY '^b';

Para hallar nombres finalizados en '', emplee '' para buscar la coincidencia en el final del nombre:

mysql> SELECT * FROM pet WHERE name REGEXP 'fy$';
+--------+--------+---------+------+------------+-------+
| name   | owner  | species | sex  | birth      | death |
+--------+--------+---------+------+------------+-------+
| Fluffy | Harold | cat     | f    | 1993-02-04 | NULL  |
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL  |
+--------+--------+---------+------+------------+-------+

Para encontrar nombres conteniendo una '', utilice esta consulta:

mysql> SELECT * FROM pet WHERE name REGEXP 'w';
+----------+-------+---------+------+------------+------------+
| name     | owner | species | sex  | birth      | death      |
+----------+-------+---------+------+------------+------------+
| Claws    | Gwen  | cat     | m    | 1994-03-17 | NULL       |
| Bowser   | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL       |
+----------+-------+---------+------+------------+------------+

Debido a que un patrón de expresión regular encuentra coincidencia sin importar el lugar del valor donde se produce, en la consulta previa no es necesario colocar un comodín a cada lado del patrón para obtener coincidencia en cualquier parte del valor, como hubiera sucedido de utilizar un patrón SQL

Para hallar nombres conteniendo exactamente cinco caracteres, use '' y '' para obligar a que la coincidencia deba estar al principio y al final del nombre, y cinco instancias de '' entre ellas.

mysql> SELECT * FROM pet WHERE name REGEXP '^.....$';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

La consulta anterior también se podría haber escrito empleando el operador '' “repetir--veces”:

mysql> SELECT * FROM pet WHERE name REGEXP '^.{5}$';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

3.3.4.8. Contar registros

Una pregunta frecuente que deben responder las bases de datos es: “¿qué tan a menudo aparece en la tabla un cierto tipo de dato?” Por ejemplo, se podría querer averiguar la cantidad de mascotas de que se dispone, o cuantas mascotas tiene cada propietario, o varios otros recuentos sobre los animales.

Contar la cantidad total de animales es la misma pregunta que “¿cuántos registros hay en la tabla ?”, ya que hay un registro por mascota. cuenta el número de filas, por ello, la consulta para contar animales luce así:

mysql> SELECT COUNT(*) FROM pet;
+----------+
| COUNT(*) |
+----------+
|        9 |
+----------+

Anteriormente se recuperaban los nombres de la gente que poseía mascotas. Se puede usar para hallar cuantas mascotas tiene cada propietario:

mysql> SELECT owner, COUNT(*) FROM pet GROUP BY owner;
+--------+----------+
| owner  | COUNT(*) |
+--------+----------+
| Benny  |        2 |
| Diane  |        2 |
| Gwen   |        3 |
| Harold |        2 |
+--------+----------+

Observe el uso de para agrupar todos los registros de cada propietario. Sin dicha cláusula, todo lo que se hubiera obtenido sería un mensaje de error:

mysql> SELECT owner, COUNT(*) FROM pet;
ERROR 1140 (42000): Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) 
with no GROUP columns is illegal if there is no GROUP BY clause

y son útiles para presentar datos en varias formas. Los siguientes ejemplos muestran diferentes operaciones:

Cantidad de animales por especies:

mysql> SELECT species, COUNT(*) FROM pet GROUP BY species;
+---------+----------+
| species | COUNT(*) |
+---------+----------+
| bird    |        2 |
| cat     |        2 |
| dog     |        3 |
| hamster |        1 |
| snake   |        1 |
+---------+----------+

Cantidad de animales por sexo:

mysql> SELECT sex, COUNT(*) FROM pet GROUP BY sex;
+------+----------+
| sex  | COUNT(*) |
+------+----------+
| NULL |        1 |
| f    |        4 |
| m    |        4 |
+------+----------+

(En esta salida, indica "sexo desconocido")

Cantidad de animales por combinación de especies y sexo:

mysql> SELECT species, sex, COUNT(*) FROM pet GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| bird    | NULL |        1 |
| bird    | f    |        1 |
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
| hamster | f    |        1 |
| snake   | m    |        1 |
+---------+------+----------+

No es necesario examinar una tabla entera cuando se emplea . Por ejemplo, la consulta anterior, se podria limitar a perros y gatos de esta manera:

mysql> SELECT species, sex, COUNT(*) FROM pet
    -> WHERE species = 'dog' OR species = 'cat'
    -> GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
+---------+------+----------+

O si desea la cantidad de animales de cada sexo contando solamente los que tienen sexo conocido:

mysql> SELECT species, sex, COUNT(*) FROM pet
    -> WHERE sex IS NOT NULL
    -> GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| bird    | f    |        1 |
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
| hamster | f    |        1 |
| snake   | m    |        1 |
+---------+------+----------+

3.3.4.9. Utilizar más de una tabla

La tabla mantiene el registro de las mascotas que se poseen. Si quisiera registrar otros datos acerca de ellas, como eventos de su vida tales como visitas al veterinario o nacimiento de crías, necesitaría otra tabla. ¿Cómo debería ser esta tabla? Se necesita:

  • Un campo con el nombre de la mascota para saber a quien pertenece cada evento registrado.

  • La fecha en que ocurrió el evento.

  • Un campo con la descripción del evento.

  • Un campo con el tipo de evento, a fin de poder clasificarlo.

Teniendo en cuenta estas consideraciones, la sentencia para la tabla ("eventos", en inglés) podría ser así:

mysql> CREATE TABLE event (name VARCHAR(20), date DATE,
    -> type VARCHAR(15), remark VARCHAR(255));

Como se hizo con la tabla , es más fácil realizar la carga inicial de datos si se crea un archivo de texto delimitado con tabulaciones que contenga la información a agregar:

name date type remark
Fluffy 1995-05-15 litter 4 kittens, 3 female, 1 male
Buffy 1993-06-23 litter 5 puppies, 2 female, 3 male
Buffy 1994-06-19 litter 3 puppies, 3 female
Chirpy 1999-03-21 vet needed beak straightened
Slim 1997-08-03 vet broken rib
Bowser 1991-10-12 kennel  
Fang 1991-10-12 kennel  
Fang 1998-08-28 birthday Gave him a new chew toy
Claws 1998-03-17 birthday Gave him a new flea collar
Whistler 1998-12-09 birthday First birthday

Los registros se cargan así:

mysql> LOAD DATA LOCAL INFILE 'event.txt' INTO TABLE event;

Con base en lo que se ha aprendido a partir de las consultas efectuadas sobre la tabla , se debería poder recuperar registros de la tabla ; los principios son los mismos. Pero en un momento dado la tabla por sí sola es insuficiente para responder las preguntas que pueden formularse.

Suponga que se desea saber a qué edad tuvo sus crías cada mascota. Anteriormente se aprendió a calcular edades a partir de dos fechas. La fecha en que la mascota tuvo sus crias está en la tabla , pero para calcular su edad, se necesita su fecha de nacimiento, la cual está localizada en la tabla . Esto significa que la consulta requiere ambas tablas:

mysql> SELECT pet.name,
    -> (YEAR(date)-YEAR(birth)) - (RIGHT(date,5)<RIGHT(birth,5)) AS age,
    -> remark
    -> FROM pet, event
    -> WHERE pet.name = event.name AND event.type = 'litter';
+--------+------+-----------------------------+
| name   | age  | remark                      |
+--------+------+-----------------------------+
| Fluffy |    2 | 4 kittens, 3 female, 1 male |
| Buffy  |    4 | 5 puppies, 2 female, 3 male |
| Buffy  |    5 | 3 puppies, 3 female         |
+--------+------+-----------------------------+

Hay varias cosas para observar en esta consulta:

  • La cláusula menciona dos tablas porque la consulta necesita traer datos de ambas

  • Cuando se combina (también se denomina join -unión, en inglés-) información desde múltiples tablas, se necesita indicar qué registro de una tabla se combinará con qué registro de la otra. Esto es sencillo porque ambas tablas tienen una columna . La consulta emplea la cláusula para hacer coincidir registros de las dos tablas basándose en el valor de .

  • Dado que la columna aparece en ambas tablas, se debe especificar a cuál tabla pertenece la columna al hacer referencia a ella. Esto se hace anteponiendo el nombre de la tabla al nombre de la columna.

No es necesario tener dos tablas diferentes para establecer una unión. A veces es útil combinar una tabla consigo misma, si se desea comparar entre sí registros de una misma tabla. Por ejemplo, para formar parejas de mascotas para reproducción, podría unir la tabla consigo misma para generar pares de animales macho y hembra de la misma especie:

mysql> SELECT p1.name, p1.sex, p2.name, p2.sex, p1.species
    -> FROM pet AS p1, pet AS p2
    -> WHERE p1.species = p2.species AND p1.sex = 'f' AND p2.sex = 'm';
+--------+------+--------+------+---------+
| name   | sex  | name   | sex  | species |
+--------+------+--------+------+---------+
| Fluffy | f    | Claws  | m    | cat     |
| Buffy  | f    | Fang   | m    | dog     |
| Buffy  | f    | Bowser | m    | dog     |
+--------+------+--------+------+---------+

En la consulta anterior se especificaron alias para la tabla con el fin de indicar a qué instancia de la tabla pertenece cada columna referenciada.