Capítulo 4. Trabajar con estructuras de bases de datos
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com
Este capítulo te muestra cómo crear tus propias bases de datos, añadir y eliminar estructuras como tablas e índices, y tomar decisiones sobre los tipos de columnas de tus tablas. Se centra en la sintaxis y las características de SQL, y no en la semántica de concebir, especificar y refinar el diseño de una base de datos; encontrarás una descripción introductoria de las técnicas de diseño de bases de datos en el Capítulo 2. Para trabajar en este capítulo, necesitas saber cómo trabajar con una base de datos existente y sus tablas, como se explica en el Capítulo 3.
En este capítulo se enumeran las estructuras de la base de datos de ejemplo sakila
. Si has seguido las instrucciones para cargar la base de datos en "Ejemplos de modelado de relaciones entre entidades", ya tendrás la base de datos disponible y sabrás cómo restaurarla después de haber modificado sus estructuras.
Cuando termines este capítulo, tendrás todos los conocimientos básicos necesarios para crear, modificar y eliminar estructuras de bases de datos. Junto con las técnicas que aprendiste en el Capítulo 3, tendrás los conocimientos necesarios para realizar una amplia gama de operaciones básicas. Los Capítulos 5 y 7 cubren las habilidades que te permiten realizar operaciones más avanzadas con MySQL.
Crear y utilizar bases de datos
Cuando hayas terminado de diseñar una base de datos, el primer paso práctico que debes dar con MySQL es crearla. Esto se hace con la sentencia CREATE DATABASE
. Supongamos que quieres crear una base de datos con el nombre lucy
. Esta es la sentencia que escribirías:
mysql
>
CREATE
DATABASE
lucy
;
Query OK, 1 row affected (0.10 sec)
Asumimos aquí que sabes cómo conectarte utilizando el cliente MySQL, tal y como se describe en el Capítulo 1. También suponemos que puedes conectarte como usuario root o como otro usuario que pueda crear, eliminar y modificar estructuras (encontrarás una discusión detallada sobre los privilegios de usuario en el Capítulo 8). Observa que cuando creas la base de datos, MySQL dice que se ha visto afectada una fila. En realidad, no se trata de una fila normal en una base de datos concreta, sino de una nueva entrada añadida a la lista que puedes ver en con el comando SHOW DATABASES
.
Una vez que hayas creado la base de datos, el siguiente paso es utilizar -es decir, elegirla como la base de datos con la que vas a trabajar. Esto se hace con el comando MySQL USE
:
mysql
>
USE
lucy
;
Database changed
Este comando debe introducirse en una sola línea y no es necesario que termine con punto y coma, aunque solemos hacerlo automáticamente por costumbre. Una vez que hayas utilizado (seleccionado) la base de datos, puedes empezar a crear tablas, índices y otras estructuras siguiendo los pasos que se explican en la siguiente sección.
Antes de pasar a la creación de otras estructuras, vamos a discutir algunas características y limitaciones de la creación de bases de datos. En primer lugar, veamos qué ocurre si intentas crear una base de datos que ya existe:
mysql
>
CREATE
DATABASE
lucy
;
ERROR 1007 (HY000): Can't create database 'lucy'; database exists
Puedes evitar este error añadiendo la frase clave IF NOT EXISTS
a ladeclaración:
mysql
>
CREATE
DATABASE
IF
NOT
EXISTS
lucy
;
Query OK, 0 rows affected (0.00 sec)
Puedes ver que MySQL no se ha quejado, pero tampoco ha hecho nada: el mensaje 0 rows affected
indica que no se ha modificado ningún dato. Este añadido es útil cuando añades sentencias SQL a un script: evita que el script aborte por error.
Veamos cómo elegir nombres de bases de datos y utilizar mayúsculas y minúsculas. Los nombres de las bases de datos definen los nombres físicos de los directorios (o carpetas) en el disco. En algunos sistemas operativos, los nombres de directorio distinguen entre mayúsculas y minúsculas; en otros, las mayúsculas y minúsculas no importan. Por ejemplo, los sistemas tipo Unix, como Linux y macOS, suelen distinguir entre mayúsculas y minúsculas, mientras que Windows no. El resultado es que los nombres de las bases de datos tienen las mismas restricciones: cuando las mayúsculas son importantes para el sistema operativo, también lo son para MySQL. Por ejemplo, en una máquina Linux, LUCY
, lucy
, y Lucy
son nombres de bases de datos diferentes; en Windows, se refieren a una sola base de datos. Utilizar mayúsculas incorrectas en Linux o macOS hará que MySQL se queje:
mysql
>
SELECT
SaKilA
.
AcTor_id
FROM
ACTor
;
ERROR 1146 (42S02): Table 'sakila.ACTor' doesn't exist
Pero en Windows, esto funcionará normalmente.
Consejo
Para que tu SQL sea independiente de la máquina, te recomendamos que utilices sistemáticamente nombres en minúsculas para las bases de datos (y para las tablas, columnas, alias e índices). Sin embargo, no es un requisito, y como han demostrado los ejemplos anteriores de este libro, puedes utilizar cualquier convención de nombres con la que te sientas cómodo. Sólo tienes que ser coherente y recordar cómo se comporta MySQL en diferentes sistemas operativos.
Este comportamiento está controlado por el parámetro lower_case_table_names
. Si se establece en 0
, los nombres de las tablas se almacenan tal y como se especifican, y las comparaciones distinguen entre mayúsculas y minúsculas. Si se establece en 1
, los nombres de las tablas se almacenan en minúsculas en el disco y las comparaciones no distinguen mayúsculas de minúsculas. Si este parámetro se establece en 2
, los nombres de las tablas se almacenan tal y como se indican, pero se comparan en minúsculas. En Windows, el valor por defecto es 1
. En macOS, el valor por defecto es 2
. En Linux, no se admite un valor de 2
; el servidor fuerza el valor a 0
en su lugar.
Existen otras restricciones para los nombres de las bases de datos. Pueden tener como máximo 64 caracteres de longitud. Tampoco debes utilizar palabras reservadas de MySQL, como SELECT
, FROM
, y USE
, como nombres de estructuras; pueden confundir al analizador sintáctico de MySQL, impidiéndole interpretar el significado de tus sentencias. Puedes eludir esta restricción encerrando la palabra reservada entre comillas (`
), pero te costará más acordarte de hacerlo de lo que vale. Además, no puedes utilizar determinados caracteres en los nombres -específicamente, la barra oblicua, la barra invertida, el punto y coma y el punto- y un nombre de base de datos no puede terminar en un espacio en blanco. De nuevo, el uso de estos caracteres confunde al analizador sintáctico de MySQL y puede dar lugar a un comportamiento impredecible. Por ejemplo, esto es lo que ocurre cuando insertas un punto y coma en el nombre de una base de datos:
mysql
>
CREATE
DATABASE
IF
NOT
EXISTS
lu
;
cy
;
Query OK, 1 row affected (0.00 sec) ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'cy' at line 1
Como puede haber más de una sentencia SQL en una sola línea, el resultado es que se crea una base de datos lu
, y luego se genera un error por la sentencia SQL muy corta e inesperada cy;
. Si realmente quieres crear una base de datos con un punto y coma en su nombre, puedes hacerlo con puntos suspensivos:
mysql
>
CREATE
DATABASE
IF
NOT
EXISTS
`lu;cy`
;
Query OK, 1 row affected (0.01 sec)
Y puedes ver que ahora tienes dos bases de datos nuevas:
mysql
>
SHOW
DATABASES
LIKE
`lu%`
;
+----------------+ | Database (lu%) | +----------------+ | lu | | lu;cy | +----------------+ 2 rows in set (0.01 sec)
Crear tablas
Esta sección trata temas sobre las estructuras de las tablas. Te mostramos cómo hacerlo:
-
Crea tablas, mediante ejemplos introductorios.
-
Elige nombres para las tablas y las estructuras relacionadas con ellas.
-
Comprende y elige los tipos de columnas.
-
Comprender y elegir claves e índices.
-
Utiliza la función propia de MySQL
AUTO_INCREMENT
.
Cuando termines esta sección, habrás completado todo el material básico sobre la creación de estructuras de bases de datos; el resto de este capítulo trata de la base de datos de ejemplo sakila
y de cómo modificar y eliminar las estructuras existentes.
Conceptos básicos
Para los ejemplos de esta sección, supondremos que aún no se ha creado la base de datos sakila
. Si quieres seguir los ejemplos y ya has cargado la base de datos, puedes abandonarla para esta sección y volver a cargarla más tarde; al abandonarla se eliminan la base de datos, sus tablas y todos los datos, pero el original es fácil de restaurar siguiendo los pasos de "Ejemplos de modelado de relaciones entre entidades". A continuación te explicamos cómo abandonarla temporalmente:
mysql
>
DROP
DATABASE
sakila
;
Query OK, 23 rows affected (0.06 sec)
La declaración DROP
se trata con más detalle al final de este capítulo, en "Eliminar estructuras".
Para empezar, crea la base de datos sakila
utilizando la sentencia
mysql
>
CREATE
DATABASE
sakila
;
Query OK, 1 row affected (0.00 sec)
A continuación, selecciona la base de datos con:
mysql
>
USE
sakila
;
Database changed
Ya estamos listos para empezar a crear las tablas que contendrán nuestros datos de . Vamos a crear una tabla para guardar los datos de los actores. Por ahora, vamos a tener una estructura simplificada, ya añadiremos más complejidad más adelante. Ésta es la sentencia que utilizaremos
mysql
>
CREATE
TABLE
actor
(
-
>
actor_id
SMALLINT
UNSIGNED
NOT
NULL
DEFAULT
0
,
-
>
first_name
VARCHAR
(
45
)
DEFAULT
NULL
,
-
>
last_name
VARCHAR
(
45
)
,
-
>
last_update
TIMESTAMP
,
-
>
PRIMARY
KEY
(
actor_id
)
-
>
)
;
Query OK, 0 rows affected (0.01 sec)
Que no cunda el pánico: aunque MySQL informe de que se han visto afectadas cero filas, ha creado la tabla:
mysql
>
SHOW
tables
;
+------------------+ | Tables_in_sakila | +------------------+ | actor | +------------------+ 1 row in set (0.01 sec)
Veamos todo esto en detalle. El comando CREATE TABLE
tiene tres secciones principales:
-
La sentencia
CREATE TABLE
, que va seguida del nombre de la tabla a crear. En este ejemplo, esactor
. -
Una lista de una o varias columnas que se añadirán a la tabla. En este ejemplo, hemos añadido unas cuantas:
actor_id SMALLINT UNSIGNED NOT NULL DEFAULT 0
,first_name VARCHAR(45) DEFAULT NULL
,last_name VARCHAR(45)
, ylast_update TIMESTAMP
. Hablaremos de ellas dentro de un momento. -
Definiciones de clave opcionales. En este ejemplo, hemos definido una única clave:
PRIMARY KEY (actor_id)
. Hablaremos en detalle de las claves y los índices más adelante en este capítulo.
Observa que el componente CREATE TABLE
va seguido de un paréntesis de apertura que va acompañado de un paréntesis de cierre al final de la frase. Observa también que los demás componentes están separados por comas. Hay otros elementos que puedes añadir a una declaración CREATE TABLE
, y hablaremos de algunos de ellos dentro de un momento.
Hablemos de las especificaciones de las columnas. La sintaxis básica es la siguiente: name type [NOT NULL | NULL] [DEFAULT value]
. El campo name
es el nombre de la columna, y tiene las mismas limitaciones que los nombres de las bases de datos, como se ha comentado en el apartado anterior. Puede tener un máximo de 64 caracteres, no se permiten barras diagonales ni diagonales inversas, no se permiten puntos, no puede terminar en espacios en blanco y la distinción entre mayúsculas y minúsculas depende del sistema operativo subyacente. El campo type
define cómo y qué se almacena en la columna; por ejemplo, hemos visto que puede definirse como VARCHAR
para cadenas, SMALLINT
para números o TIMESTAMP
para fecha y hora.
Si especificas NOT NULL
, una fila no es válida sin un valor para la columna; si especificas NULL
u omites esta cláusula, puede existir una fila sin un valor para la columna. Si especificas un value
con la cláusula DEFAULT
, se utilizará para rellenar la columna cuando no proporciones datos de otro modo; esto es especialmente útil cuando reutilizas con frecuencia un valor por defecto, como el nombre de un país. La cláusula value
debe ser una constante (como 0
, "cat"
, o 20060812045623
), excepto si la columna es del tipo TIMESTAMP
. Los tipos se tratan en detalle en "Tipos de columnas".
Las funciones NOT NULL
y DEFAULT
pueden utilizarse juntas. Si especificas NOT NULL
y añades un valor DEFAULT
, se utilizará el valor por defecto cuando no proporciones un valor para la columna. A veces, esto funciona bien:
mysql
>
INSERT
INTO
actor
(
first_name
)
VALUES
(
'John'
)
;
Query OK, 1 row affected (0.01 sec)
Y a veces no:
mysql
>
INSERT
INTO
actor
(
first_name
)
VALUES
(
'Elisabeth'
)
;
ERROR 1062 (23000): Duplicate entry '0' for key 'actor.PRIMARY'
Que funcione o no depende de las restricciones y condiciones subyacentes de la base de datos: en este ejemplo, actor_id
tiene un valor por defecto de 0
, pero también es la clave primaria. Tener dos filas con el mismo valor de clave primaria no está permitido, por lo que el segundo intento de insertar una fila sin valores (y un valor de clave primaria resultante de 0
) falla. Hablamos en detalle de las claves primarias en "Claves e índices".
Los nombres de columnas tienen menos restricciones que los nombres de bases de datos y tablas . Además, los nombres no distinguen entre mayúsculas y minúsculas y son portables en todas las plataformas. Todos los caracteres están permitidos en los nombres de columna, aunque si quieres terminarlos con espacios en blanco o incluir puntos u otros caracteres especiales, como un punto y coma o un guión, tendrás que encerrar el nombre entre comillas (`
). Una vez más, te recomendamos que elijas sistemáticamente nombres en minúsculas para las opciones impulsadas por el desarrollador (como los nombres de bases de datos, alias y tablas) y evites los caracteres que te obliguen a recordar el uso de puntos y comas.
Nombrar las columnas y otros objetos de la base de datos es algo de preferencia personal cuando se empieza de nuevo (puedes inspirarte mirando las bases de datos de ejemplo) o una cuestión de seguir las normas cuando se trabaja en una base de código existente. En general, procura evitar las repeticiones: en una tabla llamada actor
, utiliza el nombre de columna first_name
en lugar de actor_first_name
, que parecería redundante cuando fuera precedido por el nombre de la tabla en una consulta compleja (actor.actor_first_name
frente a actor.first_name
). Una excepción a esto es cuando se utiliza el omnipresente nombre de columna id
; evita utilizarlo o antepón el nombre de la tabla para mayor claridad (por ejemplo, actor_id
). Es una buena práctica utilizar el carácter de subrayado para separar palabras. Podrías utilizar otro carácter, como un guión o una barra oblicua, pero tendrías que acordarte de encerrar los nombres con puntos suspensivos (por ejemplo,
). También puedes omitir por completo el formato de separación de palabras, pero podría decirse que "CamelCase" es más difícil de leer. Al igual que ocurre con los nombres de bases de datos y tablas, la longitud máxima permitida para un nombre de columna es de 64 caracteres.actor-id
Intercalación y juegos de caracteres
Cuando comparas u ordenas cadenas, la forma en que MySQL evalúa el resultado depende del conjunto de caracteres y de la intercalación utilizados. Los juegos de caracteres, o charsets, definen qué caracteres pueden almacenarse; por ejemplo, puede que necesites almacenar caracteres no ingleses como ю o ü. Una intercalación define cómo se ordenan las cadenas, y hay diferentes intercalaciones para los distintos idiomas: por ejemplo, la posición del carácter ü en el alfabeto es diferente en dos ordenaciones alemanas, y diferente de nuevo en sueco y finlandés. Como no todo el mundo quiere almacenar cadenas en inglés, es importante que un servidor de bases de datos sea capaz de gestionar caracteres no ingleses y distintas formas de ordenarcaracteres.
Entendemos que hablar de colaciones y conjuntos de caracteres puede parecer demasiado avanzado cuando estás empezando a aprender MySQL. Sin embargo, también pensamos que son temas que merece la pena tratar, ya que los conjuntos de caracteres y las colaciones no coincidentes pueden provocar situaciones inesperadas, como la pérdida de datos y resultados incorrectos de las consultas. Si lo prefieres, puedes saltarte esta sección y algunas de las discusiones posteriores de este capítulo y volver a estos temas cuando quieras aprender sobre ellos específicamente. Eso no afectará a tu comprensión del resto del material de este libro.
En nuestros ejemplos anteriores de comparación de cadenas, ignoramos la cuestión de la colación y el conjunto de caracteres y dejamos que MySQL utilizara sus valores por defecto. En versiones de MySQL anteriores a la 8.0, el juego de caracteres por defecto es latin1
, y la colación por defecto es latin1_swedish_ci
. MySQL 8.0 cambió los valores por defecto, y ahora el juego de caracteres por defecto es utf8mb4
, y la intercalación por defecto es utf8mb4_0900_ai_ci
. MySQL puede configurarse para utilizar diferentes conjuntos de caracteres y órdenes de intercalación en los niveles de conexión, base de datos, tabla y columna. Los resultados mostrados aquí son de MySQL 8.0.
Puedes listar los juegos de caracteres disponibles en tu servidor con el comando SHOW CHARACTER SET
. Esto muestra una breve descripción de cada juego de caracteres, su intercalación por defecto y el número máximo de bytes utilizados para cada carácter en ese juego de caracteres:
mysql
>
SHOW
CHARACTER
SET
;
+----------+---------------------------------+---------------------+--------+ | Charset | Description | Default collation | Maxlen | +----------+---------------------------------+---------------------+--------+ | armscii8 | ARMSCII-8 Armenian | armscii8_general_ci | 1 | | ascii | US ASCII | ascii_general_ci | 1 | | big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 | | binary | Binary pseudo charset | binary | 1 | | cp1250 | Windows Central European | cp1250_general_ci | 1 | | cp1251 | Windows Cyrillic | cp1251_general_ci | 1 | | ... | | ujis | EUC-JP Japanese | ujis_japanese_ci | 3 | | utf16 | UTF-16 Unicode | utf16_general_ci | 4 | | utf16le | UTF-16LE Unicode | utf16le_general_ci | 4 | | utf32 | UTF-32 Unicode | utf32_general_ci | 4 | | utf8 | UTF-8 Unicode | utf8_general_ci | 3 | | utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 | +----------+---------------------------------+---------------------+--------+ 41 rows in set (0.00 sec)
Por ejemplo, el juego de caracteres latin1
es en realidad, el juego de caracteres de la página de código 1252 de Windows que admite las lenguas de Europa Occidental. La intercalación por defecto para este juego de caracteres es latin1_swedish_ci
, que sigue las convenciones suecas para ordenar los caracteres acentuados (el inglés se maneja como cabría esperar). Esta intercalación no distingue entre mayúsculas y minúsculas, como indican las letras ci
. Por último, cada carácter ocupa 1 byte. En comparación, si utilizas el juego de caracteres por defecto utf8mb4
, cada carácter ocupará hasta 4 bytes de almacenamiento. A veces, tiene sentido cambiar el valor por defecto. Por ejemplo, no hay razón para almacenar datos codificados en base64 (que, por definición, son ASCII) en utf8mb4
.
Del mismo modo, puedes enumerar las órdenes de cotejo y los conjuntos de caracteres a los que se aplican:
mysql
>
SHOW
COLLATION
;
+---------------------+----------+-----+---------+...+---------------+ | Collation | Charset | Id | Default |...| Pad_attribute | +---------------------+----------+-----+---------+...+---------------+ | armscii8_bin | armscii8 | 64 | |...| PAD SPACE | | armscii8_general_ci | armscii8 | 32 | Yes |...| PAD SPACE | | ascii_bin | ascii | 65 | |...| PAD SPACE | | ascii_general_ci | ascii | 11 | Yes |...| PAD SPACE | | ... |...| | | utf8mb4_0900_ai_ci | utf8mb4 | 255 | Yes |...| NO PAD | | utf8mb4_0900_as_ci | utf8mb4 | 305 | |...| NO PAD | | utf8mb4_0900_as_cs | utf8mb4 | 278 | |...| NO PAD | | utf8mb4_0900_bin | utf8mb4 | 309 | |...| NO PAD | | ... |...| | | utf8_unicode_ci | utf8 | 192 | |...| PAD SPACE | | utf8_vietnamese_ci | utf8 | 215 | |...| PAD SPACE | +---------------------+----------+-----+---------+...+---------------+ 272 rows in set (0.02 sec)
Nota
El número de juegos de caracteres y colaciones disponibles depende de cómo se haya construido y empaquetado el servidor MySQL. Los ejemplos que mostramos proceden de una instalación por defecto de MySQL 8.0, y se pueden ver los mismos números en Linux y Windows. MariaDB 10.5, sin embargo, tiene 322 colaciones pero 40 juegos de caracteres.
Puedes ver los valores predeterminados actuales en tu servidor de la siguiente manera:
mysql
>
SHOW
VARIABLES
LIKE
'c%'
;
+--------------------------+--------------------------------+ | Variable_name | Value | +--------------------------+--------------------------------+ | ... | | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql-8.0/charsets/ | | ... | | collation_connection | utf8mb4_0900_ai_ci | | collation_database | utf8mb4_0900_ai_ci | | collation_server | utf8mb4_0900_ai_ci | | ... | +--------------------------+--------------------------------+ 21 rows in set (0.00 sec)
Al crear una base de datos, puedes establecer el juego de caracteres por defecto y el orden de clasificación para la base de datos y sus tablas. Por ejemplo, si quieres utilizar el juego de caracteres utf8mb4
y el orden de intercalación utf8mb4_ru_0900_as_cs
(distingue mayúsculas de minúsculas), escribirías:
mysql
>
CREATE
DATABASE
rose
DEFAULT
CHARACTER
SET
utf8mb4
-
>
COLLATE
utf8mb4_ru_0900_as_cs
;
Query OK, 1 row affected (0.00 sec)
Normalmente, no hay necesidad de hacer esto si has instalado MySQL correctamente para tu idioma y región y si no planeas internacionalizar tu aplicación. Con utf8mb4
por defecto desde MySQL 8.0, hay incluso menos necesidad de cambiar el conjunto de caracteres. También puedes controlar el conjunto de caracteres y la colación para tablas o columnas individuales, pero no entraremos aquí en los detalles de cómo hacerlo. Hablaremos de cómo afectan las colaciones a los tipos de cadenas en "Tipos de cadenas".
Otras características
Esta sección describe brevemente otras funciones de la sentencia CREATE TABLE
. Incluye un ejemplo en el que se utiliza la función IF NOT EXISTS
, y una lista de funciones avanzadas y dónde encontrar más información sobre ellas en este libro. La sentencia mostrada es la representación completa de la tabla extraída de la base de datos sakila
, a diferencia del ejemplo simplificado anterior.
Puedes utilizar la frase de palabras clave IF NOT EXISTS
al crear una tabla , y funciona de forma muy similar a las bases de datos. Aquí tienes un ejemplo que no informará de un error aunque la tabla actor
exista:
mysql
>
CREATE
TABLE
IF
NOT
EXISTS
actor
(
-
>
actor_id
SMALLINT
UNSIGNED
NOT
NULL
AUTO_INCREMENT
,
-
>
first_name
VARCHAR
(
45
)
NOT
NULL
,
-
>
last_name
VARCHAR
(
45
)
NOT
NULL
,
-
>
last_update
TIMESTAMP
NOT
NULL
DEFAULT
-
>
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
,
-
>
PRIMARY
KEY
(
actor_id
)
,
-
>
KEY
idx_actor_last_name
(
last_name
)
)
;
Query OK, 0 rows affected, 1 warning (0.01 sec)
Puedes ver que no hay ninguna fila afectada y que se informa de una advertencia. Echemos un vistazo:
mysql
>
SHOW
WARNINGS
;
+-------+------+------------------------------+ | Level | Code | Message | +-------+------+------------------------------+ | Note | 1050 | Table 'actor' already exists | +-------+------+------------------------------+ 1 row in set (0.01 sec)
Existe una amplia gama de funciones adicionales que puedes añadir a una sentencia CREATE TABLE
, de las cuales sólo unas pocas están presentes en este ejemplo. Muchas de ellas son avanzadas y no se tratan en este libro, pero puedes encontrar más información en el Manual de Referencia de MySQL en la sección sobre la sentenciaCREATE TABLE
. Entre estas funciones adicionales se incluyen las siguientes:
- La función
AUTO_INCREMENT
para columnas numéricas -
Esta función te permite crear automáticamente identificadores únicos para una tabla. Lo tratamos en detalle en "La función AUTO_INCREMENTO".
- Comentarios de la columna
-
Puedes añadir un comentario a una columna; esto se muestra cuando utilizas el comando
SHOW CREATE TABLE
, del que hablaremos más adelante en esta sección. - Restricciones de clave foránea
-
Puedes indicar a MySQL que compruebe si los datos de una o varias columnas coinciden con los datos de otra tabla. Por ejemplo, la base de datos
sakila
tiene una restricción de clave foránea en la columnacity_id
de la tablaaddress
, que hace referencia a la columnacity_id
de la tablacity
. Esto significa que es imposible tener una dirección en una ciudad que no esté presente en la tablacity
. Hemos introducido las restricciones de clave externa en el Capítulo 2, y veremos qué motores admiten restricciones de clave externa en "Motores de almacenamiento alternativos". No todos los motores de almacenamiento de MySQL admiten claves foráneas. - Crear tablas temporales
-
Si creas una tabla utilizando la frase clave
CREATE TEMPORARY TABLE
, será eliminada (dropped) cuando se cierre la conexión. Esto es útil para copiar y reformatear datos, porque no tienes que acordarte de limpiarlos. A veces también se utilizan tablas temporales como optimización para mantener algunos datos intermedios. - Opciones avanzadas de la tabla
-
Puedes controlar una amplia gama de características de la tabla utilizando las opciones de la tabla. Entre ellas se incluyen el valor inicial de
AUTO_INCREMENT
, la forma en que se almacenan los índices y las filas, y opciones para anular la información que el optimizador de consultas MySQL obtiene de la tabla. También es posible especificar columnas generadas, que contienen datos como la suma de otras dos columnas, así como índices sobre dichas columnas. - Control de las estructuras de índices
-
Algunos motores de almacenamiento de MySQL te permiten especificar y controlar qué tipo de estructura interna -como un árbol B o una tabla hash- utiliza MySQL para sus índices. También puedes decirle a MySQL que quieres un índice de texto completo o de datos espaciales en una columna, lo que permite tipos especiales de búsqueda.
- Partición
-
MySQL admite diferentes estrategias de particionado, que puedes seleccionar en el momento de crear la tabla o posteriormente. En este libro no trataremos la partición.
Puedes ver la sentencia utilizada para crear una tabla utilizando la sentencia SHOW CREATE TABLE
introducida en el Capítulo 3. Esto suele mostrar una salida que incluye algunas de las funciones avanzadas que acabamos de comentar; la salida rara vez coincide con lo que realmente has escrito para crear la tabla. Aquí tienes un ejemplo para la tabla actor
:
mysql
>
SHOW
CREATE
TABLE
actor
\
G
*************************** 1. row *************************** Table: actor Create Table: CREATE TABLE `actor` ( `actor_id` smallint unsigned NOT NULL AUTO_INCREMENT, `first_name` varchar(45) NOT NULL, `last_name` varchar(45) NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`actor_id`), KEY `idx_actor_last_name` (`last_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
Observarás que la salida incluye contenido añadido por MySQL que no estaba en nuestra sentencia CREATE TABLE
original:
-
Los nombres de la tabla y las columnas van entre comillas. Esto no es necesario, pero evita los problemas de análisis que puede causar el uso de palabras reservadas y caracteres especiales, como ya se ha explicado.
-
Se incluye una cláusula adicional por defecto
ENGINE
, que indica explícitamente el tipo de tabla que debe utilizarse. La configuración en una instalación por defecto de MySQL esInnoDB
, por lo que no tiene ningún efecto en este ejemplo. -
Se incluye una cláusula adicional
DEFAULT CHARSET
, que indica a MySQL qué juego de caracteres utilizan las columnas de la tabla. De nuevo, esto no tiene ningún efecto en una instalación por defecto.
Tipos de columnas
Esta sección describe los tipos de columna que puedes utilizar en MySQL. Explica cuándo debe utilizarse cada uno y las limitaciones que tiene. Los tipos están agrupados por su finalidad. Cubriremos los tipos de datos más utilizados y mencionaremos de pasada tipos más avanzados o menos utilizados. Eso no significa que no tengan utilidad, pero considera el aprendizaje de los mismos como un ejercicio. Lo más probable es que no recuerdes cada uno de los tipos de datos y sus entresijos particulares, y no pasa nada. Merece la pena releer este capítulo más adelante y consultar la documentación de MySQL sobre el tema para mantener al día tus conocimientos.
Tipos enteros
Empezaremos con los tipos de datos numéricos, y más concretamente con los tipos enteros, o los tipos que contienen números enteros concretos. En primer lugar, los dos tipos enteros más populares:
INT[(width)] [UNSIGNED] [ZEROFILL]
-
Es el tipo numérico más utilizado; almacena valores enteros (números enteros) en el rango -2.147.483.648 a 2.147.483.647. Si se añade la palabra clave opcional
UNSIGNED
, el rango es de 0 a 4.294.967.295. La palabra claveINT
es la abreviatura deINTEGER
, y pueden utilizarse indistintamente. Una columnaINT
requiere 4 bytes de espacio de almacenamiento.INT
, al igual que otros tipos enteros, tiene dos propiedades específicas para MySQL: argumentos opcionaleswidth
yZEROFILL
argumentos. No forman parte de un estándar SQL, y a partir de MySQL 8.0 están obsoletos. Aún así, seguro que las encuentras en muchas bases de código, así que hablaremos brevemente de ambas.El parámetro
width
especifica la anchura de visualización, que las aplicaciones pueden leer como parte de los metadatos de la columna. A diferencia de los parámetros en una posición similar para otros tipos de datos, este parámetro no tiene ningún efecto sobre las características de almacenamiento de un tipo de entero concreto y no restringe el rango utilizable de valores.INT(4)
yINT(32)
son iguales a efectos de almacenamiento de datos.ZEROFILL
es un argumento adicional que se utiliza para rellenar los valores con ceros a la izquierda hasta la longitud especificada por el parámetrowidth
parámetro. Si utilizasZEROFILL
, MySQL añade automáticamenteUNSIGNED
a la declaración (ya que el relleno de ceros sólo tiene sentido en el contexto de números positivos).En algunas aplicaciones en las que
ZEROFILL
ywidth
son útiles, se puede utilizar la funciónLPAD()
, o almacenar los números formateados en columnasCHAR
. BIGINT[(width)] [UNSIGNED] [ZEROFILL]
-
En el mundo de los datos cada vez más grandes, tener tablas con recuentos de filas demiles de millones es cada vez más común. Incluso las columnas sencillas de tipo
id
pueden necesitar un rango más amplio que el que proporciona unINT
normal.BIGINT
resuelve ese problema. Es un tipo entero grande con un rango con signo de -9.223.372.036.854.775.808 a 9.223.372.036.854.775.807. UnBIGINT
sin signo puede almacenar números de 0 a 18.446.744.073.709.551.615. Las columnas de este tipo necesitarán 8 bytes de almacenamiento.Internamente, todos los cálculos dentro de MySQL se realizan utilizando valores con signo
BIGINT
oDOUBLE
. La consecuencia importante de esto es que debes tener mucho cuidado cuando manejes números extremadamente grandes. Hay dos cuestiones que debes tener en cuenta. En primer lugar, los enteros grandes sin signo mayores que 9.223.372.036.854.775.807 sólo deben utilizarse con funciones de bits. En segundo lugar, si el resultado de una operación aritmética es mayor que 9.223.372.036.854.775.807, podrían observarse resultados inesperados.Por ejemplo:
mysql
>
CREATE
TABLE
test_bigint
(
id
BIGINT
UNSIGNED
)
;
Query OK, 0 rows affected (0.01 sec)
mysql
>
INSERT
INTO
test_bigint
VALUES
(
18446744073709551615
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
test_bigint
VALUES
(
18446744073709551615
-
1
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
test_bigint
VALUES
(
184467440737095516
*
100
)
;
ERROR 1690 (22003): BIGINT value is out of range in '(184467440737095516 * 100)'
Aunque 18.446.744.073.709.551.600 es menor que 18.446.744.073.709.551.615, como internamente se utiliza un
BIGINT
con signo para la multiplicación, se observa el error de fuera de rango.
Consejo
El tipo de datos SERIAL
puede utilizarse como un alias de para BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE
. A menos que debas optimizar el tamaño de los datos y el rendimiento, considera la posibilidad de utilizar SERIAL
para tus columnas de tipo id
. Incluso el UNSIGNED INT
puede agotarse mucho más rápido de lo que cabría esperar, y a menudo en el peor momento posible.
Ten en cuenta que, aunque es posible almacenar cada número entero como BIGINT
, eso supone un desperdicio de espacio de almacenamiento. Además, como ya hemos dicho, el parámetro width
no limita el rango de valores. Para ahorrar espacio y poner restricciones a los valores almacenados, debes utilizar distintos tipos de enteros:
SMALLINT[(width)] [UNSIGNED] [ZEROFILL]
-
Almacena enteros pequeños, con un rango de -32.768 a 32.767 con signo y de 0 a 65.535 sin signo. Ocupa 2 bytes de almacenamiento.
TINYINT[(width)] [UNSIGNED] [ZEROFILL]
-
El tipo de datos numéricos más pequeño, que almacena números enteros aún más pequeños. El rango de este tipo es de -128 a 127 con signo y de 0 a 255 sin signo. Sólo ocupa 1 byte de almacenamiento.
BOOL[(width)]
-
Abreviatura de
BOOLEAN
, y sinónimo deTINYINT(1)
. Normalmente, los tipos booleanos sólo aceptan dos valores: verdadero o falso. Sin embargo, comoBOOL
en MySQL es un tipo entero, puedes almacenar valores de -128 a 127 en unBOOL
. El valor 0 se tratará como falso, y todos los valores distintos de cero como verdaderos. También es posible utilizar alias especialestrue
yfalse
para 1 y 0, respectivamente. Aquí tienes algunos ejemplos:mysql
>
CREATE
TABLE
test_bool
(
i
BOOL
)
;
Query OK, 0 rows affected (0.04 sec)
mysql
>
INSERT
INTO
test_bool
VALUES
(
true
)
,
(
false
)
;
Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0
mysql
>
INSERT
INTO
test_bool
VALUES
(
1
)
,
(
0
)
,
(
-
128
)
,
(
127
)
;
Query OK, 4 rows affected (0.02 sec) Records: 4 Duplicates: 0 Warnings: 0
mysql
>
SELECT
i
,
IF
(
i
,
'true'
,
'false'
)
FROM
test_bool
;
+------+----------------------+ | i | IF(i,'true','false') | +------+----------------------+ | 1 | true | | 0 | false | | 1 | true | | 0 | false | | -128 | true | | 127 | true | +------+----------------------+ 6 rows in set (0.01 sec)
MEDIUMINT[(width)] [UNSIGNED] [ZEROFILL]
-
Almacena valores en el rango con signo de -8.388.608 a 8.388.607 y en el rango sin signo de 0 a 16.777.215. Ocupa 3 bytes de almacenamiento.
BIT[(M)]
-
Tipo especial utilizado para almacenar valores de bits.
M
especifica el número de bits por valor y por defecto es 1 si se omite. MySQL utiliza una sintaxisb'value
para los valores binarios.
Tipos de punto fijo
Los tipos de datos DECIMAL
y NUMERIC
de MySQL son los mismos , por lo que, aunque aquí sólo describiremos DECIMAL
, esta descripción también se aplica a NUMERIC
. La principal diferencia entre los tipos de punto fijo y los de punto flotante es la precisión. Para los tipos de punto fijo, el valor recuperado es idéntico al valor almacenado; no siempre es así con los tipos que contienen puntos decimales, como los tipos FLOAT
y DOUBLE
descritos más adelante. Esta es la propiedad más importante del tipo de datos DECIMAL
, que es un tipo numérico de uso común en MySQL:
DECIMAL[(width[,decimals])] [UNSIGNED] [ZEROFILL]
-
Almacena un número de coma fija, como un salario o una distancia, con un total de
width
dígitos de los cuales algún número menordecimals
que siguen a un punto decimal. Por ejemplo, una columna declarada comoprice DECIMAL(6,2)
puede utilizarse para almacenar valores en el rango de -9.999,99 a 9.999,99.price DECIMAL(10,4)
permitiría valores como 123.456,1234.Antes de MySQL 5.7, si intentabas almacenar un valor fuera de este rango, se almacenaba como el valor más cercano del rango permitido. Por ejemplo, 100 se almacenaría como 99,99, y -100 se almacenaría como -99,99. Sin embargo, a partir de la versión 5.7.5, el modo SQL por defecto incluye el modo
STRICT_TRANS_TABLES
, que prohíbe éste y otros comportamientos inseguros. Utilizar el comportamiento antiguo es posible, pero podría provocar la pérdida de datos.Los modos SQL son configuraciones especiales que controlan el comportamiento de MySQL cuando se trata de consultas. Por ejemplo, pueden restringir el comportamiento "inseguro" o afectar al modo en que se interpretan las consultas. Para el aprendizaje de MySQL, te recomendamos que te ciñas a los valores predeterminados, ya que son seguros. Cambiar los modos SQL puede ser necesario para la compatibilidad con aplicaciones heredadas a través de las versiones de MySQL.
El parámetro
width
es opcional, y se asume un valor de 10 cuando se omite. El número dedecimals
también es opcional y, si se omite, se asume el valor 0; el valor máximo dedecimals
no puede superar el valor dewidth
. El valor máximo dewidth
es 65, y el valor máximo dedecimals
es 30.Si sólo almacenas valores positivos, puedes utilizar la palabra clave
UNSIGNED
como se describe enINT
. Si quieres un relleno cero, utiliza la palabra claveZEROFILL
para obtener el mismo comportamiento que el descrito paraINT
. La palabra claveDECIMAL
tiene tres alternativas idénticas e intercambiables:DEC
,NUMERIC
, yFIXED
.Los valores de las columnas de
DECIMAL
se almacenan utilizando un formato binario. Este formato utiliza 4 bytes por cada nueve dígitos.
Tipos de coma flotante
Además del tipo de punto fijo DECIMAL
descrito en la sección anterior , hay otros dos tipos que admiten puntos decimales: DOUBLE
(también conocido como REAL
) y FLOAT
. Están diseñados para almacenar valores numéricos aproximados en lugar de los valores exactos almacenados por DECIMAL
.
¿Por qué querrías valores aproximados? La respuesta es que muchos números con punto decimal son aproximaciones de cantidades reales. Por ejemplo, supongamos que ganas 50.000 $ al año y quieres almacenarlo como salario mensual. Si lo conviertes en una cantidad mensual, son 4.166 $ más 66 y 2/3 céntimos. Si lo almacenas como 4.166,67 $, no es lo suficientemente exacto como para convertirlo en un salario anual (ya que 12 multiplicado por 4.166,67 $ son 50.000,04 $). Sin embargo, si almacenas 2/3 con suficientes decimales, es una aproximación más cercana. Comprobarás que es lo suficientemente exacta como para multiplicar correctamente y obtener el valor original en un entorno de alta precisión como MySQL, utilizando sólo un poco de redondeo. Ahí es donde DOUBLE
y FLOAT
son útiles: te permiten almacenar valores como 2/3 o pi con un gran número de decimales, permitiendo representaciones aproximadas precisas de cantidades exactas. Posteriormente, puedes utilizar la función ROUND()
para restablecer los resultados a una precisión determinada.
Continuemos con el ejemplo anterior utilizando DOUBLE
. Supongamos que creas una tabla comola siguiente
mysql
>
CREATE
TABLE
wage
(
monthly
DOUBLE
)
;
Query OK, 0 rows affected (0.09 sec)
Ahora puedes introducir el salario mensual utilizando:
mysql
>
INSERT
INTO
wage
VALUES
(
50000
/
12
)
;
Query OK, 1 row affected (0.00 sec)
Y mira lo que hay almacenado:
mysql
>
SELECT
*
FROM
wage
;
+----------------+ | monthly | +----------------+ | 4166.666666666 | +----------------+ 1 row in set (0.00 sec)
Sin embargo, cuando lo multiplicas para obtener un valor anual, obtienes una aproximación de gran precisión:
mysql
>
SELECT
monthly
*
12
FROM
wage
;
+--------------------+ | monthly*12 | +--------------------+ | 49999.999999992004 | +--------------------+ 1 row in set (0.00 sec)
Para recuperar el valor original, aún tienes que realizar el redondeo con la precisión deseada. Por ejemplo, tu empresa podría requerir una precisión de cinco decimales. En este caso, podrías recuperar el valor original con:
mysql
>
SELECT
ROUND
(
monthly
*
12
,
5
)
FROM
wage
;
+---------------------+ | ROUND(monthly*12,5) | +---------------------+ | 50000.00000 | +---------------------+ 1 row in set (0.00 sec)
Pero una precisión de ocho decimales no daría como resultado el valor original:
mysql
>
SELECT
ROUND
(
monthly
*
12
,
8
)
FROM
wage
;
+---------------------+ | ROUND(monthly*12,8) | +---------------------+ | 49999.99999999 | +---------------------+ 1 row in set (0.00 sec)
Es importante comprender la naturaleza imprecisa y aproximada de los tipos de datos en coma flotante.
Aquí tienes los detalles de los tipos FLOAT
y DOUBLE
:
FLOAT[(width, decimals)] [UNSIGNED] [ZEROFILL]
oFLOAT[(precision)] [UNSIGNED] [ZEROFILL]
-
Almacena números en coma flotante. Tiene dos sintaxis opcionales: la primera permite un número opcional de
decimals
y una indicación opcionalwidth
y la segunda permite unaprecision
que controla la precisión de la aproximación medida en bits. Sin parámetros (el uso típico), el tipo almacena valores pequeños de coma flotante de 4 bytes y precisión simple. Cuandoprecision
está entre 0 y 24, se produce el comportamiento por defecto. Cuandoprecision
está entre 25 y 53, el tipo se comporta comoDOUBLE
. El parámetrowidth
no afecta a lo que se almacena, sólo a lo que se muestra. Las opcionesUNSIGNED
yZEROFILL
se comportan como paraINT
. DOUBLE[(width, decimals)] [UNSIGNED] [ZEROFILL]
-
Almacena números en coma flotante. Permite especificar un número opcional de
decimals
y una indicación opcionalwidth
. Sin parámetros (el uso típico), el tipo almacena valores normales de coma flotante de 8 bytes y doble precisión. El parámetrowidth
no afecta a lo que se almacena, sólo a lo que se muestra. Las opcionesUNSIGNED
yZEROFILL
se comportan igual queINT
. El tipoDOUBLE
tiene dos sinónimos idénticos:REAL
yDOUBLE PRECISION
.
Tipos de cadena
Los tipos de datos cadena se utilizan para almacenar texto y, menos obviamente, datos binarios. MySQL admite los siguientes tipos de cadenas:
[NATIONAL] VARCHAR(width) [CHARACTER SET charset_name] [COLLATE collation_name]
-
Probablemente el tipo de cadena más utilizado,
VARCHAR
almacena cadenas de longitud variable hasta un máximo dewidth
. El valor máximo dewidth
es de 65.535 caracteres. La mayor parte de la información aplicable a este tipo se aplicará también a otros tipos de cadena.Los tipos
CHAR
yVARCHAR
son muy similares, pero hay algunas distinciones importantes.VARCHAR
incurre en uno o dos bytes extra de sobrecarga para almacenar el valor de la cadena, dependiendo de si el valor es menor o mayor que 255 bytes. Ten en cuenta que este tamaño es diferente de la longitud de la cadena en caracteres, ya que algunos caracteres pueden requerir hasta 4 bytes de espacio. Podría parecer obvio, entonces, queVARCHAR
es menos eficiente. Sin embargo, esto no siempre es cierto. Dado queVARCHAR
puede almacenar cadenas de longitud arbitraria (hasta la longitudwidth
definido), las cadenas más cortas requerirán menos espacio de almacenamiento que unCHAR
de longitud similar.Otra diferencia entre
CHAR
yVARCHAR
es su gestión de los espacios finales .VARCHAR
conserva los espacios finales hasta el ancho de columna especificado y truncará el exceso, produciendo una advertencia. Como se verá más adelante, los valores deCHAR
se rellenan por la derecha hasta el ancho de columna, y no se conservan los espacios finales. ParaVARCHAR
, los espacios finales son significativos a menos que se recorten y contarán como valores únicos. Hagamos una demostración:mysql
>
CREATE
TABLE
test_varchar_trailing
(
d
VARCHAR
(
2
)
UNIQUE
)
;
Query OK, 0 rows affected (0.02 sec)
mysql
>
INSERT
INTO
test_varchar_trailing
VALUES
(
'a'
)
,
(
'a '
)
;
Query OK, 2 rows affected (0.01 sec) Records: 2 Duplicates: 0 Warnings: 0
mysql
>
SELECT
d
,
LENGTH
(
d
)
FROM
test_varchar_trailing
;
+------+-----------+ | d | LENGTH(d) | +------+-----------+ | a | 1 | | a | 2 | +------+-----------+ 2 rows in set (0.00 sec)
La segunda fila que hemos insertado tiene un espacio al final, y como el valor
width
para la columnad
es 2, ese espacio cuenta para la unicidad de una fila. Sin embargo, si intentamos insertar una fila con dos espacios finalesmysql
>
INSERT
INTO
test_varchar_trailing
VALUES
(
'a '
)
;
ERROR 1062 (23000): Duplicate entry 'a ' for key 'test_varchar_trailing.d'
MySQL se niega a aceptar la nueva fila.
VARCHAR(2)
trunca implícitamente los espacios finales más allá del conjuntowidth
por lo que el valor almacenado cambia de"a "
(con un doble espacio después de a) a"a "
(con un único espacio después de a). Como ya tenemos una fila con ese valor, se produce un error de entrada duplicada. Este comportamiento deVARCHAR
yTEXT
puede controlarse cambiando la intercalación de las columnas. Algunas colaciones, comolatin1_bin
, tienen el atributoPAD SPACE
, lo que significa que al recuperarlas se rellenan hasta elwidth
con espacios. Esto no afecta al almacenamiento, pero sí a las comprobaciones de unicidad y al funcionamiento de los operadoresGROUP BY
yDISTINCT
, que veremos en el Capítulo 5. Puedes comprobar si una intercalación esPAD SPACE
oNO PAD
ejecutando el comandoSHOW COLLATION
, como hemos mostrado en "Intercalación y juegos de caracteres". Veamos el efecto en acción creando una tabla con una intercalaciónPAD SPACE
:mysql
>
CREATE
TABLE
test_varchar_pad_collation
(
-
>
data
VARCHAR
(
5
)
CHARACTER
SET
latin1
-
>
COLLATE
latin1_bin
UNIQUE
)
;
Query OK, 0 rows affected (0.02 sec)
mysql
>
INSERT
INTO
test_varchar_pad_collation
VALUES
(
'a'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
test_varchar_pad_collation
VALUES
(
'a '
)
;
ERROR 1062 (23000): Duplicate entry 'a ' for key 'test_varchar_pad_collation.data'
La intercalación
NO PAD
es una nueva incorporación de MySQL 8.0. En versiones anteriores de MySQL, que todavía puedes ver a menudo en uso, todas las intercalaciones tienen implícitamente el atributoPAD SPACE
. Por lo tanto, en MySQL 5.7 y versiones anteriores, tu única opción para conservar los espacios finales es utilizar un tipo binario:VARBINARY
oBLOB
.Nota
Tanto los tipos de datos
CHAR
comoVARCHAR
no permiten el almacenamiento de valores superiores awidth
a menos que el modo SQL estricto esté desactivado (es decir, si no están activados niSTRICT_ALL_TABLES
niSTRICT_TRANS_TABLES
). Con la protección desactivada, los valores superiores awidth
se truncan y se muestra una advertencia. No recomendamos activar el comportamiento heredado, ya que podría provocar la pérdida de datos.La ordenación y comparación de los tipos
VARCHAR
,CHAR
, yTEXT
se produce según la intercalación del conjunto de caracteres asignado. Puedes ver que es posible especificar el juego de caracteres, así como la intercalación para cada columna individual de tipo cadena. También es posible especificar el juego de caracteresbinary
, que convierte de hechoVARCHAR
enVARBINARY
. No confundas el juego de caracteresbinary
con un atributoBINARY
para un juego de caracteres; este último es una abreviatura exclusiva de MySQL para especificar una intercalación binaria (_bin
).Además, es posible especificar una intercalación directamente en la cláusula
ORDER BY
. Las intercalaciones disponibles dependerán del conjunto de caracteres de la columna. Siguiendo con la tablatest_varchar_pad_collation
, es posible almacenar allí un símbolo ä y ver el efecto que tienen las colaciones en el ordenamiento de las cadenas:mysql
>
INSERT
INTO
test_varchar_pad_collation
VALUES
(
'ä'
)
,
(
'z'
)
;
Query OK, 2 rows affected (0.01 sec) Records: 2 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
test_varchar_pad_collation
-
>
ORDER
BY
data
COLLATE
latin1_german1_ci
;
+------+ | data | +------+ | a | | ä | | z | +------+ 3 rows in set (0.00 sec)
mysql
>
SELECT
*
FROM
test_varchar_pad_collation
-
>
ORDER
BY
data
COLLATE
latin1_swedish_ci
;
+------+ | data | +------+ | a | | z | | ä | +------+ 3 rows in set (0.00 sec)
El atributo
NATIONAL
(o su forma abreviada equivalente,NCHAR
) es una forma estándar de SQL para especificar que una columna de tipo cadena debe utilizar un conjunto de caracteres predefinido. MySQL utilizautf8
como este conjunto de caracteres. Sin embargo, es importante señalar que MySQL 5.7 y 8.0 discrepan sobre qué es exactamenteutf8
: el primero lo utiliza como alias deutf8mb3
, y el segundo deutf8mb4
. Por tanto, es mejor no utilizar el atributoNATIONAL
, así como alias ambiguos. La mejor práctica con cualquier columna y dato relacionado con el texto es ser lo menos ambiguo y específico posible. [NATIONAL] CHAR(width) [CHARACTER SET charset_name] [COLLATE collation_name]
-
CHAR
almacena una cadena de longitud fija (como un nombre, una dirección, o una ciudad) de longitudwidth
. Si no se proporciona unwidth
se asumeCHAR(1)
. El valor máximo dewidth
es 255. Al igual que enVARCHAR
, los valores de las columnasCHAR
siempre se almacenan con la longitud especificada. Una sola letra almacenada en una columnaCHAR(255)
ocupará 255 bytes (en el juego de caractereslatin1
) y se rellenará con espacios. El relleno se elimina al leer los datos, a menos que esté activado el modo SQL dePAD_CHAR_TO_FULL_LENGTH
. Vale la pena mencionar de nuevo que esto significa que las cadenas almacenadas en columnasCHAR
perderán todos sus espacios finales.En el pasado, el
width
de una columnaCHAR
solía llevar asociado un tamaño en bytes. Ahora no siempre es así, y definitivamente no lo es por defecto. Los conjuntos de caracteres multibyte, como el predeterminadoutf8mb4
en MySQL 8.0, pueden dar lugar a valores mucho mayores. De hecho, InnoDB codificará las columnas de longitud fija como columnas de longitud variable si su tamaño máximo supera los 768 bytes. Así, en MySQL 8.0, por defecto InnoDB almacenará una columnaCHAR(255)
como lo haría con una columnaVARCHAR
. He aquí un ejemplo:mysql
>
CREATE
TABLE
test_char_length
(
-
>
utf8char
CHAR
(
10
)
CHARACTER
SET
utf8mb4
-
>
,
asciichar
CHAR
(
10
)
CHARACTER
SET
binary
-
>
)
;
Query OK, 0 rows affected (0.04 sec)
mysql
>
INSERT
INTO
test_char_length
VALUES
(
'Plain text'
,
'Plain text'
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
test_char_length
VALUES
(
'的開源軟體'
,
'Plain text'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
LENGTH
(
utf8char
)
,
LENGTH
(
asciichar
)
FROM
test_char_length
;
+------------------+-------------------+ | LENGTH(utf8char) | LENGTH(asciichar) | +------------------+-------------------+ | 10 | 10 | | 15 | 10 | +------------------+-------------------+ 2 rows in set (0.00 sec)
Como los valores se alinean a la izquierda y se rellenan con espacios a la derecha, y los espacios finales no se tienen en cuenta en absoluto para
CHAR
, es imposible comparar cadenas formadas sólo por espacios. Si te encuentras en una situación en la que eso es importante,VARCHAR
es el tipo de datos que debes utilizar. BINARY[(width)]
yVARBINARY(width)
-
Estos tipos son muy similares a
CHAR
yVARCHAR
, pero almacenan cadenas binarias. Las cadenas binarias tienen el juego de caracteres y la colación especiales debinary
, y su ordenación depende de los valores numéricos de los bytes de los valores almacenados. En lugar de cadenas de caracteres, se almacenan cadenas de bytes. En la discusión anterior sobreVARCHAR
describimos el conjunto de caracteresbinary
y el atributoBINARY
. Sólo el conjunto de caracteresbinary
"convierte" unVARCHAR
oCHAR
en su respectiva formaBINARY
. Aplicar el atributoBINARY
a un conjunto de caracteres no cambiará el hecho de que se almacenen cadenas de caracteres. A diferencia de lo que ocurre conVARCHAR
yCHAR
,width
aquí se trata exactamente del número de bytes. Cuandowidth
se omite enBINARY
, por defecto es 1.Al igual que en
CHAR
, los datos de la columnaBINARY
se rellenan por la derecha. Sin embargo, al ser un dato binario, se rellena utilizando bytes cero, normalmente escritos como0x00
o\0
.BINARY
trata un espacio como carácter significativo, no como relleno. Si necesitas almacenar datos que puedan terminar en bytes cero que sean significativos para ti, utiliza los tiposVARBINARY
oBLOB
.Es importante tener presente el concepto de cadenas binarias cuando trabajes con estos dos tipos de datos. Aunque acepten cadenas, no son sinónimos de los tipos de datos que utilizan cadenas de texto. Por ejemplo, no puedes cambiar las mayúsculas y minúsculas de las letras almacenadas, ya que ese concepto no se aplica realmente a los datos binarios. Eso queda bastante claro cuando consideras los datos reales almacenados. Veamos un ejemplo:
mysql
>
CREATE
TABLE
test_binary_data
(
-
>
d1
BINARY
(
16
)
-
>
,
d2
VARBINARY
(
16
)
-
>
,
d3
CHAR
(
16
)
-
>
,
d4
VARCHAR
(
16
)
-
>
)
;
Query OK, 0 rows affected (0.03 sec)
mysql
>
INSERT
INTO
test_binary_data
VALUES
(
-
>
'something'
-
>
,
'something'
-
>
,
'something'
-
>
,
'something'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
d1
,
d2
,
d3
,
d4
FROM
test_binary_data
;
*************************** 1. row *************************** d1: 0x736F6D657468696E6700000000000000 d2: 0x736F6D657468696E67 d3: something d4: something 1 row in set (0.00 sec)
mysql
>
SELECT
UPPER
(
d2
)
,
UPPER
(
d4
)
FROM
test_binary_data
;
*************************** 1. row *************************** UPPER(d2): 0x736F6D657468696E67 UPPER(d4): SOMETHING 1 row in set (0.01 sec)
Observa cómo el cliente de línea de comandos MySQL muestra realmente los valores de los tipos binarios en formato hexadecimal. Creemos que esto es mucho mejor que las conversiones silenciosas que se realizaban antes de MySQL 8.0, que podían dar lugar a malentendidos. Para recuperar los datos de texto reales, tienes que convertir explícitamente los datos binarios en texto:
mysql
>
SELECT
CAST
(
d1
AS
CHAR
)
d1t
,
CAST
(
d2
AS
CHAR
)
d2t
-
>
FROM
test_binary_data
;
+------------------+-----------+ | d1t | d2t | +------------------+-----------+ | something | something | +------------------+-----------+ 1 row in set (0.00 sec)
También puedes ver que el relleno de
BINARY
se convirtió en espacios al realizar la fundición. BLOB[(width)]
yTEXT[(width)] [CHARACTER SET charset_name] [COLLATE collation_name]
-
BLOB
yTEXT
son tipos de datos utilizados habitualmente para almacenar datos de gran tamaño. Puedes pensar enBLOB
como unVARBINARY
que almacena tantos datos como quieras, y lo mismo paraTEXT
yVARCHAR
. Los tiposBLOB
yTEXT
pueden almacenar hasta 65.535 bytes o caracteres, respectivamente. Como siempre, ten en cuenta que existen conjuntos de caracteres multibyte. El atributowidth
es opcional, y cuando se especifica, MySQL cambiará el tipo de datosBLOB
oTEXT
por el tipo más pequeño capaz de almacenar esa cantidad de datos. Por ejemplo,BLOB(128)
hará que se utiliceTINYBLOB
:mysql
>
CREATE
TABLE
test_blob
(
data
BLOB
(
128
)
)
;
Query OK, 0 rows affected (0.07 sec)
mysql
>
DESC
test_blob
;
+-------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+-------+ | data | tinyblob | YES | | NULL | | +-------+----------+------+-----+---------+-------+ 1 row in set (0.00 sec)
Para el tipo
BLOB
y los tipos relacionados, los datos se tratan exactamente igual que en el caso deVARBINARY
. Es decir, no se asume ningún conjunto de caracteres, y la comparación y la ordenación se basan en los valores numéricos de los bytes reales almacenados. ParaTEXT
, puedes especificar el juego de caracteres y la intercalación exactos que desees. Para ambos tipos y sus variantes, no se realiza ningún relleno enINSERT
, ni ningún recorte enSELECT
, lo que los hace ideales para almacenar datos tal cual. Además, no se permite una cláusulaDEFAULT
, y cuando se crea un índice en una columnaBLOB
oTEXT
, debe definirse un prefijo que limite la longitud de los valores indexados. Hablamos más sobre esto en "Claves e índices".Una posible diferencia entre
BLOB
yTEXT
es su gestión de los espacios finales en. Como ya hemos demostrado,VARCHAR
yTEXT
pueden rellenar cadenas dependiendo de la intercalación utilizada.BLOB
yVARBINARY
utilizan el conjunto de caracteresbinary
con una única intercalaciónbinary
sin relleno y son inmunes a las confusiones de intercalación y problemas relacionados. A veces, puede ser una buena opción utilizar estos tipos para mayor seguridad. Además, antes de MySQL 8.0, eran los únicos tipos que conservaban los espacios finales. TINYBLOB
yTINYTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
-
Son idénticos a
BLOB
yTEXT
, respectivamente, excepto que se puede almacenar un máximo de 255 bytes o caracteres. MEDIUMBLOB
yMEDIUMTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
-
Son idénticos a
BLOB
yTEXT
, respectivamente, excepto que se puede almacenar un máximo de 16.777.215 bytes o caracteres. Los tiposLONG
yLONG VARCHAR
se asignan al tipo de datosMEDIUMTEXT
por compatibilidad. LONGBLOB
yLONGTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
-
Son idénticos a
BLOB
yTEXT
, respectivamente, excepto que se puede almacenar un máximo de 4 GB de datos. Ten en cuenta que éste es un límite duro incluso en el caso deLONGTEXT
, por lo que el número de caracteres en conjuntos de caracteres multibyte puede ser inferior a 4.294.967.295. El tamaño máximo efectivo de los datos que puede almacenar un cliente estará limitado por la cantidad de memoria disponible, así como por el valor de la variablemax_packet_size
, que por defecto es de 64 MiB. ENUM(value1[,value2[, …]]) [CHARACTER SET charset_name] [COLLATE collation_name]
-
Este tipo almacena una lista, o enumeración, de valores de cadena. Una columna de tipo
ENUM
puede establecerse en un valor de la listavalue1
,value2
y así sucesivamente, hasta un máximo de 65.535 valores diferentes. Aunque los valores se almacenan y recuperan como cadenas, lo que se almacena en la base de datos es una representación entera. La columnaENUM
puede contener valoresNULL
(almacenados comoNULL
), la cadena vacía''
(almacenada como0
), o cualquiera de los elementos válidos (almacenados como1
,2
,3
, etc.). Puedes evitar que se acepten los valoresNULL
declarando la columna comoNOT NULL
al crear la tabla.Este tipo ofrece una forma compacta de almacenar valores de una lista de valores predefinidos, como nombres de estados o países. Considera este ejemplo utilizando nombres de frutas; el nombre puede ser cualquiera de los valores predefinidos
Apple
,Orange
, oPear
(además deNULL
y la cadena vacía):mysql
>
CREATE
TABLE
fruits_enum
-
>
(
fruit_name
ENUM
(
'Apple'
,
'Orange'
,
'Pear'
)
)
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
fruits_enum
VALUES
(
'Apple'
)
;
Query OK, 1 row affected (0.00 sec)
Si intentas insertar un valor que no está en la lista, MySQL produce un error para indicarte que no ha almacenado los datos que le pediste:
mysql
>
INSERT
INTO
fruits_enum
VALUES
(
'Banana'
)
;
ERROR 1265 (01000): Data truncated for column 'fruit_name' at row 1
Tampoco se acepta una lista de varios valores permitidos:
mysql
>
INSERT
INTO
fruits_enum
VALUES
(
'Apple,Orange'
)
;
ERROR 1265 (01000): Data truncated for column 'fruit_name' at row 1
Visualizando el contenido de la tabla, puedes ver que no se han almacenado valores no válidos:
mysql
>
SELECT
*
FROM
fruits_enum
;
+------------+ | fruit_name | +------------+ | Apple | +------------+ 1 row in set (0.00 sec)
Las versiones anteriores de MySQL producían una advertencia en lugar de un error y almacenaban una cadena vacía en lugar de un valor no válido. Ese comportamiento puede activarse desactivando el modo SQL estricto por defecto. También es posible especificar un valor por defecto distinto de la cadena vacía:
mysql
>
CREATE
TABLE
new_fruits_enum
-
>
(
fruit_name
ENUM
(
'Apple'
,
'Orange'
,
'Pear'
)
-
>
DEFAULT
'Pear'
)
;
Query OK, 0 rows affected (0.01 sec)
mysql
>
INSERT
INTO
new_fruits_enum
VALUES
(
)
;
Query OK, 1 row affected (0.02 sec)
mysql
>
SELECT
*
FROM
new_fruits_enum
;
+------------+ | fruit_name | +------------+ | Pear | +------------+ 1 row in set (0.00 sec)
Aquí, si no se especifica un valor, se almacena el valor por defecto
Pear
. SET( value1 [, value2 [, …]]) [CHARACTER SET charset_name] [COLLATE collation_name]
-
Este tipo almacena un conjunto de valores de cadena. Una columna de tipo
SET
se puede establecer en cero o más valores de la listavalue1
,value2
y así sucesivamente, hasta un máximo de 64 valores diferentes. Aunque los valores son cadenas, lo que se almacena en la base de datos es una representación entera.SET
se diferencia deENUM
en que cada fila sólo puede almacenar un valorENUM
en una columna, pero puede almacenar varios valoresSET
. Este tipo es útil para almacenar una selección de opciones de una lista, como las preferencias del usuario. Considera este ejemplo utilizando nombres de frutas; el nombre puede ser cualquier combinación de los valores predefinidos:mysql
>
CREATE
TABLE
fruits_set
-
>
(
fruit_name
SET
(
'Apple'
,
'Orange'
,
'Pear'
)
)
;
Query OK, 0 rows affected (0.08 sec)
mysql
>
INSERT
INTO
fruits_set
VALUES
(
'Apple'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
fruits_set
VALUES
(
'Banana'
)
;
ERROR 1265 (01000): Data truncated for column 'fruit_name' at row 1
mysql
>
INSERT
INTO
fruits_set
VALUES
(
'Apple,Orange'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
*
FROM
fruits_set
;
+--------------+ | fruit_name | +--------------+ | Apple | | Apple,Orange | +--------------+ 2 rows in set (0.00 sec)
De nuevo, ten en cuenta que podemos almacenar varios valores del conjunto en un solo campo y que se almacena una cadena vacía para las entradas no válidas.
Al igual que con los tipos numéricos, te recomendamos que elijas siempre el tipo más pequeño posible para almacenar valores. Por ejemplo, si estás almacenando el nombre de una ciudad, utiliza CHAR
o VARCHAR
en lugar de, por ejemplo, el tipo TEXT
. Tener columnas más cortas ayuda a mantener bajo el tamaño de tu tabla, lo que a su vez ayuda al rendimiento cuando el servidor tiene que buscar en una tabla.
Utilizar un tamaño fijo con el tipo CHAR
suele ser más rápido que utilizar un tamaño variable con VARCHAR
, ya que el servidor MySQL sabe dónde empieza y acaba cada fila y puede saltarse rápidamente las filas para encontrar la que necesita. Sin embargo, con los campos de longitud fija, todo el espacio que no utilices se desperdicia. Por ejemplo, si permites hasta 40 caracteres en el nombre de una ciudad, CHAR(40)
utilizará siempre 40 caracteres, independientemente de la longitud real del nombre de la ciudad. Si declaras que el nombre de la ciudad es VARCHAR(40)
, sólo utilizarás el espacio que necesites, más 1 byte para almacenar la longitud del nombre. Si el nombre medio de una ciudad tiene 10 caracteres, esto significa que utilizar un campo de longitud variable ocupará una media de 29 bytes menos por entrada. Esto puede suponer una gran diferencia si almacenas millones de direcciones.
En general, si el espacio de almacenamiento es escaso o esperas grandes variaciones en la longitud de las cadenas que se van a almacenar, utiliza un campo de longitud variable; si el rendimiento es una prioridad, utiliza un campo de longitud fija.
Tipos de fecha y hora
Estos tipos sirven para almacenar determinadas marcas de tiempo, fechas o intervalos de tiempo. Hay que tener especial cuidado con las zonas horarias. Intentaremos explicar los detalles, pero merece la pena releer esta sección y la documentación más adelante, cuando necesites trabajar realmente con husos horarios. Los tipos de fecha y hora en MySQL son:
DATE
-
Almacena y muestra una fecha en el formato
YYYY-MM-DD
para el intervalo 1000-01-01 a 9999-12-31. Las fechas deben introducirse siempre como triples de año, mes y día, pero el formato de la entrada puede variar, como se muestra en los siguientes ejemplos:YYYY-MM-DD
oYY-MM-DD
-
Es opcional si proporcionas años de dos o cuatro dígitos. Te recomendamos encarecidamente que utilices la versión de cuatro dígitos para evitar confusiones sobre el siglo. En la práctica, si utilizas la versión de dos dígitos, verás que del 70 al 99 se interpretan como de 1970 a 1999, y del 00 al 69 como de 2000 a 2069.
YYYY/MM/DD
,YYYY:MM:DD
,YY-MM-DD
, u otros formatos puntuados-
MySQL permite cualquier carácter de puntuación para separar los componentes de una fecha. Recomendamos utilizar guiones y, de nuevo, evitar los años de dos dígitos.
YYYY-M-D
,YYYY-MM-D
oYYYY-M-DD
-
Cuando se utilizan signos de puntuación (de nuevo, se permite cualquier carácter de puntuación), los días y meses de un solo dígito pueden especificarse como tales. Por ejemplo, el 2 de febrero de 2006 puede especificarse como
2006-2-2
. Los años de dos dígitos equivalentes están disponibles, pero no se recomiendan. YYYYMMDD
oYYMMDD
-
La puntuación puede omitirse en ambos estilos de fecha, pero las secuencias de dígitos deben tener seis u ocho dígitos de longitud.
También puedes introducir una fecha proporcionando tanto una fecha como una hora en los formatos que se describen más adelante para
DATETIME
yTIMESTAMP
, pero sólo el componente fecha se almacena en una columnaDATE
. Independientemente del tipo de entrada, el tipo de almacenamiento y visualización es siempreYYYY-MM-DD
. La fecha cero0000-00-00
está permitida en todas las versiones y puede utilizarse para representar un valor desconocido o ficticio. Si una fecha de entrada está fuera de rango, se almacena la fecha cero. Sin embargo, sólo las versiones de MySQL hasta la 5.6 inclusive lo permiten por defecto. Tanto la 5.7 como la 8.0 establecen por defecto modos SQL que prohíben este comportamiento:STRICT_TRANS_TABLES
,NO_ZERO_DATE
, yNO_ZERO_IN_DATE
.Si utilizas una versión antigua de MySQL, te recomendamos que añadas estos modos a tu sesión actual:
mysql
>
SET
sql_mode
=
CONCAT
(
@
@
sql_mode
,
-
>
',STRICT_TRANS_TABLES'
,
-
>
',NO_ZERO_DATE'
,
',NO_ZERO_IN_DATE'
)
;
Consejo
También puedes establecer la variable
sql_mode
a nivel global del servidor y en el archivo de configuración. Esta variable debe listar cada modo que quieras que esté habilitado.Aquí tienes algunos ejemplos de inserción de fechas en un servidor MySQL 8.0 con la configuración por defecto:
mysql
>
CREATE
TABLE
testdate
(
mydate
DATE
)
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/0'
)
;
ERROR 1292 (22007): Incorrect date value: '2020/02/0' for column 'mydate' at row 1
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/1'
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/31'
)
;
ERROR 1292 (22007): Incorrect date value: '2020/02/31' for column 'mydate' at row 1
mysql
>
INSERT
INTO
testdate
VALUES
(
'2020/02/100'
)
;
ERROR 1292 (22007): Incorrect date value: '2020/02/100' for column 'mydate' at row 1
Una vez ejecutadas las sentencias
INSERT
, la tabla tendrá los siguientes datos:mysql
>
SELECT
*
FROM
testdate
;
+------------+ | mydate | +------------+ | 2020-02-01 | +------------+ 1 row in set (0.00 sec)
MySQL te protegía de tener datos "malos" almacenados en tu tabla. A veces puedes necesitar conservar la entrada real y procesarla manualmente más tarde. Puedes hacerlo eliminando los modos SQL mencionados de la lista de modos de la variable
sql_mode
. En ese caso, después de ejecutar las sentenciasINSERT
anteriores, acabarías con los siguientes datos:mysql
>
SELECT
*
FROM
testdate
;
+------------+ | mydate | +------------+ | 2020-02-00 | | 2020-02-01 | | 0000-00-00 | | 0000-00-00 | +------------+ 4 rows in set (0.01 sec)
Observa de nuevo que la fecha se muestra en el formato
YYYY-MM-DD
independientemente de cómo se haya introducido. TIME [fraction]
-
Almacena una hora en el formato
HHH:MM:SS
para el intervalo -838:59:59 a 838:59:59. Esto es útil para almacenar la duración de alguna actividad. Los valores que se pueden almacenar están fuera del rango del reloj de 24 horas para permitir calcular y almacenar grandes diferencias entre valores de tiempo (hasta 34 días, 22 horas, 59 minutos y 59 segundos).fraction
enTIME
y otros tipos de datos relacionados especifica la precisión fraccionaria en segundos en el rango de 0 a 6. El valor por defecto es 0, lo que significa que no se conservan los segundos fraccionarios.Las horas deben introducirse siempre en el orden días, horas, minutos, segundos, utilizando los siguientes formatos:
DD HH:MM:SS[.fraction]
,HH:MM:SS[.fraction]
,DD HH:MM
,HH:MM
,DD HH
oSS[.fraction]
-
DD
representa un valor de días de uno o dos dígitos en el intervalo de 0 a 34. El valorDD
se separa del valor de la horaHH
por un espacio, mientras que los demás componentes se separan por dos puntos. Ten en cuenta queMM:SS
no es unacombinación válida, ya que no se puede desambiguar deHH:MM
. Si la definición deTIME
no especificafraction
o lo establece en 0, la inserción de segundos fraccionarios hará que los valores se redondeen al segundo más próximo.Por ejemplo, si insertas
2 13:25:58.999999
en una columnaTIME
con unfraction
de 0, se almacena el valor61:25:59
, ya que la suma de 2 días (48 horas) y 13 horas es 61 horas. A partir de MySQL 5.7, el modo SQL establecido por defecto prohíbe la inserción de valores incorrectos. Sin embargo, es posible activar el comportamiento anterior. Entonces, si intentas insertar un valor que está fuera de los límites, se genera una advertencia y el valor se limita al tiempo máximo disponible. Del mismo modo, si intentas insertar un valor no válido, se genera una advertencia y el valor se pone a cero. Puedes utilizar el comandoSHOW WARNINGS
para informar de los detalles de la advertencia generada por la sentencia SQL anterior. Nuestra recomendación es que te ciñas al modo SQL estricto por defecto. A diferencia de lo que ocurre con el tipoDATE
, aparentemente no hay ninguna ventaja en permitir entradas incorrectas enTIME
, aparte de una gestión de errores más sencilla por parte de la aplicación y de mantener los comportamientos heredados.Vamos a probar todo esto en la práctica:
mysql
>
CREATE
TABLE
test_time
(
id
SMALLINT
,
mytime
TIME
)
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
test_time
VALUES
(
1
,
"2 13:25:59"
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
INSERT
INTO
test_time
VALUES
(
2
,
"35 13:25:59"
)
;
ERROR 1292 (22007): Incorrect time value: '35 13:25:59' for column 'mytime' at row 1
mysql
>
INSERT
INTO
test_time
VALUES
(
3
,
"900.32"
)
;
Query OK, 1 row affected (0.00 sec)
mysql
>
SELECT
*
FROM
test_time
;
+------+----------+ | id | mytime | +------+----------+ | 1 | 61:25:59 | | 3 | 00:09:00 | +------+----------+ 2 rows in set (0.00 sec)
H:M:S
y combinaciones de uno, dos y tres dígitos-
Puedes utilizar diferentes combinaciones de dígitos al insertar o actualizar datos; MySQL los convierte al formato de hora interno y los muestra de forma coherente. Por ejemplo,
1:1:3
equivale a01:01:03
. Se pueden mezclar diferentesnúmeros de dígitos; por ejemplo,1:12:3
equivale a01:12:03
. Considera estos ejemplos:mysql
>
CREATE
TABLE
mytime
(
testtime
TIME
)
;
Query OK, 0 rows affected (0.12 sec)
mysql
>
INSERT
INTO
mytime
VALUES
-
>
(
'-1:1:1'
)
,
(
'1:1:1'
)
,
-
>
(
'1:23:45'
)
,
(
'123:4:5'
)
,
-
>
(
'123:45:6'
)
,
(
'-123:45:6'
)
;
Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
mytime
;
+------------+ | testtime | +------------+ | -01:01:01 | | 01:01:01 | | 01:23:45 | | 123:04:05 | | 123:45:06 | | -123:45:06 | +------------+ 5 rows in set (0.01 sec)
Ten en cuenta que las horas se muestran con dos dígitos para los valores comprendidos entre -99 y 99.
HHMMSS
,MMSS
ySS
-
Se pueden omitir los signos de puntuación, pero las secuencias de dígitos deben tener una longitud de dos, cuatro o seis dígitos. Ten en cuenta que el par de dígitos situado más a la derecha siempre se interpreta como un valor de
SS
(segundos), el segundo par de la derecha (si está presente) comoMM
(minutos), y el tercer par de la derecha (si está presente) comoHH
(horas). El resultado es que un valor como1222
se interpreta como 12 minutos y 22 segundos, no como 12 horas y 22 minutos.También puedes introducir una hora proporcionando tanto una fecha como una hora en los formatos descritos para
DATETIME
yTIMESTAMP
, pero sólo el componente de la hora se almacena en una columnaTIME
. Independientemente del tipo de entrada, el tipo de almacenamiento y visualización es siempreHH:MM:SS
. La hora cero00:00:00
puede utilizarse para representar un valor desconocido o ficticio.
TIMESTAMP[(fraction)]
-
Almacena y muestra un par de fecha y hora en el formato
YYYY-MM-DD HH:MM:SS[.fraction][time zone offset]
para el rango 1970-01-01 00:00:01.000000 a2038-01-19 03:14:07.999999
. Este tipo es muy similar al tipoDATETIME
, pero hay algunas diferencias. Ambos tipos aceptan un modificador de zona horaria al valor de entrada MySQL 8.0, y ambos tipos almacenarán y presentarán los datos de la misma forma a cualquier cliente de la misma zona horaria. Sin embargo, los valores de las columnas deTIMESTAMP
se almacenan internamente siempre en la zona horaria UTC, lo que permite obtener automáticamente una zona horaria local para clientes situados en zonas horarias diferentes. Esta es una distinción muy importante que debes recordar. Podría decirse queTIMESTAMP
es más cómodo de utilizar cuando se trata de diferentes zonas horarias.Antes de MySQL 5.6, sólo el tipo
TIMESTAMP
admitía la inicialización y actualización automáticas. Además, sólo una única columna de este tipo por tabla podía hacerlo. Sin embargo, a partir de la 5.6, tantoTIMESTAMP
comoDATETIME
admiten estos comportamientos, y cualquier número de columnas puede hacerlo.Los valores almacenados en una columna
TIMESTAMP
siempre coinciden con el modeloYYYY-MM-DD HH:MM:SS[.fraction][time zone offset]
pero los valores pueden proporcionarse en una amplia gama de formatos:YYYY-MM-DD HH:MM:SS
oYY-MM-DD HH:MM:SS
-
Los componentes de fecha y hora siguen las mismas restricciones relajadas que los componentes
DATE
yTIME
descritos anteriormente. Esto incluye la admisión de cualquier carácter de puntuación, incluida (a diferencia deTIME
) la flexibilidad en la puntuación utilizada en el componente de hora. Por ejemplo,0
es válido. YYYYMMDDHHMMSS
oYYMMDDHHMMSS
-
Se pueden omitir los signos de puntuación, pero la cadena debe tener una longitud de 12 ó 14 dígitos. Recomendamos utilizar sólo la versión inequívoca de 14 dígitos, por las razones expuestas para el tipo
DATE
. Puedes especificar valores con otras longitudes sin proporcionar separadores, pero no recomendamos hacerlo.
Veamos la función de actualización automática con más detalle. Puedes controlarla en añadiendo los siguientes atributos a la definición de la columna al crear una tabla, o más tarde, como explicaremos en "Modificación de estructuras":
-
Si quieres que la marca de tiempo se establezca sólo cuando se inserte una nueva fila en la tabla, añade
DEFAULT CURRENT_TIMESTAMP
al final de la declaración de la columna. -
Si no quieres una marca de tiempo por defecto, pero quieres que se utilice la hora actual de siempre que se actualicen los datos de una fila, añade
ON UPDATE CURRENT_TIMESTAMP
al final de la declaración de la columna. -
Si quieres ambas cosas, es decir, que la marca de tiempo se ajuste a la hora actual en cada nueva fila y siempre que se modifique una fila existente, añade
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
al final de la declaración de la columna.
Si no especificas
DEFAULT NULL
oNULL
para una columnaTIMESTAMP
, tendrá0
como valor por defecto. YEAR[(4)]
-
Almacena un año de cuatro dígitos en el intervalo de 1901 a 2155, así como el año cero, 0000. Los valores ilegales se convierten al año cero. Puedes introducir los valores del año como cadenas (como
'2005'
) o enteros (como2005
). El tipoYEAR
requiere 1 byte de espacio de almacenamiento.En versiones anteriores de MySQL, era posible especificar el parámetro
digits
pasando2
o4
. La versión de dos dígitos almacenaba valores de 70 a 69, que representaban de 1970 a 2069. MySQL 8.0 no admite el tipo de dos dígitosYEAR
, y especificar el parámetrodigits
con fines de visualización está obsoleto. DATETIME[(fraction)]
-
Almacena y muestra un par de fecha y hora en el formato
YYYY-MM-DD HH:MM:SS[.fraction][time zone offset]
para el intervalo1000-01-01
00:00:00
a9999-12-31 23:59:59
. Al igual que enTIMESTAMP
, el valor almacenado siempre coincide con la plantillaYYYY-MM-DD HH:MM:SS
, pero el valor puede introducirse en los mismos formatos enumerados en la descripción deTIMESTAMP
. Si asignas sólo una fecha a una columnaDATETIME
, se asume la hora cero00:00:00
. Si asignas sólo una hora a una columnaDATETIME
, se asume la fecha cero0000-00-00
. Este tipo tiene las mismas características de actualización automática queTIMESTAMP
. A menos que se especifique el atributoNOT NULL
para una columnaDATETIME
, el valor por defecto esNULL
; en caso contrario, el valor por defecto es0
. A diferencia deTIMESTAMP
, los valores deDATETIME
no se convierten a la zona horaria UTC para sualmacenamiento.
Otros tipos
Actualmente, a partir de MySQL 8.0, los tipos de datos espaciales y JSON
entran en esta amplia categoría. Utilizarlos es un tema bastante avanzado, y no los trataremos en profundidad.
Los tipos de datos espaciales se ocupan de almacenar objetos geométricos, y MySQL tiene tipos correspondientes a las clases OpenGIS. Trabajar con estos tipos es un tema que merece un libro por sí solo.
El tipo de datos JSON
permite el almacenamiento nativo de documentos JSON válidos. Antes de MySQL 5.7, JSON solía almacenarse en una columna TEXT
o similar. Sin embargo, eso tiene muchas desventajas: por ejemplo, los documentos no se validan, y no se realiza ninguna optimización del almacenamiento (todo el JSON se almacena simplemente en su forma de texto). Con el tipo nativo JSON
, se almacena en formato binario. Si tuviéramos que resumirlo en una frase: utiliza el tipo de datos JSON
para JSON, querido lector.
Claves e índices
Verás que casi todas las tablas que utilices tendrán una cláusula PRIMARY KEY
declarada en su declaración CREATE TABLE
, y a veces varias cláusulas KEY
. Las razones por las que necesitas una clave primaria y claves secundarias se trataron en el Capítulo 2. En esta sección se explica cómo se declaran las claves primarias, qué ocurre entre bastidores cuando lo haces, y por qué puede interesarte crear también otras claves e índices sobre tus datos.
Una clave primaria identifica de forma única cada fila de una tabla. Y lo que es aún más importante, para el motor de almacenamiento InnoDB por defecto, una clave primaria también se utiliza como índice agrupado. Eso significa que todos los datos reales de la tabla se almacenan en una estructura de índices. Esto es diferente de MyISAM, que almacena los datos y los índices por separado. Cuando una tabla utiliza un índice agrupado, se denomina tabla agrupada. Como hemos dicho, en una tabla agrupada cada fila se almacena dentro de un índice, en lugar de almacenarse en lo que suele llamarse un montón. Al agrupar una tabla, sus filas se ordenan según el índice agrupado y se almacenan físicamente en las páginas hoja de ese índice. No puede haber más de un índice agrupado por tabla. Para estas tablas, los índices secundarios se refieren a los registros del índice agrupado en lugar de a las filas reales de la tabla. Esto suele mejorar el rendimiento de las consultas, aunque puede ser perjudicial para las escrituras. InnoDB no te permite elegir entre tablas agrupadas y no agrupadas; se trata de una decisión de diseño que no puedes cambiar.
Las claves primarias suelen ser una parte recomendable de cualquier diseño de base de datos , pero para InnoDB son necesarias. De hecho, si no especificas una cláusula PRIMARY KEY
al crear una tabla InnoDB, MySQL utilizará la primera columna UNIQUE NOT NULL
como base para el índice agrupado. Si no se dispone de dicha columna, se crea un índice agrupado oculto, basado en los valores ID asignados por InnoDB a cada fila.
Dado que InnoDB es el motor de almacenamiento por defecto de MySQL y un estándar de facto en la actualidad, nos centraremos en su comportamiento en este capítulo. Los motores de almacenamiento alternativos como MyISAM, MEMORY o MyRocks se tratarán en "Motores de almacenamiento alternativos".
Como ya se ha dicho, cuando se define una clave primaria, ésta se convierte en un índice agrupado, y todos los datos de la tabla se almacenan en los bloques hoja de ese índice. InnoDB utiliza índices de árbol B (más concretamente, la variante B+árbol), a excepción de los índices sobre tipos de datos espaciales, que utilizan la estructura de árbol R. Otros motores de almacenamiento pueden implementar tipos de índice diferentes, pero cuando no se especifica el motor de almacenamiento de una tabla, puedes asumir que todos los índices son de árbol B.
Tener un índice agrupado, o dicho de otro modo, tener tablas organizadas por índices, acelera las consultas y ordenaciones en las que intervienen las columnas de clave primaria. Sin embargo, un inconveniente es que modificar las columnas de una clave primaria es caro. Por tanto, un buen diseño requerirá una clave primaria basada en columnas que se utilicen frecuentemente para filtrar en las consultas, pero que se modifiquen con poca frecuencia. Recuerda que no tener ninguna clave primaria hará que InnoDB utilice un índice de clúster implícito; por tanto, si no estás seguro de qué columnas elegir para una clave primaria, considera la posibilidad de utilizar una columna sintética similar a id
. Por ejemplo, el tipo de datos SERIAL
podría encajar bien en ese caso.
Alejándonos de los detalles internos de InnoDB, cuando declaras una clave primaria para una tabla en MySQL, se crea una estructura que almacena información sobre dónde se guardan los datos de cada fila de la tabla. Esta información se denomina índice, y su finalidad es acelerar las búsquedas que utilizan la clave primaria. Por ejemplo, cuando declaras PRIMARY KEY (actor_id)
en la tabla actor
de la base de datos sakila
, MySQL crea una estructura que le permite encontrar filas que coincidan con un actor_id
concreto (o con un rango de identificadores) con extrema rapidez.
Esto es útil para emparejar actores con películas o películas con categorías, por ejemplo. Puedes mostrar los índices disponibles en una tabla utilizando elcomando SHOW INDEX
(o SHOW INDEXES
):
mysql
>
SHOW
INDEX
FROM
category
\
G
*************************** 1. row *************************** Table: category Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: category_id Collation: A Cardinality: 16 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: Visible: YES Expression: NULL 1 row in set (0.00 sec)
La cardinalidad es el número de valores únicos del índice; para un índice sobre una clave primaria, es igual al número de filas de la tabla.
Ten en cuenta que todas las columnas que forman parte de una clave primaria deben estar declaradas como NOT NULL
, ya que deben tener un valor para que la fila sea válida. Sin el índice, la única forma de encontrar filas en la tabla es leer cada una del disco y comprobar si coincide con el category_id
que estás buscando. Para tablas con muchas filas, esta búsqueda exhaustiva y secuencial es extremadamente lenta. Sin embargo, no puedes indexarlo todo; volveremos sobre este punto al final de esta sección.
Puedes crear otros índices sobre los datos de una tabla. Lo haces para que otras búsquedas (ya sean sobre otras columnas o combinaciones de columnas) sean rápidas, y para evitar escaneos secuenciales. Por ejemplo, tomemos de nuevo la tabla actor
. Además de tener una claveprimaria en actor_id
, también tiene una clave secundaria en last_name
para mejorar la búsqueda por el apellido de un actor:
mysql
>
SHOW
CREATE
TABLE
actor
\
G
*************************** 1. row *************************** Table: actor Create Table: CREATE TABLE `actor` ( `actor_id` smallint unsigned NOT NULL AUTO_INCREMENT, ... `last_name` varchar(45) NOT NULL, ... PRIMARY KEY (`actor_id`), KEY `idx_actor_last_name` (`last_name`) ) ... 1 row in set (0.00 sec)
Puedes ver que la palabra clave KEY
se utiliza para indicar a MySQL que se necesita un índice adicional. Alternativamente, puedes utilizar la palabra INDEX
en lugar de KEY
. A continuación de esa palabra clave aparece el nombre del índice, y luego se incluye entre paréntesis la columna a indexar. También puedes añadir índices después de crear las tablas; de hecho, puedes cambiar prácticamente cualquier cosa de una tabla después de su creación. Esto se trata en "Modificar estructuras".
Puedes construir un índice sobre más de una columna. Por ejemplo, considera la siguiente tabla, que es una tabla modificada de sakila
:
mysql
>
CREATE
TABLE
customer_mod
(
-
>
customer_id
smallint
unsigned
NOT
NULL
AUTO_INCREMENT
,
-
>
first_name
varchar
(
45
)
NOT
NULL
,
-
>
last_name
varchar
(
45
)
NOT
NULL
,
-
>
varchar
(
50
)
DEFAULT
NULL
,
-
>
PRIMARY
KEY
(
customer_id
)
,
-
>
KEY
idx_names_email
(
first_name
,
last_name
,
)
)
;
Query OK, 0 rows affected (0.06 sec)
Puedes ver que hemos añadido un índice de clave primaria en la columna identificadora customer_id
, y también hemos añadido otro índice -llamado idx_names_email
- que incluye las columnas first_name
, last_name
y email
en este orden. Veamos ahora cómo puedes utilizar ese índice adicional.
Puedes utilizar el índice idx_names_email
para realizar búsquedas rápidas en por combinaciones de las tres columnas de nombre. Por ejemplo, es útil en la siguiente consulta:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
AND
-
>
last_name
=
'Williams'
AND
-
>
=
'rose.w@nonexistent.edu'
;
Sabemos que ayuda a la búsqueda, porque todas las columnas que figuran en el índice se utilizan en la consulta. Puedes utilizar la sentencia EXPLAIN
para comprobar si lo que crees que debería ocurrir está ocurriendo en realidad:
mysql
>
EXPLAIN
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
AND
-
>
last_name
=
'Williams'
AND
-
>
=
'rose.w@nonexistent.edu'
\
G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: customer_mod partitions: NULL type: ref possible_keys: idx_names_email key: idx_names_email key_len: 567 ref: const,const,const rows: 1 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec)
Puedes ver que MySQL informa de que los possible_keys
son idx_names_email
(lo que significa que el índice podría utilizarse para esta consulta) y que el key
que ha decidido utilizar es idx_names_email
. Por tanto, lo que esperas y lo que está ocurriendo es lo mismo, ¡y eso son buenas noticias! Encontrarás más información sobre la sentencia EXPLAIN
en el capítulo 7.
El índice que hemos creado también es útil para realizar consultas sólo sobre la columna first_name
. Por ejemplo, puede ser utilizado por la siguiente consulta:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
;
Puedes volver a utilizar EXPLAIN
para comprobar si se está utilizando el índice. La razón por la que se puede utilizar es porque la columna first_name
es la primera que aparece en el índice. En la práctica, esto significa que el índice agrupa, o almacena junta, la información sobre las filas de todas las personas con el mismo nombre de pila, por lo que el índice puede utilizarse para encontrar a cualquier persona con un nombre de pila coincidente.
El índice también puede utilizarse para búsquedas que incluyan combinaciones de nombre y apellidos, exactamente por las mismas razones que acabamos de exponer. El índice agrupa a las personas con el mismo nombre, y agrupa a las personas con nombres idénticos por apellidos. Por tanto, puede utilizarse para esta consulta:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
first_name
=
'Rose'
AND
-
>
last_name
=
'Williams'
;
Sin embargo, el índice no puede utilizarse para esta consulta porque la columna situada más a la izquierda del índice, first_name
, no aparece en la consulta:
mysql
>
SELECT
*
FROM
customer_mod
WHERE
-
>
last_name
=
'Williams'
AND
-
>
=
'rose.w@nonexistent.edu'
;
El índice debe ayudar a reducir el conjunto de filas a un conjunto menor de posibles respuestas. Para que MySQL pueda utilizar un índice, la consulta debe cumplir las dos condiciones siguientes:
-
La columna situada más a la izquierda en la cláusula
KEY
(oPRIMARY KEY
) debe estar en la consulta. -
La consulta no debe contener cláusulas
OR
para columnas que no estén indexadas.
De nuevo, siempre puedes utilizar la sentencia EXPLAIN
para comprobar si se puede utilizar un índice para una consulta concreta.
Antes de terminar esta sección, aquí tienes algunas ideas sobre cómo elegir y diseñar índices. Cuando te plantees añadir un índice, piensa en lo siguiente:
-
Los índices cuestan espacio en disco, y deben actualizarse cada vez que cambian los datos. Si tus datos cambian con frecuencia, o cambian muchos datos cuando haces una modificación, los índices ralentizarán el proceso. Sin embargo, en la práctica, como las sentencias
SELECT
(lecturas de datos) suelen ser mucho más frecuentes que otras sentencias (modificaciones de datos), los índices suelen ser beneficiosos. -
Añade sólo un índice que se vaya a utilizar con frecuencia. No te molestes en indexar columnas antes de ver qué consultas necesitan tus usuarios y tus aplicaciones. Siempre puedes añadir índices después.
-
Si todas las columnas de un índice se utilizan en todas las consultas, enumera la columna con mayor número de duplicados a la izquierda de la cláusula
KEY
. Esto minimiza el tamaño del índice. -
Cuanto más pequeño sea el índice, más rápido será. Si indexas columnas grandes, obtendrás un índice mayor. Ésta es una buena razón para asegurarte de que tus columnas sean lo más pequeñas posible cuando diseñes tus tablas.
-
Para columnas largas, puedes utilizar sólo un prefijo de los valores de una columna para crear el índice. Puedes hacerlo añadiendo un valor entre paréntesis después de la definición de la columna, como
KEY idx_names_email (first_name(3), last_name(2), email(10))
. Esto significa que sólo se indexan los 3 primeros caracteres defirst_name
, después los 2 primeros caracteres delast_name
, y a continuación 10 caracteres deemail
. ¡Esto supone un ahorro importante respecto a indexar 140 caracteres de las tres columnas! Cuando hagas esto, tu índice será menos capaz de identificar filas de forma única, pero será mucho más pequeño y seguirá siendo razonablemente bueno para encontrar filas coincidentes. Utilizar un prefijo es obligatorio para tipos largos comoTEXT
.
Para terminar esta sección, tenemos que hablar de algunas peculiaridades relativas a las claves secundarias en InnoDB. Recuerda que todos los datos de la tabla se almacenan en las hojas del índice agrupado. Eso significa, utilizando el ejemplo de actor
, que si necesitamos obtener los datos de first_name
al filtrar por last_name
, aunque podamos utilizar idx_actor_last_name
para un filtrado rápido, tendremos que acceder a los datos por la clave primaria. Como consecuencia, cada clave secundaria en InnoDB tiene todas las columnas de clave primaria añadidas a su definición de forma implícita. Tener claves primarias innecesariamente largas en InnoDB da como resultado claves secundarias significativamente hinchadas.
Esto también puede verse en la salida de EXPLAIN
(observa la Extra: Using index
en la primera salida del primer comando):
mysql
>
EXPLAIN
SELECT
actor_id
,
last_name
FROM
actor
WHERE
last_name
=
'Smith'
\
G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ref possible_keys: idx_actor_last_name key: idx_actor_last_name key_len: 182 ref: const rows: 1 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec)
mysql
>
EXPLAIN
SELECT
first_name
FROM
actor
WHERE
last_name
=
'Smith'
\
G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ref possible_keys: idx_actor_last_name key: idx_actor_last_name key_len: 182 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
Efectivamente, idx_actor_last_name
es un índice de cobertura para la primera consulta, lo que significa que InnoDB puede extraer todos los datos necesarios sólo de ese índice. Sin embargo, para la segunda consulta, InnoDB tendrá que hacer una búsqueda adicional en un índice agrupado para obtener el valor de la columna first_name
.
La función AUTO_INCREMENT
La función AUTO_INCREMENT
, propiedad de MySQL, te permite crear un identificador único para una fila sin ejecutar una consulta SELECT
. Así es como funciona. Tomemos de nuevo la tabla simplificada actor
:
mysql
>
CREATE
TABLE
actor
(
-
>
actor_id
smallint
unsigned
NOT
NULL
AUTO_INCREMENT
,
-
>
first_name
varchar
(
45
)
NOT
NULL
,
-
>
last_name
varchar
(
45
)
NOT
NULL
,
-
>
PRIMARY
KEY
(
actor_id
)
-
>
)
;
Query OK, 0 rows affected (0.03 sec)
Es posible insertar filas en esa tabla sin especificar la dirección actor_id
:
mysql
>
INSERT
INTO
actor
VALUES
(
NULL
,
'Alexander'
,
'Kaidanovsky'
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
actor
VALUES
(
NULL
,
'Anatoly'
,
'Solonitsyn'
)
;
Query OK, 1 row affected (0.01 sec)
mysql
>
INSERT
INTO
actor
VALUES
(
NULL
,
'Nikolai'
,
'Grinko'
)
;
Query OK, 1 row affected (0.00 sec)
Al visualizar los datos de esta tabla, puedes ver que cada fila tiene asignado un valor para la columna actor_id
:
mysql
>
SELECT
*
FROM
actor
;
+----------+------------+-------------+ | actor_id | first_name | last_name | +----------+------------+-------------+ | 1 | Alexander | Kaidanovsky | | 2 | Anatoly | Solonitsyn | | 3 | Nikolai | Grinko | +----------+------------+-------------+ 3 rows in set (0.00 sec)
Cada vez que se inserta una nueva fila, se crea un valor único para la columna actor_id
para esa nueva fila.
Considera cómo funciona esta característica. Puedes ver que la columna actor_id
se declara como un número entero con las cláusulas NOT NULL AUTO_INCREMENT
. AUTO_INCREMENT
indica a MySQL que cuando no se proporciona un valor para esta columna, el valor asignado debe ser uno más que el máximo almacenado actualmente en la tabla. La secuencia AUTO_INCREMENT
comienza en 1 para una tabla vacía.
La cláusula NOT NULL
es necesaria para las columnas AUTO_INCREMENT
; cuando insertas NULL
(o 0, aunque esto no es recomendable), el servidor MySQL busca automáticamente el siguiente identificador disponible y lo asigna a la nueva fila. Puedes insertar manualmente valores negativos si la columna no estaba definida como UNSIGNED
; sin embargo, para el siguiente incremento automático, MySQL simplemente utilizará el mayor valor (positivo) de la columna, o empezará desde 1 si no hay valores positivos.
La función AUTO_INCREMENT
tiene los siguientes requisitos:
-
La columna en la que se utiliza debe estar indexada.
-
La columna en la que se utiliza no puede tener un valor
DEFAULT
. -
Sólo puede haber una columna
AUTO_INCREMENT
por tabla.
MySQL admite distintos motores de almacenamiento; hablaremos más sobre ellos en "Motores de almacenamiento alternativos". Cuando utilizas el tipo de tabla MyISAM no predeterminado, puedes utilizar la función AUTO_INCREMENT
en claves que comprenden varias columnas. En efecto, puedes tener varios contadores independientes dentro de una única columna AUTO_INCREMENT
. Sin embargo, esto no es posible con InnoDB.
Aunque la función AUTO_INCREMENT
es útil, no es portable a otros entornos de bases de datos, y oculta los pasos lógicos para crear nuevos identificadores. También puede dar lugar a ambigüedades; por ejemplo, al eliminar o truncar una tabla se restablecerá el contador, pero al eliminar filas seleccionadas (con una cláusula WHERE
) no se restablece el contador. Además, si se inserta una fila dentro de una transacción, pero luego esa transacción se deshace, el identificador se agotará de todos modos. Como ejemplo, vamos a crear la tabla count
que contiene un campo autoincrementable counter
:
mysql
>
CREATE
TABLE
count
(
counter
INT
AUTO_INCREMENT
KEY
)
;
Query OK, 0 rows affected (0.13 sec)
mysql
>
INSERT
INTO
count
VALUES
(
)
,
(
)
,
(
)
,
(
)
,
(
)
,
(
)
;
Query OK, 6 rows affected (0.01 sec) Records: 6 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
count
;
+---------+ | counter | +---------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | +---------+ 6 rows in set (0.00 sec)
Insertar varios valores funciona como se esperaba. Ahora, vamos a borrar unas cuantas filas y a añadir seis filas nuevas:
mysql
>
DELETE
FROM
count
WHERE
counter
>
4
;
Query OK, 2 rows affected (0.00 sec)
mysql
>
INSERT
INTO
count
VALUES
(
)
,
(
)
,
(
)
,
(
)
,
(
)
,
(
)
;
Query OK, 6 rows affected (0.00 sec) Records: 6 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
count
;
+---------+ | counter | +---------+ | 1 | | 2 | | 3 | | 4 | | 7 | | 8 | | 9 | | 10 | | 11 | | 12 | +---------+ 10 rows in set (0.00 sec)
Aquí vemos que el contador no se pone a cero y continúa a partir de 7. Sin embargo, si truncamos la tabla, eliminando así todos los datos, el contador se pone a 1:
mysql
>
TRUNCATE
TABLE
count
;
Query OK, 0 rows affected (0.00 sec)
mysql
>
INSERT
INTO
count
VALUES
(
)
,
(
)
,
(
)
,
(
)
,
(
)
,
(
)
;
Query OK, 6 rows affected (0.01 sec) Records: 6 Duplicates: 0 Warnings: 0
mysql
>
SELECT
*
FROM
count
;
+---------+ | counter | +---------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | +---------+ 6 rows in set (0.00 sec)
En resumen: AUTO_INCREMENT
garantiza una secuencia de valores transaccionales y monotónicamente crecientes. Sin embargo, no garantiza en modo alguno que cada identificador individual proporcionado siga exactamente al anterior. Normalmente, este comportamiento de AUTO_INCREMENT
es suficientemente claro y no debería suponer un problema. Sin embargo, si tu caso de uso particular requiere un contador que garantice que no haya huecos, deberías plantearte utilizar algún tipo de solución. Por desgracia, es probable que se implemente en el lado de la aplicación.
Alteración de estructuras
Ya te hemos enseñado todo lo que necesitas para crear bases de datos, tablas, índices y columnas. En esta sección aprenderás a añadir, eliminar y modificar columnas, bases de datos, tablas e índices en estructuras ya existentes.
Añadir, quitar y cambiar columnas
Puedes utilizar la sentencia ALTER TABLE
para añadir nuevas columnas a una tabla, eliminar columnas existentes y cambiar los nombres, tipos y longitudes de las columnas.
Empecemos por considerar cómo se modifican las columnas existentes. Consideremos un ejemplo en el que renombramos una columna de la tabla. La tabla language
tiene una columna llamada last_update
que contiene la hora en que se modificó el registro. Para cambiar el nombre de esta columna a last_updated_time
, escribirías
mysql
>
ALTER
TABLE
language
RENAME
COLUMN
last_update
TO
last_updated_time
;
Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
Este ejemplo concreto utiliza la función DDL en línea de MySQL. Lo que realmente ocurre entre bastidores es que MySQL sólo modifica los metadatos y no necesita reescribir realmente la tabla de ninguna manera. Puedes verlo por la ausencia de filas afectadas. No todas las sentencias DDL pueden realizarse en línea, por lo que éste no será el caso de muchos de los cambios que realices.
Nota
DDL significa lenguaje de definición de datos, y en el contexto de SQL es un subconjunto de sintaxis y sentencias utilizadas para crear, modificar y eliminar objetos del esquema, como bases de datos, tablas, índices y columnas. CREATE TABLE
y ALTER TABLE
son operaciones DDL, por ejemplo.
Ejecutar sentencias DDL requiere mecanismos internos especiales de, incluidos bloqueos especiales, lo cual es bueno, ¡ya que probablemente no te gustaría que las tablas cambiaran mientras se ejecutan tus consultas! Estos bloqueos especiales se denominan bloqueos de metadatos en MySQL, y en "Bloqueos de metadatos" te explicamos detalladamente cómo funcionan .
Ten en cuenta que todas las sentencias DDL, incluidas las que se ejecutan mediante DDL en línea, requieren la obtención de bloqueos de metadatos. En ese sentido, las sentencias DDL en línea no son tan "en línea", pero no bloquearán la tabla de destino por completo mientras se estén ejecutando.
Ejecutar sentencias DDL en un sistema en funcionamiento bajo carga es una aventura arriesgada: incluso una sentencia que debería ejecutarse casi instantáneamente puede causar estragos. Te recomendamos que leas detenidamente sobre el bloqueo de metadatos en el Capítulo 6 y en el enlace a la documentación de MySQL, y que experimentes ejecutando distintas sentencias DDL con y sin carga concurrente. Puede que esto no sea demasiado importante mientras aprendes MySQL, pero creemos que merece la pena advertirte por adelantado. Con esto cubierto, volvamos a nuestro ALTER
de la tabla language
.
Puedes comprobar el resultado con la declaración SHOW COLUMNS
:
mysql
>
SHOW
COLUMNS
FROM
language
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | NO | | NULL |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... +-------------------+------------------+------+-----+-------------------+... 3 rows in set (0.01 sec)
En el ejemplo anterior hemos utilizado la sentencia ALTER TABLE
con la palabra clave RENAME COLUMN
. Se trata de una función de MySQL 8.0. Alternativamente podríamos utilizar ALTER TABLE
con la palabra clave CHANGE
por compatibilidad:
mysql
>
ALTER
TABLE
language
CHANGE
last_update
last_updated_time
TIMESTAMP
-
>
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
;
Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0
En este ejemplo, puedes ver que hemos proporcionado cuatro parámetros a la sentencia ALTER TABLE
con la palabra clave CHANGE
:
-
El nombre de la tabla,
language
-
El nombre original de la columna,
last_update
-
El nuevo nombre de la columna,
last_updated_time
-
El tipo de columna,
TIMESTAMP
, con un montón de atributos extra, que son necesarios para no cambiar la definición original
Debes proporcionar los cuatro; eso significa que tienes que reespecificar el tipo y las cláusulas que lo acompañan. En este ejemplo, como estamos utilizando MySQL 8.0 con la configuración predeterminada, TIMESTAMP
ya no tiene valores predeterminados explícitos. Como puedes ver, utilizar RENAME COLUMN
es mucho más fácil que CHANGE
.
Si quieres modificar el tipo y las cláusulas de una columna, pero no su nombre, puedes utilizar la palabra clave MODIFY
:
mysql
>
ALTER
TABLE
language
MODIFY
name
CHAR
(
20
)
DEFAULT
'n/a'
;
Query OK, 0 rows affected (0.14 sec) Records: 0 Duplicates: 0 Warnings: 0
También puedes hacerlo con la palabra clave CHANGE
, pero especificando dos veces el mismo nombre de columna:
mysql
>
ALTER
TABLE
language
CHANGE
name
name
CHAR
(
20
)
DEFAULT
'n/a'
;
Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
Ten cuidado cuando modifiques tipos:
-
No utilices tipos incompatibles, ya que confías en que MySQL convierta correctamente los datos de un formato a otro (por ejemplo, convertir una columna
INT
en una columnaDATETIME
no es probable que haga lo que esperabas). -
No trunques los datos a menos que sea lo que quieres. Si reduces el tamaño de un tipo, los valores se editarán para ajustarse a la nueva anchura, y puedes perder datos.
Supongamos que quieres añadir una columna adicional a una tabla existente. He aquí cómo hacerlo con la sentencia ALTER TABLE
:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
20
)
;
Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0
Debes proporcionar la palabra clave ADD
, el nombre de la nueva columna, y el tipo de columna y las cláusulas. Este ejemplo añade la nueva columna, native_name
, como última columna de la tabla, tal y como se muestra con la sentencia SHOW COLUMNS
:
mysql
>
SHOW
COLUMNS
FROM
artist
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | YES | | n/a |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... | native_name | char(20) | YES | | NULL |... +-------------------+------------------+------+-----+-------------------+... 4 rows in set (0.00 sec)
Si quieres que en su lugar sea la primera columna, utiliza la palabra clave FIRST
como se indica a continuación:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
20
)
FIRST
;
Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql
>
SHOW
COLUMNS
FROM
language
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | native_name | char(20) | YES | | NULL |... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | YES | | n/a |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... +-------------------+------------------+------+-----+-------------------+... 4 rows in set (0.01 sec)
Si quieres que se añada en una posición concreta, utiliza la palabra clave AFTER
:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
20
)
AFTER
name
;
Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql
>
SHOW
COLUMNS
FROM
language
;
+-------------------+------------------+------+-----+-------------------+... | Field | Type | Null | Key | Default |... +-------------------+------------------+------+-----+-------------------+... | language_id | tinyint unsigned | NO | PRI | NULL |... | name | char(20) | YES | | n/a |... | native_name | char(20) | YES | | NULL |... | last_updated_time | timestamp | NO | | CURRENT_TIMESTAMP |... +-------------------+------------------+------+-----+-------------------+... 4 rows in set (0.00 sec)
Para eliminar una columna, utiliza la palabra clave DROP
seguida del nombre de la columna. He aquí cómo deshacerte de la columna native_name
recién añadida:
mysql
>
ALTER
TABLE
language
DROP
native_name
;
Query OK, 0 rows affected (0.07 sec) Records: 0 Duplicates: 0 Warnings: 0
Esto elimina tanto la estructura de la columna como los datos que contenga. También elimina la columna de cualquier índice en el que estuviera; si es la única columna del índice, éste también se elimina. No puedes eliminar una columna si es la única de una tabla; para ello, elimina la tabla, como se explica en "Eliminar estructuras". Ten cuidado al eliminar columnas, porque cuando cambia la estructura de una tabla, generalmente tendrás que modificar cualquier sentencia INSERT
que utilices para insertar valores en un orden determinado. Para más información, consulta "La sentencia INSERT".
MySQL te permite especificar varias alteraciones en una sola sentencia ALTER TABLE
separándolas con comas. Aquí tienes un ejemplo que añade una nueva columna y ajusta otra:
mysql
>
ALTER
TABLE
language
ADD
native_name
CHAR
(
255
)
,
MODIFY
name
CHAR
(
255
)
;
Query OK, 6 rows affected (0.06 sec) Records: 6 Duplicates: 0 Warnings: 0
Observa que esta vez puedes ver que se han modificado seis registros. En los comandos anteriores de ALTER TABLE
, MySQL informó de que no se había visto afectada ninguna fila. La diferencia es que esta vez no estamos realizando una operación DDL en línea, porque cambiar el tipo de cualquier columna siempre provocará la reconstrucción de la tabla. Te recomendamos que leas sobre las operaciones DDL en línea en el Manual de Referencia cuando planifiques tus cambios. Combinar operaciones online y offline dará como resultado una operación offline.
Cuando no se utiliza DDL en línea o cuando alguna de las modificaciones está "desconectada", es muy eficaz unir varias modificaciones en una sola operación. Eso ahorra potencialmente el coste de crear una tabla nueva, copiar los datos de la tabla antigua a la nueva, eliminar la tabla antigua y renombrar la tabla nueva con el nombre de la tabla antigua para cada modificación individualmente.
Añadir, eliminar y modificar índices
Como hemos comentado anteriormente, a menudo es difícil saber qué índices de son útiles antes de que se utilice la aplicación que estás construyendo. Puede que descubras que una función concreta de la aplicación es mucho más popular de lo que esperabas, lo que te hará evaluar cómo mejorar el rendimiento de las consultas asociadas. Por tanto, te resultará útil poder añadir, modificar y eliminar índices sobre la marcha, una vez implementada la aplicación. Esta sección te muestra cómo hacerlo. Ten en cuenta que la modificación de índices no afecta a los datos almacenados en una tabla.
Empezaremos añadiendo un nuevo índice. Imagina que la tabla language
se consulta con frecuencia utilizando una cláusula WHERE
que especifica la name
. Para acelerar estas consultas, has decidido añadir un nuevo índice, al que has llamado idx_name
. He aquí cómo lo añades después de crear la tabla:
mysql
>
ALTER
TABLE
language
ADD
INDEX
idx_name
(
name
)
;
Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0
De nuevo, puedes utilizar indistintamente los términos KEY
y INDEX
. Puedes comprobar los resultados con la declaración SHOW CREATE TABLE
:
mysql
>
SHOW
CREATE
TABLE
language
\
G
*************************** 1. row *************************** Table: language Create Table: CREATE TABLE `language` ( `language_id` tinyint unsigned NOT NULL AUTO_INCREMENT, `name` char(255) DEFAULT NULL, `last_updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`language_id`), KEY `idx_name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Como era de esperar, el nuevo índice forma parte de la estructura de la tabla. También puedes especificar una clave primaria para una tabla después de crearla:
mysql
>
CREATE
TABLE
no_pk
(
id
INT
)
;
Query OK, 0 rows affected (0.02 sec)
mysql
>
INSERT
INTO
no_pk
VALUES
(
1
)
,
(
2
)
,
(
3
)
;
Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0
mysql
>
ALTER
TABLE
no_pk
ADD
PRIMARY
KEY
(
id
)
;
Query OK, 0 rows affected (0.13 sec) Records: 0 Duplicates: 0 Warnings: 0
Veamos ahora cómo eliminar un índice. Para eliminar un índice de clave no primaria, haz lo siguiente:
mysql
>
ALTER
TABLE
language
DROP
INDEX
idx_name
;
Query OK, 0 rows affected (0.08 sec) Records: 0 Duplicates: 0 Warnings: 0
Puedes eliminar un índice de clave primaria del siguiente modo:
mysql
>
ALTER
TABLE
no_pk
DROP
PRIMARY
KEY
;
Query OK, 3 rows affected (0.07 sec) Records: 3 Duplicates: 0 Warnings: 0
MySQL no te permite tener varias claves primarias en una tabla. Si quieres cambiar la clave primaria, tendrás que eliminar el índice existente antes de añadir el nuevo. Sin embargo, sabemos que es posible agrupar operaciones DDL. Considera este ejemplo:
mysql
>
ALTER
TABLE
language
DROP
PRIMARY
KEY
,
-
>
ADD
PRIMARY
KEY
(
language_id
,
name
)
;
Query OK, 0 rows affected (0.09 sec) Records: 0 Duplicates: 0 Warnings: 0
No puedes modificar un índice una vez creado. Sin embargo, en a veces querrás hacerlo; por ejemplo, puede que quieras reducir el número de caracteres indexados de una columna o añadir otra columna al índice. El mejor método para hacerlo es eliminar el índice y volver a crearlo con la nueva especificación. Por ejemplo, supongamos que decides que quieres que el índice idx_name
incluya sólo los 10 primeros caracteres del artist_name
. Simplemente haz lo siguiente:
mysql
>
ALTER
TABLE
language
DROP
INDEX
idx_name
,
-
>
ADD
INDEX
idx_name
(
name
(
10
)
)
;
Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0
Cambiar el nombre de las tablas y modificar otras estructuras
Hemos visto cómo modificar columnas e índices de una tabla; ahora vamos a ver cómo modificar las propias tablas. Es fácil cambiar el nombre de una tabla. Supongamos que quieres cambiar el nombre de language
a languages
. Utiliza el siguiente comando:
mysql
>
ALTER
TABLE
language
RENAME
TO
languages
;
Query OK, 0 rows affected (0.04 sec)
La palabra clave TO
es opcional.
Hay otras cosas que puedes hacer con las declaraciones de ALTER
, entre ellas:
-
Cambia el juego de caracteres y el orden de intercalación por defecto de una base de datos, una tabla o una columna.
-
Gestionar y modificar restricciones. Por ejemplo, puedes añadir y eliminar claves foráneas.
-
Añade particiones a una tabla o modifica la definición actual de particiones.
-
Cambiar el motor de almacenamiento de una tabla.
Puedes encontrar más información sobre estas operaciones en el Manual de Referencia de MySQL, en las secciones sobre ALTER DATABASE
y ALTER TABLE
. Una notación alternativa más corta para la misma sentencia es RENAME TABLE
:
mysql
>
RENAME
TABLE
languages
TO
language
;
Query OK, 0 rows affected (0.04 sec)
Una cosa que no es posible alterar es el nombre de una determinada base de datos . Sin embargo, si utilizas el motor InnoDB, puedes utilizar RENAME
para mover tablas entrebases de datos:
mysql
>
CREATE
DATABASE
sakila_new
;
Query OK, 1 row affected (0.05 sec)
mysql
>
RENAME
TABLE
sakila
.
language
TO
sakila_new
.
language
;
Query OK, 0 rows affected (0.05 sec)
mysql
>
USE
sakila
;
Database changed
mysql
>
SHOW
TABLES
LIKE
'lang%'
;
Empty set (0.00 sec)
mysql
>
USE
sakila_new
;
Database changed
mysql
>
SHOW
TABLES
LIKE
'lang%'
;
+------------------------------+ | Tables_in_sakila_new (lang%) | +------------------------------+ | language | +------------------------------+ 1 row in set (0.00 sec)
Borrar estructuras
En la sección anterior mostramos cómo puedes eliminar columnas y filas de una base de datos; ahora describiremos cómo eliminar bases de datos y tablas.
Dar de baja bases de datos
Eliminar una base de datos es muy sencillo. A continuación te explicamos cómo eliminar la base de datos sakila
:
mysql
>
DROP
DATABASE
sakila
;
Query OK, 25 rows affected (0.16 sec)
El número de filas devuelto en la respuesta es el número de tablas eliminadas. Debes tener cuidado al eliminar una base de datos, ya que se borran todas sus tablas, índices y columnas, así como todos los archivos y directorios asociados en disco que MySQL utiliza para mantenerlos.
Si una base de datos no existe, al intentar eliminarla MySQL informa de un error. Intentemos eliminar de nuevo la base de datos sakila
:
mysql
>
DROP
DATABASE
sakila
;
ERROR 1008 (HY000): Can't drop database 'sakila'; database doesn't exist
Puedes evitar el error, que es útil cuando incluyes la declaración en un script, utilizando la frase IF EXISTS
:
mysql
>
DROP
DATABASE
IF
EXISTS
sakila
;
Query OK, 0 rows affected, 1 warning (0.00 sec)
Puedes ver que se informa de una advertencia, ya que la base de datos sakila
ya se ha eliminado.
Eliminar tablas
Eliminar tablas es tan fácil como eliminar una base de datos. Vamos a crear y eliminar una tabla de la base de datos sakila
:
mysql
>
CREATE
TABLE
temp
(
id
SERIAL
PRIMARY
KEY
)
;
Query OK, 0 rows affected (0.05 sec)
mysql
>
DROP
TABLE
temp
;
Query OK, 0 rows affected (0.03 sec)
No te preocupes: el mensaje 0 rows affected
es engañoso. Verás que la tabla ha desaparecido definitivamente.
Puedes utilizar la frase IF EXISTS
para evitar errores. Probemos a eliminar de nuevo la tabla temp
:
mysql
>
DROP
TABLE
IF
EXISTS
temp
;
Query OK, 0 rows affected, 1 warning (0.01 sec)
Como siempre, puedes investigar la advertencia con la declaración SHOW WARNINGS
:
mysql
>
SHOW
WARNINGS
;
+-------+------+-----------------------------+ | Level | Code | Message | +-------+------+-----------------------------+ | Note | 1051 | Unknown table 'sakila.temp' | +-------+------+-----------------------------+ 1 row in set (0.00 sec)
Puedes eliminar más de una tabla en una única sentencia separando los nombres de las tablas con comas:
mysql
>
DROP
TABLE
IF
EXISTS
temp
,
temp1
,
temp2
;
Query OK, 0 rows affected, 3 warnings (0.00 sec)
En este caso hay tres advertencias porque ninguna de estas tablas existía.
Get Aprender MySQL, 2ª Edición now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.