El fu­n­da­me­n­to de nuestro universo digital es una acu­mu­la­ción de datos que aumenta sin cesar. Como co­n­se­cue­n­cia, los sistemas de gestión de bases de datos como MySQL son los elementos centrales de Internet y del mundo in­te­r­co­ne­c­ta­do. Estos permiten procesar grandes ca­n­ti­da­des de datos de manera ele­c­tró­ni­ca, ar­chi­var­los con co­he­re­n­cia y gua­r­dar­los pe­r­ma­ne­n­te­me­n­te. Para ello, los datos complejos se de­s­co­m­po­nen en ca­n­ti­da­des ma­ne­ja­bles y, en función de las ne­ce­si­da­des, se re­la­cio­nan entre sí. En nuestro manual de MySQL para pri­n­ci­pia­n­tes te pre­se­n­ta­mos los pri­n­ci­pios básicos de la gestión de bases de datos y te mostramos a través de ejemplos cómo puedes optimizar la gestión de los datos de tu proyecto web con MySQL.

¿Qué es MySQL?

MySQL se cuenta junto a Oracle y Microsoft SQL Server entre los sistemas de gestión de bases de datos re­la­cio­na­les más populares a escala mundial (en­cue­n­tras un listado actual en https://db-engines.com/en/ranking). El software de­sa­rro­lla­do en 1994 por la empresa MySQL AB cuenta hoy en día con el pa­tro­ci­nio de Oracle Co­r­po­ra­tion y se co­me­r­cia­li­za bajo un sistema de licencias dual. Asimismo, además de la edición En­te­r­pri­se pro­pie­ta­ria, Oracle ofrece una versión de código abierto con una licencia GPL.  Esta licencia dual ofrece a las empresas la po­si­bi­li­dad de de­sa­rro­llar apli­ca­cio­nes propias basándose en MySQL sin tener que so­me­te­r­las a la licencia de código abierto. En la comunidad open source, sin embargo, la absorción de MySQL por parte de Oracle es objeto de críticas. MySQL está escrito en C y C++ y, además, está provisto de un ana­li­za­dor si­n­tá­c­ti­co de SQL basado en Yacc con un to­ke­ni­za­dor (escáner léxico) propio. Asimismo, el sistema de gestión de bases de datos se destaca por su amplio soporte de sistemas ope­ra­ti­vos.

Hecho

La abre­via­tu­ra SQL procede de “Stru­c­tu­red Query Language”, un lenguaje de pro­gra­ma­ción que sirve para gestionar es­tru­c­tu­ras de bases de datos. Entre las posibles ope­ra­cio­nes se en­cue­n­tran la consulta, in­te­gra­ción, ac­tua­li­za­ción y eli­mi­na­ción de datos exi­s­te­n­tes.

MariaDB: una bi­fu­r­ca­ción de MySQL con potencial

En el ámbito del de­sa­rro­llo web, la in­te­gra­ción del proyecto MySQL en el catálogo de productos de Oracle es objeto de de­s­co­n­fia­n­za y crítica debido, pri­n­ci­pa­l­me­n­te, al aumento constante de las di­fe­re­n­cias entre la versión MySQL con licencia GPL y el producto En­te­r­pri­se de pago. Las nuevas funciones del sistema de gestión de bases de datos son cada vez más fre­cue­n­tes, aunque solo en la versión pro­pie­ta­ria. Las bases de datos de errores no públicas y los tests in­su­fi­cie­n­tes hacen suponer a la comunidad que, bajo las alas del software Oracle, el proyecto open source no disfruta de igualdad de co­n­di­cio­nes, lo que des­en­ca­de­na un apoyo cada vez menor por parte de esta.

Ya en el año 2009, el equipo de de­sa­rro­llo principal en torno a Michael “Monty” Widenius, inventor de la base de datos MySQL, le dio la espalda al popular sistema de bases de datos y puso en marcha con MariaDB un fork de código abierto de MySQL. A finales de 2012, di­s­tri­bu­cio­nes Linux como Fedora, OpenSUSE, Slackware y Arch Linux fueron las primeras en re­em­pla­zar MySQL por MariaDB como in­s­ta­la­ción estándar. Un gran número de proyectos open source, de empresas de software y pla­ta­fo­r­mas web conocidas siguieron el mismo ejemplo, entre ellas Mozilla, Ubuntu, Google, Red Hat En­te­r­pri­se Linux, Web of Trust, Team Speak, la Fundación Wikimedia y XAMPP.  

Aún hoy queda patente que, en co­m­pa­ra­ción con la versión de MySQL de código abierto, MariaDB sigue de­sa­rro­llá­n­do­se. Por lo tanto, es de suponer que el fork vaya a superar pronto a su proyecto madre.

Consejo

En el sector del software se puede hablar de un fork como una rama de de­sa­rro­llo que surge de la bi­fu­r­ca­ción de un proyecto (en su mayoría, de fuente abierta). Un fork se crea sobre el código fuente del proyecto madre y se de­sa­rro­lla en un proyecto de se­gui­mie­n­to in­de­pe­n­die­n­te.

Sistemas de bases de datos

Hoy en día, la gestión ele­c­tró­ni­ca de datos tiene lugar pri­n­ci­pa­l­me­n­te con sistemas de bases de datos (SBS), que están formados por dos co­m­po­ne­n­tes, la propia base de datos (BD) y el sistema de gestión de la base de datos (SGBD):

  • El sistema de gestión de bases de datos: por SGBD se entiende todo aquel software que, como MySQL, sirve para gestionar el sistema de bases de datos. Entre las tareas de este software de gestión se encuentra la es­tru­c­tu­ra­ción de los datos conforme a un modelo de base de datos pre­de­fi­ni­do. Además, el SGBD controla los accesos de escritura y lectura, ad­mi­ni­s­tra grandes ca­n­ti­da­des de datos y accesos paralelos y se ocupa de que se cumplan las normas con respecto a la in­te­gri­dad de la in­fo­r­ma­ción, así como a su pro­te­c­ción y seguridad.
  • La base de datos es un conjunto de datos con co­n­te­ni­dos re­la­cio­na­dos, como, por ejemplo, in­fo­r­ma­ción de los clientes o datos de los CMS. Un SGBD puede gestionar varias bases de datos de manera si­mu­l­tá­nea.

El siguiente gráfico muestra una pre­se­n­ta­ción es­que­má­ti­ca de un Sistema de Bases de Datos:

Modelo re­la­cio­nal de bases de datos

Según su de­fi­ni­ción, MySQL es un SGBD re­la­cio­nal. Esto significa que la totalidad de los datos que utiliza MySQL se almacena en tablas re­la­cio­na­das entre sí por medio de claves (keys).

Veamos cómo se puede ilustrar esto con un ejemplo sencillo. A co­n­ti­nua­ción aparecen las tablas autores y obras:

Todas las tablas de una base de datos re­la­cio­nal están formadas por columnas y filas, y cada columna está diseñada para un atributo de­te­r­mi­na­do. En la tabla autores se en­cue­n­tran, por ejemplo, los atributos id, nombre y apellido. Las filas de una tabla están de­sig­na­das como rows y cada una de ellas contiene un conjunto de datos, el cual viene ide­n­ti­fi­ca­do (numerado), por lo general, por medio de una clave primaria. El tipo de atributo es­ta­ble­ci­do como clave primaria se define a la hora de crear la tabla. El requisito es que la clave primaria po­si­bi­li­te una asi­g­na­ción ine­quí­vo­ca, de modo que esta solo puede uti­li­zar­se una vez en la columna. Para ello se re­co­mie­n­da una nu­me­ra­ción en serie vía ID.

La tabla obras presenta, además de la clave primaria id_obras, id_autores como clave foránea (foreign key). Esta crea una relación entre ambas tablas y enlaza los conjuntos de datos de una tabla con los de la otra. Cuando se establece una conexión entre dos tablas de una base de datos re­la­cio­nal se puede hablar de un join. Esta conexión podría tener lugar con la siguiente consulta a la base de datos: “carga todas las obras del autor John Ronald Reuel Tolkien con la fecha de la primera pu­bli­ca­ción”.

Tolkien ha sido cla­si­fi­ca­do en la tabla autores con la clave primaria id_autores 1. Para consultar todas las obras de dicho autor, este se presenta en la tabla obras como clave foránea. De esta manera se solicitan todos los rows enlazados con el id_autores 1.

En la práctica, todas las ope­ra­cio­nes de bases de datos en MySQL se llevan a cabo con comandos SQL es­ta­n­da­ri­za­dos como SELECT, INSERT, UPDATE y DELETE, pero ha­bla­re­mos de ellos en capítulos po­s­te­rio­res de nuestro manual de MySQL.

Como es lógico, también se pueden guardar todos los datos re­fe­re­n­tes a los autores y a sus obras en una única tabla. Un al­ma­ce­na­mie­n­to de datos de estas ca­ra­c­te­rí­s­ti­cas implica que una base de datos contenga un sinnúmero de entradas re­du­n­da­n­tes, ya que, por ejemplo, los datos en las columnas nombre y apellido se es­pe­ci­fi­can de forma in­di­vi­dual para cada obra. Una re­du­n­da­n­cia de este tipo no solo supone una carga para la memoria, sino que también da lugar a que se tengan que efectuar ac­tua­li­za­cio­nes en di­fe­re­n­tes puntos de la base de datos. Es por eso que cuando se trabaja con bases de datos re­la­cio­na­les se limite su contenido a un asunto por tabla. Se habla, en este caso, de una no­r­ma­li­za­ción de los datos.

El campo de apli­ca­ción principal de MySQL es el al­ma­ce­na­mie­n­to de datos en el contexto de las páginas web dinámicas. La co­m­bi­na­ción de MySQL con el software de se­r­vi­do­res web Apache y los lenguajes de script PHP o Perl se ha co­n­so­li­da­do como es­tru­c­tu­ra clásica de software en el de­sa­rro­llo web. El stack web puede llevarse a cabo como LAMP (Linux), MAMP (macOS) o WAMP (Windows) con los sistemas ope­ra­ti­vos de se­r­vi­do­res ha­bi­tua­les.

Para los que empiezan a trabajar con MySQL pueden ser re­co­me­n­da­bles los entornos locales XAMPP para recabar las primeras ex­pe­rie­n­cias con el sistema de gestión de bases de datos, el cual se basa en  MariaDB en la versión actual.

In­s­ta­la­ción del sistema de gestión de bases de datos

A co­n­ti­nua­ción, te pre­se­n­ta­mos algunos ejemplos prácticos para arrojar más luz sobre los fu­n­da­me­n­tos de MySQL. Nuestro tutorial para MySQL tiene como base el entorno de prueba de XAMPP y los códigos de snippets y las capturas de pantalla se centran en las ope­ra­cio­nes de bases de datos que se llevan a cabo a través de PHP con ayuda de un servidor Apache HTTP en un ordenador Windows. En lugar de la clásica base de datos MySQL, en este caso, se aplica el fork MariaDB. Ac­tua­l­me­n­te, ambos sistemas de gestión de bases de datos son tan co­m­pa­ti­bles que todas las ope­ra­cio­nes son exac­ta­me­n­te iguales. En el marco de un tutorial para pri­n­ci­pia­n­tes es in­di­fe­re­n­te si se trabaja con MySQL o con MariaDB. Para saber cómo puedes instalar un entorno de prueba de manera local en tu ordenador Windows, visita nuestro tutorial de XAMPP. Si quieres aprender a trabajar con bases de datos re­la­cio­na­les desde el principio lo re­co­me­n­da­ble es que te centres di­re­c­ta­me­n­te en MariaDB. Un entorno al­te­r­na­ti­vo y gratuito que se basa en MySQL es AMPPS. También tienes la po­si­bi­li­dad de integrar un stack web pe­r­so­na­li­za­do. MySQL y MariaDB se pueden combinar, según sea necesario, con diversos sistemas ope­ra­ti­vos, se­r­vi­do­res web y lenguajes de scripts. Los paquetes de descarga gratuitos y con licencia GPL están di­s­po­ni­bles en la página mysql.com y mariadb.com. Puedes obtener in­s­tru­c­cio­nes de­ta­lla­das de in­s­ta­la­ción para di­fe­re­n­tes pla­ta­fo­r­mas en la do­cu­me­n­ta­ción en inglés de MySQL y MariaDB.

Gestión de bases de datos con ph­p­M­yA­d­min

Para la gestión de MySQL nos basamos en la apli­ca­ción web libre ph­p­M­yA­d­min, contenido en el paquete de in­s­ta­la­ción de XAMPP, pero también se ofrece por separado en la página web oficial del proyecto como paquete de descarga ph­p­M­yA­d­min es co­n­si­de­ra­do como el software estándar para la ad­mi­ni­s­tra­ción de bases de datos MySQL en la World Wide Web. La apli­ca­ción web escrita en PHP y Ja­va­S­cri­pt facilita las ope­ra­cio­nes de bases de datos a través de in­te­r­fa­ces gráficas de usuario. De esta manera se pueden crear y gestionar las tablas de la base de datos re­la­cio­nal có­mo­da­me­n­te en el navegador web. En este sentido, no es necesario en principio conocer los co­rre­s­po­n­die­n­tes comandos SQL.

Iniciar ph­p­M­yA­d­min

Si has instalado XAMPP, inicia el sistema gestor de bases de datos (MySQL o MariaDB) en el panel de control, de la misma forma como se inician los otros co­m­po­ne­n­tes del stack. Para ello, utiliza el botón “Start” que hay debajo de “Actions”. Para poder acceder a ph­p­M­yA­d­min a través del navegador web, es necesario que inicies el servidor web Apache. Los módulos activados aparecen re­sa­l­ta­dos en color verde en el panel de control de XAMPP. También puedes conocer el estado actual de los módulos de XAMPP en forma de no­ti­fi­ca­ción por medio del cuadro de texto.

Hecho

XAMPP fue de­sa­rro­lla­do en el marco del proyecto de software Apache Friends como sistema compacto de pruebas para su uso en or­de­na­do­res locales. El paquete de software no está pensado para poner a di­s­po­si­ción servicios web en Internet. Como sistema pro­du­c­ti­vo, XAMPP no es apto para el ámbito de la seguridad debido a sus numerosas li­mi­ta­cio­nes.

En el modo de prueba local, se puede acceder a la interfaz web del software de ad­mi­ni­s­tra­ción a través de http://localhost/ph­p­m­ya­d­min/.

Siempre y cuando se haya definido una co­n­tra­se­ña para la cuenta root en la in­s­ta­la­ción de MySQL, será necesario in­tro­du­ci­r­la en la pantalla de registro en ph­p­M­yA­d­min. En caso de utilizar esta he­rra­mie­n­ta en un producto de alo­ja­mie­n­to web, los datos de registro serán otorgados por el proveedor co­rre­s­po­n­die­n­te. En este caso, como usuario, no sueles tener derechos de acceso raíz.

Una vez has iniciado sesión, ph­p­M­yA­d­min muestra la página de inicio de la apli­ca­ción, la cual permite realizar los ajustes básicos para obtener el conjunto de ca­ra­c­te­res (colación) de la conexión de MySQL o para se­le­c­cio­nar el modo de vi­sua­li­za­ción deseado (idioma, diseño y tamaño de fuente). En la parte derecha aparece un resumen de los datos de re­fe­re­n­cia de tu servidor de base de datos, del software de servidor web utilizado, así como de la in­fo­r­ma­ción sobre la versión actual de ph­p­M­yA­d­min. La barra del menú está, al igual que el resto de barras de menú de la apli­ca­ción, diseñada en forma de pestañas. Para se­le­c­cio­nar unas u otras, tan solo hay que hacer clic en Databases, SQL, Status, User accounts, Export, Import, Settings, Re­pli­ca­tion, Variables y More.

En el extremo izquierdo de la interfaz de usuario aparece un panel de na­ve­ga­ción en el que se indican todas las tablas en la base de datos a las que tienes acceso con ph­p­M­yA­d­min. Bajo el logo del programa en la esquina superior izquierda, hay una función con la que puedes acceder tanto a la página de inicio del software como a la do­cu­me­n­ta­ción oficial. Además, también existe la po­si­bi­li­dad de co­n­fi­gu­rar el panel de na­ve­ga­ción y ac­tua­li­zar su vista.

A co­n­ti­nua­ción em­pe­za­re­mos nuestro curso intensivo de MySQL para que puedas crear tu primera base de datos.

Crea tu base de datos

Para crear una base de datos con ph­p­M­yA­d­min hay que se­le­c­cio­nar la pestaña “Databases” (bases de datos) en la barra del menú de la página de inicio.

Introduce un nombre para la base de datos en el campo de entrada bajo “Create database” (crear nueva base de datos) y se­le­c­cio­na un co­te­ja­mie­n­to (collation). En este caso es re­co­me­n­da­ble la colación utf8mb4_unicode_ci. Con ella in­fo­r­ma­mos al servidor de bases de datos sobre la co­di­fi­ca­ción para los datos tra­n­s­mi­ti­dos o recibidos. Las variantes mb4 también autorizan signos exóticos como símbolos o emojis, que se escapan a los comandos básicos de Unicode (Basic Mu­l­ti­li­n­gual Plane) y, por lo tanto, resultan re­co­me­n­da­bles.

Confirma la entrada haciendo clic en “Create” (crear). La base de datos creada aparece en el panel de na­ve­ga­ción en el lado izquierdo de la pantalla. Las nuevas bases de datos no incluyen ningún contenido. Para depositar datos es necesario que, a co­n­ti­nua­ción, crees una tabla.

Crear tablas

Para crear una nueva tabla, se­le­c­cio­na la base de datos deseada y navega por la barra de menú hasta llegar a la pestaña “Structure” (es­tru­c­tu­ra).

Crea una tabla e introduce el nombre (por ejemplo, users) y el número deseado de columnas mediante el botón “Create table” (generar tabla). Recuerda que a cada columna le co­rre­s­po­n­de un atributo de la entrada en la tabla. Si necesitas incluir más columnas, lo puedes hacer más tarde.

Si, por ejemplo, quieres crear una base de datos de usuarios, puedes recurrir a los si­guie­n­tes elementos para las columnas de la tabla:

Columna De­s­cri­p­ción
id Un número de ide­n­ti­fi­ca­ción ine­quí­vo­co para cada usuario
forename Nombre del usuario
surname Apellidos del usuario
email Dirección de correo ele­c­tró­ni­co del usuario
password Co­n­tra­se­Ã±a del usuario
created_at Fecha en la que se crea la entrada
updated_at Fecha en la que se actualiza la entrada

Para la base de datos de usuarios, crea la tabla users con siete columnas y confirma la entrada con “Go”.

Una vez creada la tabla, ph­p­M­yA­d­min te da la po­si­bi­li­dad de definir los nombres de las columnas y de realizar los ajustes de formato para los datos previstos.

La tabla que aparece a co­n­ti­nua­ción incluye una de­s­cri­p­ción de la es­tru­c­tu­ra de las tablas así como de los posibles ajustes.

Opción De­s­cri­p­ción
Name A cada columna en una tabla de una base de datos se le asigna un nombre, el cual puede escogerse li­bre­me­n­te, aunque con algunas re­s­tri­c­cio­nes. A este respecto, los ca­ra­c­te­res del alfabeto latino (ma­yú­s­cu­las o mi­nú­s­cu­las, pero sin acentos), los números, el símbolo del dólar y el guion bajo no re­pre­se­n­tan ningún problema. Este se puede usar como al­te­r­na­ti­va al espacio, que no está permitido (in­co­rre­c­to: user id; correcto: user_id). Los nombres de las columnas tienen que estar formados por otros símbolos además de números. Asimismo, en el lenguaje para bases de datos SQL se pueden encontrar algunas palabras clave que están re­se­r­va­das para de­te­r­mi­na­das tareas. Se puede acceder a una lista de las mismas en la do­cu­me­n­ta­ción de MySQL. La mayor parte de estas li­mi­ta­cio­nes pueden eludirse, pero la columna co­rre­s­po­n­die­n­te siempre debe ir entre comillas simples (`...´). Estas mismas reglas se aplican, entre otros, a los nombres de las tablas en MySQL. Se re­co­mie­n­da optar por nombres de columna elo­cue­n­tes y que se adapten a los atributos co­rre­s­po­n­die­n­tes.
Type El tipo de datos pone de relieve cuál es la na­tu­ra­le­za de la in­fo­r­ma­ción que se guarda en una columna. MySQL y MariaDB te permiten definir datos en forma de números enteros o de números de coma flotante, de hora o fecha, así como de cadenas de texto y datos binarios. Se puede encontrar una de­s­cri­p­ción de los mismos en la tabla de tipos de datos.
Length/Values En algunos tipos de datos (por ejemplo, las cadenas de texto) se puede asignar una longitud máxima a los valores de una columna, aunque este ajuste es opcional.
Default La opción “Default” (por defecto) permite definir un valor estándar para una columna, el cual se inserta au­to­má­ti­ca­me­n­te cuando un conjunto de datos no contiene ningún valor para la columna co­rre­s­po­n­die­n­te.
Collation La opción “Collation” define un de­te­r­mi­na­do tipo de ca­ra­c­te­res para una columna, el cual puede diferir de los ajustes globales de la base de datos. Se puede modificar, además, la co­di­fi­ca­ción en los di­fe­re­n­tes niveles de la tabla para todas las columnas.
At­tri­bu­tes Algunos tipos de datos se pueden fijar de una manera más precisa a través de atributos op­cio­na­les. Así, con los atributos signed y unsigned se puede es­ta­ble­cer, por ejemplo, si los números enteros o los de coma flotante asumen valores positivos (unsigned) o también negativos (signed).
Index A través de la opción “Index” se pueden definir las reglas para la in­de­xa­ción. Si se­le­c­cio­nas el ajuste Index PRIMARY para las columnas, este funciona como clave primaria de la tabla. El ajuste UNIQUE indica que los valores en esta columna solo pueden guardarse una vez, evitando las du­pli­ca­cio­nes.
A_I La abre­via­tu­ra “A_I” hace re­fe­re­n­cia a AUTO_INCREMENT e indica al gestor de bases de datos que in­cre­me­n­te un valor au­to­má­ti­ca­me­n­te cuando no se indique ninguno a la hora de crear una secuencia de datos. Esta opción es de apli­ca­ción cuando se indexan conjuntos de datos.
Comments El campo “Comments” integra co­me­n­ta­rios en las columnas de la tabla.

Todas estas opciones co­m­pre­n­den los ajustes más im­po­r­ta­n­tes de las columnas de las tablas. Si te mueves hacia la derecha con ayuda de la barra de de­s­pla­za­mie­n­to en­co­n­tra­rás otras po­si­bi­li­da­des de co­n­fi­gu­ra­ción —no incluidas en este tutorial de MySQL para pri­n­ci­pia­n­tes.

La siguiente tabla expone di­fe­re­n­tes tipos de datos que se pueden procesar tanto con MySQL como con MariaDB, así como su campo de valores y sus re­qui­si­tos de al­ma­ce­na­mie­n­to.

Tipo De­s­cri­p­ción Campo de valores Re­qui­si­tos de al­ma­ce­na­mie­n­to
TINYINT Un número entero muy pequeño Sin signos: de 0 a 255 Con signos: de -128 a +127 1 byte
SMALLINT Un número entero pequeño Sin signos: de 0 a 65.535 Con signos: de -32.768 a +32.767 2 bytes
MEDIUMINT Un número entero mediano Sin signos: de 0 a 16.777.215 Con signos: de -8.388.608 a +8.388.607 3 bytes
INT/INTEGER Un número entero de tamaño normal Sin signos: de 0 a 4.294.967.295 Con signos: de -2.147.483.648 a +2.147.483.647 4 bytes
BIGINT Un número entero grande Sin signos: de 0 a 18.446.744.073.709.551.615 Con signos: de -9.223.372.036.854.775.808 a +9.223.372.036.854.775.807 8 bytes
FLOAT Un número de coma flotante de simple precisión Sin signos: de 0 a 3,4e+38 Con signos: -3,4e+38 bis 3,4e+38 4 bytes
DOUBLE Un número de coma flotante de doble precisión Sin signos: de 0 a 3,4e+38 Con signos: de -3,4e+38 a 3,4e+38 8 bytes
DATE Fecha en formato 'YYYY-MM-DD' De '1000-01-01' a '9999-12-31' 3 bytes
TIME Hora en formato 'HH:MM:SS.ssssss' De '-838:59:59.999999' a '838:59:59.999999' 3 bytes
DATETIME Fecha en formato 'YYYY-MM-DD HH:MM:SS.ssssss' Se co­rre­s­po­n­de con DATE y TIME (hasta 23:59:59.999999 horas) 8 bytes
TIMESTAMP Marca temporal en formato 'YYYY-MM-DD HH:MM:DD' '1970-01-01 00:00:01' (UTC) hasta '2038-01-19 05:14:07' (UTC) 4 bytes
YEAR Año entre 1901 y 2155 Desde 1901 hasta 2155 y 0000 1 byte
CHAR Cadena de ca­ra­c­te­res de longitud fija; el número de ca­ra­c­te­res se co­rre­s­po­n­de con M Para M: de 0 a 255 ca­ra­c­te­res M byte
VARCHAR Cadena de ca­ra­c­te­res de longitud variable; el número de ca­ra­c­te­res se co­rre­s­po­n­de con M Para M: de 0 a 65.535 ca­ra­c­te­res Máx. M + 2 bytes
TINYTEXT Cadena de ca­ra­c­te­res muy pequeña de longitud variable; el número de ca­ra­c­te­res se co­rre­s­po­n­de con M Para M: de 0 a 255 ca­ra­c­te­res M + 1 byte
TEXT Cadena de ca­ra­c­te­res de longitud variable; el número de ca­ra­c­te­res se co­rre­s­po­n­de con M Para M: de 0 a 65.535 ca­ra­c­te­res M + 2 bytes
ME­DIU­M­TE­XT Cadena de ca­ra­c­te­res de tamaño medio y longitud variable; el número de ca­ra­c­te­res se co­rre­s­po­n­de con M Para M: de 0 a 16.777.215 ca­ra­c­te­res M + 3 bytes
LONGTEXT Cadena de ca­ra­c­te­res larga de longitud variable; el número de ca­ra­c­te­res se co­rre­s­po­n­de con M Para M: de 0 a 4.294.967.295 ca­ra­c­te­res (4 GB) M + 4 bytes
BLOB Un BLOB (Binary Large Object) es un objeto binario con datos de longitud variable (p. ej., imágenes, datos de audio). Máx. longitud de M: 65.535 bytes M + 2 bytes
TINYBLOB Objeto binario de pequeña en­ve­r­ga­du­ra con datos de longitud variable Máx. longitud de M: 255 bytes M + 1 byte
ME­DIU­M­BLOB Objeto binario de tamaño medio con datos de longitud variable Máx. Longitud de M: 16.777.215 bytes M + 3 bytes
LONGBLOB Objeto binario grande con datos de longitud variable Máx. longitud de M: 4.294.967.295 bytes (4 GB) M + 4 bytes
ENUM (enu­me­ra­ción) Un objeto string cuyos valores pe­r­mi­ti­dos se definen al crear la columna Máx. 65.535 elementos di­fe­re­n­tes 1 o 2 bytes, en función del número de valores posible
SET Un objeto string cuyos valores pe­r­mi­ti­dos se definen al crear la tabla. Es posible hacer una selección múltiple Máx. 64 valores di­fe­re­n­tes 1, 2, 3, 4, u 8, en función del número de valores posible

Para la tabla de ejemplo users se han co­n­fi­gu­ra­do los si­guie­n­tes ajustes:

Los posibles valores para la columna id se definen como números enteros (íntegro, INT) y llevan el atributo UNSIGNED. El id solo puede adoptar valores numéricos positivos. En “Index” hemos se­le­c­cio­na­do el ajuste PRIMARY para id. A este respecto, el número de ide­n­ti­fi­ca­ción hace de clave primaria para la tabla users y la marca de ve­ri­fi­ca­ción en “A_I” (Auto_Increment) indica al sistema gestor de bases de datos que los ide­n­ti­fi­ca­do­res de cada entrada tienen que generarse como números co­n­se­cu­ti­vos.

Los valores para las columnas forename, surname, email y password se definen como un tipo de datos VARCHAR. Se trata, en este caso, de cadenas de ca­ra­c­te­res variables cuya longitud (M) está limitada a 50 ca­ra­c­te­res mediante la opción “Length/Value”. Para la columna email se activa el índice UNIQUE. De este modo se garantiza que cada dirección de correo ele­c­tró­ni­co de la tabla se guarde una única vez.  

Para las columnas created_at y updated_at hemos se­le­c­cio­na­do el tipo de datos TIMESTAMP. El sistema de gestión de bases de datos almacena datos te­m­po­ra­les para crear y ac­tua­li­zar las entradas en formato YYYY-MM-DD HH:MM:DD. Puesto que el sistema tiene que generar au­to­má­ti­ca­me­n­te una marca temporal para cada entrada, se­le­c­cio­na­mos el valor estándar CURRENT_TIMESTAMP para la columna created_at. La columna updated_at adquiere im­po­r­ta­n­cia cuando se actualiza una entrada. En este sentido, pe­r­mi­ti­mos los valores Null para esta columna y se establece NULL como valor estándar.

Hecho

En PHP, el valor NULL re­pre­se­n­ta una entrada vacía, por lo que un campo tiene el valor NULL cuando no se le haya asignado ningún valor.

Como motor de al­ma­ce­na­mie­n­to se emplea el formato estándar de tabla de MySQL conocido como InnoDB.

ph­p­M­yA­d­min traduce todos los ajustes que se pueden realizar a una tabla en código SQL y, si es necesario, este puede vi­sua­li­zar­se, en “Vista previa de SQL”.

CREATE TABLE test.users ( id INT UNSIGNED NOT NULL AUTO_INCREMENT , forename VARCHAR(50) NOT NULL , surname VARCHAR(50) NOT NULL , email VARCHAR(50) NOT NULL , password VARCHAR(50) NOT NULL , created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP , updated_at TIMESTAMP NULL DEFAULT NULL , PRIMARY KEY (id), UNIQUE (email)) ENGINE = InnoDB;

En el capítulo sobre las consultas a las bases de datos te ofrecemos una de­s­cri­p­ción detallada sobre la sintaxis de SQL.

Los ajustes pueden guardarse haciendo clic en “Save” (guardar). La tabla users se muestra en el panel de na­ve­ga­ción de la base de datos bajo la opción test.

Ad­mi­ni­s­trar tablas

Para poder ad­mi­ni­s­trar una tabla ya creada, haz clic en el nombre de la tabla en el panel de na­ve­ga­ción. Acto seguido, ph­p­M­yA­d­min te muestra una visión general de los datos guardados en la tabla co­rre­s­po­n­die­n­te en la pestaña “Browse” (vi­sua­li­zar). En el caso de la tabla del ejemplo, la consulta no genera ningún resultado porque aún no contiene datos.

La barra del menú cuenta con di­fe­re­n­tes pestañas para gestionar las tablas de datos. Si quieres modificar la es­tru­c­tu­ra de una tabla de datos, haz clic en la etiqueta “Structure”; para agregar datos se­le­c­cio­na la opción “Insert” (añadir). ph­p­M­yA­d­min también permite buscar en las tablas, ad­mi­ni­s­trar derechos y exportar conjuntos de datos o im­po­r­tar­los de otras tablas.

Corregir la es­tru­c­tu­ra de la tabla

Si quieres corregir tu tabla po­s­te­rio­r­me­n­te para incluir columnas adi­cio­na­les, eliminar las que ya existen o re­vi­sar­las, ve a la pestaña “Structure”.

Puedes añadir columnas con la opción “Add columns”, mediante la que puedes indicar el número deseado de columnas nuevas y la posición de las mismas.

En el siguiento gráfico se añade una nueva columna por medio de updated_at:

Si quieres eliminar columnas ya exi­s­te­n­tes, se­le­c­ció­na­las marcando la casilla co­rre­s­po­n­die­n­te y haz clic en “Drop”.

La edición de una columna se lleva a cabo a través del botón “Change”. A través de este se te redirige a una vista de edición que se parece a la que se utiliza para la creación de tablas:

En algunas ci­r­cu­n­s­ta­n­cias, los cambios rea­li­za­dos en la es­tru­c­tu­ra de las tablas pueden conllevar una pérdida de in­fo­r­ma­ción. Por eso, antes de editar o eliminar las tablas pre­via­me­n­te creadas es re­co­me­n­da­ble que hagas una copia de seguridad de tu base de datos: haz clic en la pestaña “Export” (exportar), se­le­c­cio­na el formato de datos deseado para el backup y confirma el cambio mediante la opción “Go”. A co­n­ti­nua­ción, se abre una ventana de diálogo en la que el navegador web te pide el destino de al­ma­ce­na­mie­n­to para la descarga. El programa gratuito My­S­Q­L­Du­m­per es una al­te­r­na­ti­va a la creación de copias de seguridad de bases de datos con ph­p­M­yA­d­min.

Crear entradas de tabla

Para in­tro­du­cir los datos en una tabla vía ph­p­M­yA­d­min se puede recurrir a dos opciones: o bien se importan conjuntos de datos de un archivo externo (por ejemplo, de una copia de seguridad) o se crean las entradas en la tabla ma­nua­l­me­n­te. Se­le­c­cio­na la tabla de ejemplo users y ve a la pestaña “Insert” (insertar).

ph­p­M­yA­d­min te muestra la siguiente máscara de entrada de datos:

En el apartado “Column” aparece una lista de las columnas que se han definido para la tabla users, y “Type” alberga in­fo­r­ma­ción sobre el tipo de datos que se espera, así como el número máximo de ca­ra­c­te­res entre pa­ré­n­te­sis. Omitimos “Function” y pasamos di­re­c­ta­me­n­te a “Value”, apartado en el cual se definen los valores de cada una de las columnas de la tabla del ejemplo.

En el capítulo anterior se ha co­n­fi­gu­ra­do la tabla users de tal manera que el sistema de gestión de bases de datos provea au­to­má­ti­ca­me­n­te de contenido a los campos de las columnas id, created_at y updated_at. En la columna id se otorga un número de ide­n­ti­fi­ca­ción a cada entrada por medio de AUTO_INCREMENT. Al campo created_at se le asigna de manera au­to­má­ti­ca la fecha actual y a updated_at el sistema le concede el valor estándar NULL. Sin embargo, se tienen que in­tro­du­cir los datos de forma manual para las columnas forename, surname, email y password, tal y como se ve a co­n­ti­nua­ción con datos de usuario ficticios:

  • forename: John
  • surname: Doe
  • email: john@doe.com
  • password: qwertz

Pinchando en “Go” los datos se tra­n­s­fie­ren a la tabla. ph­p­M­yA­d­min salta au­to­má­ti­ca­me­n­te a la pestaña “SQL” y resume las ope­ra­cio­nes rea­li­za­das en la base de datos en una sentencia según la sintaxis de SQL:

INSERT INTO users (id, forename, surname, email, password, created_at, updated_at) VALUES (NULL, 'John', 'Doe', 'john@doe.com', 'qwertz', CURRENT_TIMESTAMP, NULL);

En principio, todas las ope­ra­cio­nes que se llevan a cabo en ph­p­M­yA­d­min a través de una interfaz gráfica de usuario también pueden es­cri­bi­r­se en SQL, lo que forma parte del pro­ce­di­mie­n­to habitual en el marco del de­sa­rro­llo web.

Las llamadas consultas SQL (SQL queries) se en­cue­n­tran en el código fuente de todas las apli­ca­cio­nes web dinámicas y permiten que el servidor web in­ter­ac­túe con la base de datos. El lenguaje de bases de datos SQL se basa en comandos y permite, por ejemplo, consultar y utilizar datos en el marco del fu­n­cio­na­mie­n­to del programa. En el siguiente capítulo del presente manual de MySQL te pre­se­n­ta­mos los comandos SQL más im­po­r­ta­n­tes, como por ejemplo SELECT, INSERT, DELETE y UPDATE, y te ofrecemos in­fo­r­ma­ción sobre la sintaxis de las ope­ra­cio­nes de bases de datos básicas.

Pero primero in­tro­du­ci­mos datos de usuario adi­cio­na­les en la tabla users y echamos un vistazo a la tabla re­su­l­ta­n­te en la pestaña “Browse”:

Con un clic sobre el nombre de una columna se puede organizar la tabla en el orden que se desee.

Es­ta­ble­cer la conexión con la base de datos

Tras haber in­tro­du­ci­do las entradas en la tabla de ejemplo users, en los si­guie­n­tes capítulos mostramos cómo se pueden solicitar estos datos vía PHP a través del servidor web Apache.

Para ello, el primer paso consiste en crear la conexión con la base de datos. En PHP se puede recurrir a tres in­te­r­fa­ces: MySQL Extension, MySQL Improved Extension (MySQLi) y PHP Data Objects (PDO).

  • MySQL Extension: la extensión MySQL consiste en una interfaz de MySQL hoy obsoleta que fue muy popular en su momento. Frente a MySQLi y PDO, esta versión tiene la de­s­ve­n­ta­ja de que no soporta ni se­n­te­n­cias pre­pa­ra­das ni pa­rá­me­tros con nombre.
  • MySQLi: MySQLi es una versión mejorada de la clásica extensión de PHP para acceder a las bases de datos MySQL. La interfaz trabaja de un modo pro­ce­di­me­n­tal, pero también está orientada a objetos. Sin embargo, su uti­li­za­ción es algo limitada en las bases de datos MySQL y Maria DB.
  • PDO: los PHP Data Objects (PDO) son in­te­r­fa­ces orie­n­ta­das a objetos que dispone una capa de ab­s­tra­c­ción para acceder a los datos. Así, los PDO no solo permiten la in­te­gra­ción de bases de datos MySQL en PHP, sino también de otros sistemas de bases de datos como Po­s­t­gre­S­QL, Oracle, MSSQL o SQLite.

A partir de aquí nos co­n­ce­n­tra­mos en las co­ne­xio­nes de bases de datos vía PDO.

Para poder hacer pe­ti­cio­nes a una base de datos con un script de PHP, la base de datos se ha de au­te­n­ti­fi­car en primer lugar. Con la siguiente línea de código se crea una conexión con la base de datos a través de un PDO:

<?php
$pdo = new PDO('DSN', 'username', 'password');
?>

Se re­co­mie­n­da in­te­grar­la al principio de cada script que contenga ope­ra­cio­nes de bases de datos.

Se utiliza la palabra clave de PHP new para crear una instancia de la clase base de PDO, cuyo co­n­s­tru­c­tor espera tres pa­rá­me­tros: el Data Source Name (DSN), un nombre de usuario y la co­n­tra­se­ña para la base de datos, si la hubiera. En este caso, el DSN está formado por los si­guie­n­tes pa­rá­me­tros:

  • co­n­tro­la­dor PDO para bases de datos: mysql
  • servidor de la base de datos (host=): localhost
  • nombre de la base de datos (dbname=): test
  • colación (charset=): utf8

Si todavía no has definido los datos de acceso para tu base de datos, utiliza el nombre de usuario root y una co­n­tra­se­ña vacía:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
?>

La conexión con la base de datos se almacena en la variable $pdo. Esto permite remitir a ella po­s­te­rio­r­me­n­te en el script.

Una vez creada la conexión con la base de datos, se le pueden realizar distintas consultas a medida que se va es­cri­bie­n­do el código en el script. Cuando se finaliza, también se in­te­rru­m­pe la conexión de la base de datos.

Realizar consultas con SELECT, INSERT, UPDATE y DELETE

Para extraer in­fo­r­ma­ción de un banco de datos es necesario recurrir al lenguaje de bases de datos SQL, que se basa se­má­n­ti­ca­me­n­te en el inglés y tiene un ma­n­te­ni­mie­n­to sencillo. Su sintaxis es, en gran medida, au­toe­x­pli­ca­ti­va.

En SQL se trabaja con se­n­te­n­cias, que también se conocen como consultas o preguntas.

Una consulta SELECT sencilla está formada, por ejemplo, por los si­guie­n­tes co­m­po­ne­n­tes:

SELECT column FROM tabla;

Primero se define el comando de SQL SELECT y a co­n­ti­nua­ción las columnas y tablas co­rre­s­po­n­die­n­tes a las que hace re­fe­re­n­cia el comando. Se utiliza un punto y coma para cerrar la sentencia.

Asimismo, tienes la po­si­bi­li­dad de ampliar la sentencia para una condición opcional o para una función de cla­si­fi­ca­ción o de agru­pa­ción:

SELECT columna FROM tabla WHERE condición ORDER BY secuencia de clasificación;

En este sentido se aplica la co­n­ve­n­ción de que, para facilitar la lectura, los comandos de SQL se escriben en mayúscula y los nombres de las bases de datos, de las tablas y de los campos en minúscula. SQL es fu­n­da­me­n­ta­l­me­n­te un lenguaje sin formato y no hace di­s­ti­n­cio­nes entre ma­yú­s­cu­las y mi­nú­s­cu­las.

En caso de que recurras a nombres de tabla y de columna que se co­rre­s­po­n­dan con palabras clave de SQL pre­de­fi­ni­das (no re­co­me­n­da­ble), estos deben ir entre comillas simples (' ').

A co­n­ti­nua­ción, ilu­s­tra­mos la sintaxis de se­n­te­n­cias SQL sencillas con ejemplos de los comandos SELECT, INSERT, UPDATE y DELETE.

SELECT

Se utiliza el comando SELECT para consultar series de datos (rows) de un número de­te­r­mi­na­do de tablas. Si se quieren mostrar en el navegador tanto el nombre y los apellidos como las di­re­c­cio­nes de correo ele­c­tró­ni­co de todos los usuarios en la tabla de ejemplo creada con an­te­rio­ri­dad, tienes que crear en el di­re­c­to­rio htdocs de tu entorno de XAMPP un nuevo archivo PHP text.php e incluir el siguiente script:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, email FROM users";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br /><br />";
}
?>

Este ejemplo de código se lee de la siguiente manera: en primer lugar se inicia el script con la etiqueta de inicio de PHP <?php. En la segunda línea se establece la conexión con la base de datos test en localhost y se guarda en la variable $pdo. La sentencia SQL con el comando SELECT se encuentra en la línea 3. En este punto se indica al sistema de gestión de bases de datos que consulte las columnas forename, surname y email de la tabla users. Esta sentencia se guarda en la variable $sql.

Las líneas de la 4 a la 7 muestran el co­n­s­tru­c­tor foreach, que permite la iteración sobre arrays, lo que significa que una es­tru­c­tu­ra de datos se repasa de forma gradual. Tanto los arrays que se quieren iterar como el modo en que se deben guardar los datos co­n­su­l­ta­dos se definen entre comillas detrás del co­n­s­tru­c­tor foreach:

$pdo->query($sql) as $row

La variable $pdo se comunica con la base de datos deseada a través de la conexión definida en la línea 2. Con la función query()y enviamos al banco de datos la sentencia SQL al­ma­ce­na­da en la variable $sql.

El servidor web solicita las columnas forename, surname e email de la tabla users de la base de datos test y recorre todas y cada una de las filas de la tabla en el marco del co­n­s­tru­c­tor foreach. La palabra clave de PHP as en la variable $row define dónde se tienen que guardar los datos se­le­c­cio­na­dos.

En la primera ronda del co­n­s­tru­c­tor foreach, el array tendría el siguiente aspecto:

$row = array (
    forename => John,
    surname => Doe,
    email => john@doe.com
)

Una parte del bucle foreach en nuestro ejemplo es la re­pro­du­c­ción como texto a cada vuelta del bucle vía echo. Aquí, se repasa cada línea de la tabla, se leen los datos de­po­si­ta­dos para las columnas definidas en la sentencia SQL y se emiten a través del navegador web.

Si se tienen que leer todas las columnas de una tabla de datos, se debe utilizar el asterisco (*) como es­pa­cia­dor en la secuencia SQL.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM users";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br />";
    echo "Created at: " . $row['created_at'] . "<br /><br />";
}
?>

Con ello se tiene la po­si­bi­li­dad de utilizar todos los datos incluidos en users en el marco del script. En el siguiente scree­n­shot se co­m­ple­me­n­ta el texto con la fecha relativa a la creación de la entrada:

En los dos ejemplos an­te­rio­res, el servidor web emite datos de usuario en el orden en el que se han in­tro­du­ci­do en la tabla users (conforme al ide­n­ti­fi­ca­dor). Si quieres emitir los datos en otro orden, defínelo con ayuda de la palabra clave de SQL ORDER BY. En el siguiente ejemplo se ofrecen los datos en orden al­fa­bé­ti­co según el nombre:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, email FROM users ORDER BY forename";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br /><br />";
}
?>

INSERT

Rara vez se crean ma­nua­l­me­n­te con ph­p­M­yA­d­min las entradas de una base de datos. Por lo general, es el servidor web es el que escribe los datos durante la ejecución del script, como ocurre, por ejemplo, cuando un usuario de Internet cu­m­pli­me­n­ta un fo­r­mu­la­rio online en una página web o deja un co­me­n­ta­rio en una tienda online. En ambos casos se utiliza el comando de SQL INSERT en un segundo plano.

Las se­n­te­n­cias SQL con el comando INSERT se crean siguiendo el siguiente esquema:

INSERT INTO tabla (columna1, columna 2, columna 3) VALUES (valor1, valor2, valor3);

Esto se lee de la siguiente manera: abre la tabla co­rre­s­po­n­die­n­te e introduce los valores 1, 2 y 3 en las columnas 1, 2 y 3.

Un script PHP sencillo con el que se pueda añadir otra entrada en la tabla de ejemplo users puede tener la siguiente apa­rie­n­cia:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "INSERT INTO users (forename, surname, email, password) 
VALUES ('Paddy', 'Irish', 'paddy@irish.com', 'qaywsx')";
if ($pdo->exec($sql) === 1)
    echo "New record created successfully";
?>

En primer lugar se crea la conexión con la base de datos y se guarda en la variable $pdo. A co­n­ti­nua­ción, se define la sentencia SQL y se almacena en la variable $sql. En la línea 5 se utiliza el operador flecha (->) para acceder a la variable $pdo y, con ayuda de la función exec(), ejecutar la sentencia SQL al­ma­ce­na­da en $sql.

Para ga­ra­n­ti­zar que el script inserte a un conjunto de ca­ra­c­te­res en la tabla users, es necesario comprobar el número de las filas im­pli­ca­das con ayuda de la condición if. Esta se ocupa de que el string New record created su­c­ce­s­s­fu­lly solo se emita a través del navegador web cuando la cantidad de conjuntos de datos incluidos sea 1. Si el script vuelve a eje­cu­tar­se, el aviso no aparecerá. Las entradas dobles se evitan de­fi­nie­n­do como UNIQUE al valor email.

Al acceder a la vista general de la tabla de ejemplo users en la base de datos test se puede observar que la tabla se ha ampliado con la entrada 5. El número de ide­n­ti­fi­ca­ción y la fecha se añaden de forma au­to­má­ti­ca según lo previsto.

UPDATE

Para ac­tua­li­zar conjuntos de datos ya exi­s­te­n­tes, se puede recurrir al comando SQL UPDATE tal y como se muestra en el siguiente ejemplo:

UPDATE table SET column1 = value1, value2 = value2 WHERE column3 = value3

Esta sentencia SQL tiene el siguiente si­g­ni­fi­ca­do: se­le­c­cio­na la tabla indicada y reemplaza el valor en la columna1 por el valor1 y el valor en la columna2 por el valor2 siempre y cuando la columna3 tenga el valor3. Si te olvidas de incluir la condición, MySQL so­bree­s­cri­be estos campos en todos los conjuntos de datos.

En este caso estamos tra­ba­ja­n­do con una sentencia SQL que vincula una operación a una condición. Si tra­s­la­da­mos esto a la tabla de nuestro ejemplo, la dirección de correo ele­c­tró­ni­co del usuario John Doe se actualiza por medio del siguiente script de PHP:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "UPDATE users 
SET email = 'john.doe@example.com', updated_at = NOW() WHERE id = 1";
if ($pdo->exec($sql) === 1)
    echo "Update successful";
?>

En la sentencia SQL definimos que el valor actual en la columna email tenga que ser re­em­pla­za­do por el valor john.doe@example.com siempre y cuando el valor en la columna contenga el id 1. Por lo tanto, se actualiza el conjunto de datos con la clave primaria 1. Asimismo, también se debe ac­tua­li­zar en la misma sentencia SQL el valor para la columna updated_at con ayuda de la función de MySQL now(), que devuelve la fecha actual. A co­n­ti­nua­ción, se ejecuta la sentencia como antes con la línea de código $pdo->exec($sql) en el contexto de una condición if.

Si la ac­tua­li­za­ción se ha llevado a cabo con éxito, ph­p­M­yA­d­min debería indicar en la etiqueta “Browse” que la tabla está ac­tua­li­za­da:

En el ejemplo, hemos ac­tua­li­za­do una dirección de correo ele­c­tró­ni­co y hemos su­s­ti­tui­do el valor estándar NULL en la columna updated_at por el registro de fecha y hora. El comando UPDATE permite también tra­n­s­fe­rir valores de una columna a otra. Esta operación puede uti­li­zar­se cuando se amplía la tabla users del ejemplo para la columna email_re­gi­s­tra­tion, lo que ofrece la po­si­bi­li­dad de di­fe­re­n­ciar entre dos di­re­c­cio­nes de correo ele­c­tró­ni­co: una utilizada durante el proceso de registro y otra de contacto actual que se puede modificar con el tiempo. Al principio, no obstante, ambas di­re­c­cio­nes son iguales, de modo que se pueden tra­n­s­fe­rir los valores de un campo a otro. Para ello creamos la columna nueva email_re­gi­s­tra­tion con ph­p­M­yA­d­min uti­li­za­n­do “Add columns” en la pestaña “Structure”:

Para tra­n­s­fe­rir los valores se utiliza la sentencia UPDATE siguiente:

UPDATE users SET email_registration = email

Debido a que se quieren ac­tua­li­zar todos los registros, no hace falta formular ninguna condición para la ac­tua­li­za­ción.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "UPDATE users SET email_registration = email";
$affected = $pdo->exec($sql);
echo "$affected rows updated<br>";
?>

Si se ejecuta el script en el servidor web, el sistema gestor de bases de datos tra­n­s­fie­re los valores de la columna email para todos los conjuntos de datos a la columna email_re­gi­s­tra­tion.

DELETE

Las entradas se pueden eliminar con el comando SQL DELETE, que se utiliza siguiendo el esquema que te pre­se­n­ta­mos a co­n­ti­nua­ción:

DELETE FROM tabla WHERE columna = valor

Si trabajas con ide­n­ti­fi­ca­do­res en tu base de datos, es adecuado ide­n­ti­fi­car con ellos las entradas que vas a borrar. Si, por ejemplo, quieres eliminar la entrada 5 en la tabla del ejemplo, debes proceder de la siguiente manera:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "DELETE FROM users WHERE id = 5";
$affected = $pdo->exec($sql);
echo "$affected rows updated<br>";
?>

El comando SQL DELETE elimina siempre una fila entera. Si quieres eliminar valores en columnas de­te­r­mi­na­das de una base de datos, puedes recurrir a la sentencia UPDATE. Con UPDATE tabla SET columna = NULL WHERE  se le puede asignar el valor NULL a una columna, pero siempre que se haya au­to­ri­za­do dicho valor para la columna co­rre­s­po­n­die­n­te.

Se­n­te­n­cias pre­pa­ra­das

Con PDO se pueden realizar ope­ra­cio­nes de bases de datos en calidad de se­n­te­n­cias pre­pa­ra­das (prepared sta­te­me­nts). En la ac­tua­li­dad, estas “consultas pre­fa­bri­ca­das” son una práctica habitual en el de­sa­rro­llo web y están re­s­pa­l­da­das, sobre todo, por todos los sistemas de gestión de bases de datos modernos.  

  • En los ejemplos expuestos con an­te­rio­ri­dad hemos tra­n­s­fe­ri­do di­re­c­ta­me­n­te valores de parámetro a la sentencia SQL. Los prepared sta­te­me­nts, por el contrario, trabajan con ma­r­ca­do­res que solo po­s­te­rio­r­me­n­te se llenan con valores. Esto hace posible que el SGBD pueda comprobar la validez de los pa­rá­me­tros antes de uti­li­zar­los. Las se­n­te­n­cias pre­pa­ra­das ofrecen, siempre y cuando estas se incluyan en el texto fuente, una pro­te­c­ción efectiva contra la inyección SQL. En este patrón de ataque, los hackers crean o modifican comandos SQL para acceder a in­fo­r­ma­ción sensible, tra­n­s­fe­rir datos o aportar los comandos en un sistema.

La inyección SQL se basa en un conocido fallo de seguridad en el entorno de las bases de datos SQL: si se entregan datos de usuario, por ejemplo, vía $_GET con pa­rá­me­tros estáticos, esto permite a los atacantes en­ri­que­cer el input con me­ta­ca­ra­c­te­res que pueden conducir a efectos in­de­sea­dos en caso de que estos alcancen al in­té­r­pre­te de SQL sin en­ma­s­ca­ra­mie­n­to. Este método de in­fi­l­tra­ción puede pre­ve­ni­r­se con efe­c­ti­vi­dad por medio de consultas pa­ra­me­tri­za­das. En este sentido, los prepared sta­te­me­nts hacen las veces de pla­n­ti­llas para los comandos SQL que se tra­n­s­mi­ten a la base de datos separados de los ve­r­da­de­ros pa­rá­me­tros. Esta valida los datos en­tre­ga­dos, enmascara los me­ta­ca­ra­c­te­res au­to­má­ti­ca­me­n­te e inserta los pa­rá­me­tros en lugar de los ma­r­ca­do­res en la secuencia SQL. 

Las se­n­te­n­cias pre­pa­ra­das ofrecen, además de seguridad, una ventaja en cuanto a re­n­di­mie­n­to. Esta se hace visible cuando el mismo comando SQL se tiene que ejecutar en bucle con di­fe­re­n­tes pa­rá­me­tros. Tras ser analizado si­n­tá­c­ti­ca­me­n­te, la sentencia preparada se introduce en el sistema de base de datos y tiene que eje­cu­tar­se con nuevos pa­rá­me­tros. Así, las consultas complejas pueden agi­li­zar­se de forma co­n­si­de­ra­ble.

En PDO, los prepared sta­te­me­nts pueden eje­cu­tar­se con ayuda de la función prepare(). Esta prepara una sentencia para la ejecución y devuelve un objeto de secuencia. Como marcador para los valores co­rre­s­po­n­die­n­tes se utiliza o el símbolo de in­te­rro­ga­ción (?) o un parámetro con nombre (named parameter).

Se­n­te­n­cias pre­pa­ra­das con pa­rá­me­tros sin nombre

El siguiente ejemplo de código muestra la operación INSERT como sentencia preparada con pa­rá­me­tros sin nombre:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
// prepare SQL statement 
$statement = $pdo->prepare("INSERT INTO users (forename, surname, email, password) 
VALUES (?, ?, ?, ?)");
// bind parameter 
$statement ->bindParam(1, $forename);
$statement ->bindParam(2, $surname);
$statement ->bindParam(3, $email);
$statement ->bindParam(4, $password);
// insert data sets
$forename = "Anders";
$surname = "Andersen";
$email = "anders@andersen.com";
$password = "mnbvc";
if ($statement->execute())
    echo "New record $forename created successfully<br>";
$forename = "Matti";
$surname = "Meikäläinen";
$email = "matti@meikäläinen.com";
$password = "jhgfd";
// display status
if ($statement->execute())
    echo "New record $forename created successfully<br>";
?>

Ini­cia­l­me­n­te se genera un objeto sentencia de la consulta deseada con ayuda de la función prepare() y se guarda en el array $statement. En lugar de valores de parámetro concretos, se utiliza el signo de in­te­rro­ga­ción como marcador.

Si una sentencia SQL solo contiene ma­r­ca­do­res, los valores en­tre­ga­dos por separado en el código se han de vincular a la sentencia. En PHP se emplea para ello la función bindParam(). Uti­li­za­mos el operador flecha (->) para acceder al método bindParam() del objeto $statement y asignarle variables (1 hace re­fe­re­n­cia al primer signo de in­te­rro­ga­ción, 2 al segundo, etc.).

Ahora, la plantilla SQL re­su­l­ta­n­te puede eje­cu­tar­se tan a menudo como se desea con los pa­rá­me­tros deseados. En el ejemplo actual se definen los valores de las variables para dos registros. La ejecución de las se­n­te­n­cias SQL pre­fa­bri­ca­das tiene lugar para cada entrada mediante execute().

Se­n­te­n­cias pre­pa­ra­das con pa­rá­me­tros con nombre

Los pa­rá­me­tros con nombre resultan algo más claros que los ma­r­ca­do­res con el signo de in­te­rro­ga­ción. Se trata, en este caso, de ma­r­ca­do­res pe­r­so­na­li­za­dos de­sig­na­dos, según las ne­ce­si­da­des, por el siguiente esquema:

:ejemplo

Los pa­rá­me­tros con nombre no pueden contener espacios en blanco o guiones, por lo que en su lugar se tiene que utilizar el guion bajo (_).

El siguiente ejemplo muestra la operación INSERT como sentencia preparada con pa­rá­me­tros con nombre:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
// prepare SQL statement
$statement = $pdo->prepare("INSERT INTO users (forename, surname, email, password) 
VALUES (:forename, :surname, :email, :password)");
// bind parameter
$statement ->bindParam(':forename', $forename);
$statement ->bindParam(':surname', $surname);
$statement ->bindParam(':email', $email);
$statement ->bindParam(':password', $password);
// insert data sets
$forename = "Anders";
$surname = "Andersen";
$email = "anders@andersen.com";
$password = "mnbvc";
if ($statement->execute())
    echo "New record $forename created successfully<br>";
$forename = "Matti";
$surname = "Meikäläinen";
$email = "matti@meikäläinen.com";
$password = "jhgfd";
$statement->execute();
// display status
if ($statement->execute())
    echo "New record $forename created successfully<br>";
?>

En la sentencia preparada se en­cue­n­tran los pa­rá­me­tros con nombre :forename, :surname, :email y :password. Estos se vinculan a las variables $forename, $surname, $email y $password por medio de bindParam(). En el ejemplo actual hemos designado tanto los pa­rá­me­tros como las variables en co­rre­s­po­n­de­n­cia con los nombres de las columnas de la tabla del ejemplo, ya que no es la sintaxis la que lo pre­de­te­r­mi­na. Sí se re­co­mie­n­da utilliar una de­no­mi­na­ción coherente a favor de un código fuente legible. Tanto la asi­g­na­ción de los valores de las variables como la ejecución de la sentencia SQL se lleva a cabo de forma análoga al ejemplo anterior.

Funciones de fecha y hora en MySQL

MySQL y MariaDB soportan diversas funciones para trabajar con in­fo­r­ma­ción sobre la fecha y la hora.  Puedes consultar aquí la lista completa de funciones de fecha y hora. En nuestro tutorial de MySQL para pri­n­ci­pia­n­tes te pre­se­n­ta­mos algunas de ellas.

Función de fecha y hora De­s­cri­p­ción
CURRENT_TIMESTAMP() / NOW() En el ejemplo para el comando SQL UPDATE ya has podido conocer la función NOW(), que es sinónimo de la función CURRENT_TIMESTAMP(). Se puede recurrir a ella siempre que, en el marco de una operación de bases de datos, se tenga que mantener la fecha y hora actuales.
CURDATE() / CURRENT_DATE() La función CURDATE() devuelve la fecha actual.
CURTIME() / CURRENT_TIME() La función CURTIME() devuelve la hora actual.
DAY() / DA­YO­F­MO­N­TH() Devuelve el día del mes (0–31); necesita una fecha o marca temporal como argumento
DAYOFWEEK() Devuelve el día de la semana (1 = domingo); necesita una fecha o marca temporal como argumento
MONTH() Devuelve el mes (1–12); necesita una fecha o marca temporal como argumento
YEAR() Devuelve el año (1000–9999, 0); necesita una fecha o marca temporal como argumento
DATE() Extrae la fecha de una fecha u hora dadas; necesita una fecha o marca temporal como argumento
TIME() Extrae la hora de una fecha u hora dadas; necesita una fecha o marca temporal como argumento
DATE_FORMAT() Ajusta una hora o fecha según los pa­rá­me­tros es­pe­ci­fi­ca­dos; necesita una fecha o marca temporal como argumento

Un posible escenario de apli­ca­ción para las funciones de fecha y hora son, por ejemplo, las consultas a las bases de datos en las que se tienen que leer todos los conjuntos de datos creados en un día de­te­r­mi­na­do.

El siguiente script emite todos los registros de la tabla users del ejemplo creados hoy:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, email FROM users WHERE DATE(created_at) = CURDATE()";
foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . "<br />";
    echo "E-Mail: " . $row['email'] . "<br /><br />";
}
?>

Para mostrar ex­clu­si­va­me­n­te las entradas de hoy se utiliza la siguiente condición en la sentencia SQL:

DATE(created_at) = CURDATE()

En primer lugar, se extrae la fecha de la marca temporal guardada en la columna created_at con ayuda de la función DATE() y se compara en el siguiente paso con la fecha actual. El comando SELECT solo se­le­c­cio­na las entradas cuya marca temporal coincida con la fecha actual. 

Como al­te­r­na­ti­va puedes se­le­c­cio­nar la entrada ac­tua­li­za­da el 17.05.2017, a cuyo efecto hay que adaptar la condición de la sentencia SQL:

SELECT forename, surname, email FROM users WHERE DATE(updated_at) = '2016-12-16'

En este caso se compara la fecha de re­fe­re­n­cia con una concreta. Asimismo, se pueden limitar las consultas a un año, mes o día de­te­r­mi­na­dos.

La siguiente sentencia hace re­fe­re­n­cia a todas las entradas de la tabla users creadas en mayo:

SELECT forename, surname, email FROM users WHERE month(created_at) = 12";

Además de los signos de igualdad, SQL también soporta los si­guie­n­tes ope­ra­do­res de co­m­pa­ra­ción en las co­n­di­cio­nes:

Operador de co­m­pa­ra­ción De­s­cri­p­ción
= Igual
< Menor que
> Mayor que
<= Menor o igual que
>= Mayor o igual que
!= No idéntico

Asimismo, también se pueden vincular varias co­n­di­cio­nes con ope­ra­do­res lógicos:

Operador lógico De­s­cri­p­ción
OR o || O lógico
AND o && Y lógico

La siguiente sentencia permite, por ejemplo, se­le­c­cio­nar todas las entradas creadas después de febrero y antes de abril:

SELECT forename, surname, email FROM users WHERE MONTH(created_at) > 2 AND MONTH(created_at) < 4";

Hasta ahora se han guardado los datos relativos a la fecha y hora en nuestra base de datos en el formato an­te­rio­r­me­n­te indicado. Con MySQL y MariaDB estos no están es­ta­ble­ci­dos. La función DATE_FORMAT() ofrece la po­si­bi­li­dad de ajustar la fecha y la hora con pa­rá­me­tros op­cio­na­les.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT forename, surname, DATE_FORMAT(created_at,'%D %M %Y') AS created_at FROM users WHERE id=1";
    foreach ($pdo->query($sql) as $row) {
    echo $row['forename'] . " " . $row['surname'] . " your profile was created at: " . $row['created_at'] . ".<br /><br />";
}
?>

La tabla que aparece a co­n­ti­nua­ción muestra posibles pa­rá­me­tros para la función DATE_FORMAT(), tal y como aparece es­pe­ci­fi­ca­do en la do­cu­me­n­ta­ción.

Parámetro De­s­cri­p­ción Campo de valores/Ejemplos
%c Valor numérico para los meses con 1 o 2 cifras De 0 a 12
%d Valor numérico para los días del mes con 2 cifras De 00 a 31
%e Valor numérico para los días del mes con 1 o 2 cifras De 0 a 31
%H Indicador para las horas con 2 cifras De 00 a 23
%i Valor numérico para los minutos con 2 cifras De 00 a 59
%j Valor numérico para los días con 3 cifras De 001 a 366
%k Valor numérico para las horas con 1 o 2 cifras De 0 a 23
%M Meses en el esquema regional actual Enero, febrero, marzo, etc.
%m Valor numérico para los meses con 2 cifras De 00 a 12
%s Valor numérico para los segundos con 2 cifras De 00 a 59
%T Valores te­m­po­ra­les en formato de 24 horas (abre­via­tu­ra para '%H:%i:%S'. HH:MM:SS
%u Valor numérico de la semana en el año empezando por el lunes De 00 a 53
%W Día de la semana en el esquema regional actual Lunes, martes, etc.
%w Día de la semana 0 = domingo, 6 = sábado
%Y Año con 4 cifras P.ej., 2017
%y Año con 2 cifras P.ej., 16

Mensajes de error en MySQL

Si un script no se ejecuta como estaba previsto, se pueden presentar errores si­n­tá­c­ti­cos en el código fuente o surgir tablas con nombres, columnas o variables in­co­rre­c­tos. El servidor de bases de datos no emite au­to­má­ti­ca­me­n­te estos mensajes de error, sino que, a menudo, el resultado deseado no se ma­ni­fie­s­ta sin hacer re­fe­re­n­cia alguna a la operación fallida.

Con errorInfo(), PDO ofrece una función con la que se pueden recuperar otros datos sobre errores en la última operación realizada en la base de datos, por ejemplo, para emitirlos a través del navegador web.

En el siguiente script de ac­tua­li­za­ción de la dirección de correo ele­c­tró­ni­co se emplea la función errorInfo() combinada con el co­n­s­tru­c­tor if . La condición es la correcta ejecución de la sentencia SQL. Si esta se lleva a cabo como es debido, el servidor web emite el string Update su­c­ce­s­s­ful. De lo contrario, se ejecuta el código es­pe­ci­fi­ca­do en else.

En el ejemplo, se informa al usuario de que se ha producido un error en SQL y se emite tanto la sentencia SQL co­rre­s­po­n­die­n­te como in­fo­r­ma­ción adicional sobre errores por medio de errorInfo():

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$statement = $pdo -> prepare("UPDATE user SET password = :password, updated_at = NOW() WHERE id = 1");
$statement ->bindParam(':password', $password);
$password = "YXDli_89%s";
if ($statement ->execute()){
    echo "Update successful";
} else {
    echo "SQL Error <br />";
    echo $statement->queryString."<br />";
    echo $statement->errorInfo()[2];
}
?>

Si el script se ejecuta a través del servidor web, se obtiene la siguiente in­fo­r­ma­ción:

SQL Error 
UPDATE user SET password = :password, updated_at = NOW() WHERE id = 1
Table 'test.user' doesn't exist

El comando SQL UPDATE hace re­fe­re­n­cia a una tabla con el nombre user. La tabla de nuestro ejemplo se llama users. El servidor SQL no es capaz de encontrar la tabla so­li­ci­ta­da y ofrece el mensaje Table 'test.user' doesn't exist. El motivo del fallo ha sido un error ti­po­grá­fi­co en la sentencia SQL que se corrige con facilidad.

En cuanto a los valores de retorno de la función errorInfo(), se puede hablar de un array con tres elementos:

[0] = código de error SQL

[1] = código de error es­pe­cí­fi­co del co­n­tro­la­dor

[2] = mensaje de error es­pe­cí­fi­co del co­n­tro­la­dor

La in­fo­r­ma­ción a la que se accede a través de errorInfo() puede definirse indicando los elementos deseados entre corchetes.

En la práctica, no es muy común que el navegador web entregue in­fo­r­ma­ción detallada sobre los errores, pues no sirven de nada a los usuarios. Por el contrario, los atacantes po­te­n­cia­les pueden utilizar estos mensajes de error para seguir a las consultas SQL y encontrar las de­bi­li­da­des de las apli­ca­cio­nes. Por esto, es re­co­me­n­da­ble que la in­fo­r­ma­ción general dada a los usuarios acerca de los fallos sea muy general y que la in­fo­r­ma­ción referente a los mismos se almacene de manera interna.

Esto podría lograrse como se muestra a co­n­ti­nua­ción:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$statement = $pdo->prepare("UPDATE user SET password = :password, updated_at = NOW() WHERE id = 1");
$statement->bindParam(':password', $password);
$password = "YXDli_89%s";
if ($statement->execute()) {
     echo "Update successful";
} else {
    echo “Unfortunately an error has occurred during the updating of your password. Please contact our administrator on: admin@website.com.”;
    $error = DATE("Y-m-d H:i:s") . " - " . __FILE__ . " - " . $statement->queryString . " - " . $statement->errorInfo()[2] . " \r\n";
    file_put_contents("sqlerrors.log", $error, FILE_APPEND); 
}
?>

En lugar de emitir el valor de retorno de la función errorInfo() a través de echo en el navegador web, se guarda junto con la marca temporal actual, la ruta del archivo y la sentencia SQL en la variable $error.

La función PHP DATE() ofrece la marca temporal de Unix en el formato es­pe­ci­fi­ca­do. La llamada constante mágica __FILE__ entrega la ruta completa para el archivo test.php. So­li­ci­ta­mos la sentencia SQL actual al igual que en el ejemplo anterior vía $statement->que­r­y­S­tri­ng. Por último, se almacenan los datos guardados en $error con ayuda de la función file_put_contents() como texto en el archivo sqlerrors.log en la carpeta htdocs.

JOIN

Como ya se ha me­n­cio­na­do en el capítulo sobre las bases de datos re­la­cio­na­les, se pueden consultar datos de di­fe­re­n­tes bases de datos si­mu­l­tá­nea­me­n­te. Si ya estás fa­mi­lia­ri­za­do con las ope­ra­cio­nes básicas de bases de datos, vo­l­ve­re­mos a recurrir a ellas para demostrar cómo se pueden vincular las di­fe­re­n­tes tablas de tu base de datos en un join (en español, unión o conexión).

La co­m­bi­na­ción de las tablas de una consulta tiene lugar con ayuda del comando SQL JOIN. Aquí se vinculan dos o más tablas no­r­ma­li­za­das por medio de columnas enlazadas, lo que se puede llevar a cabo a través de una clave foránea.

A co­n­ti­nua­ción, te de­mo­s­tra­mos cómo se enlazan las tablas de bases de datos con el siguiente ejemplo:

La tabla muestra una selección de algunas de las canciones más famosas de los años 60 y puede servir como ejemplo negativo de un mal diseño de bases de datos

A primera vista llama la atención el hecho de que la tabla muestre gran cantidad de datos re­du­n­da­n­tes. Estos se eliminan y  di­s­tri­bu­yen en el marco de la no­r­ma­li­za­ción en tablas separadas y se combinan entre sí con ayuda de claves foráneas.

Formas normales

Un buen diseño de bases de datos se distingue por tener un mínimo de re­du­n­da­n­cia. Sin embargo, deben evitarse las entradas dobles a través de la no­r­ma­li­za­ción de las tablas de datos. En el marco del modelo de bases de datos re­la­cio­nal se han es­ta­ble­ci­do tres formas normales co­m­ple­me­n­ta­rias que es­ta­ble­cen reglas fijas para una es­tru­c­tu­ra­ción de datos óptima.

Primera forma normal

Una tabla se co­rre­s­po­n­de con la primera forma normal cuando todos los valores de los atributos son atómicos. Se entiende por valores de atributo atómicos aquellos que solo contienen in­fo­r­ma­ción, lo que se puede observar cla­ra­me­n­te en nuestro ejemplo negativo.

Ob­se­r­ve­mos, por ejemplo, las columnas album_title e interpret de la tabla album. En lugar de incluir cada dato de la tabla en una columna diferente, lo hemos hecho más fácil y hemos in­tro­du­ci­do datos relativos al año de pu­bli­ca­ción del álbum, así como de la exi­s­te­n­cia de la banda entre pa­ré­n­te­sis detrás del título del álbum o de los datos del in­té­r­pre­te. Este descuido se hace evidente cuando, por ejemplo, se quieren consultar todos los títulos pu­bli­ca­dos en un año de­te­r­mi­na­do.

En este caso, es re­co­me­n­da­ble crear tablas de datos siguiendo las reglas de la primera forma normal. En la tabla de nuestro ejemplo esto tendría el siguiente aspecto:

Los datos aparecen separados y pueden leerse sin problemas. Asimismo, la tabla sigue co­n­te­nie­n­do re­du­n­da­n­cias, como antes. Sigue leyendo y te ex­pli­ca­re­mos cómo puedes eli­mi­nar­las.

Segunda forma normal

Una tabla está en segunda forma normal cuando se cumplen todas las co­n­di­cio­nes de la primera forma normal y cada atributo que no forme parte de la totalidad de la clave primaria de la tabla es in­de­pe­n­die­n­te fu­n­cio­na­l­me­n­te.

A menudo, hay tablas de datos que contienen una columna que funciona como clave primaria. Las tablas de este tipo se co­rre­s­po­n­den au­to­má­ti­ca­me­n­te con la segunda forma normal si se cumplen las co­n­di­cio­nes de la primera forma normal. En ocasiones puede suceder que la clave primaria de una tabla aparezca en dos columnas, tal y como ocurre en la tabla del ejemplo.

Para obtener el título deseado de la columna title de la tabla title se necesita, por un lado, el album_id y por otro el número del título en la columna track. Se puede obtener el título Sympathy for the Devil, por ejemplo, por medio de la clave primaria album_ID = 3 AND track = 1, por lo que en este caso se puede hablar de una clave primaria compuesta. Esta es necesaria para las consultas referidas a la columna title. Las columnas album_title, released, interpret y years_active dependen de album_id. Sin embargo, para estas columnas no existe ninguna de­pe­n­de­n­cia funcional con respecto a la totalidad de las claves primarias. No se cumplen, por tanto, las co­n­di­cio­nes de la segunda forma normal.

Esto puede mo­di­fi­car­se colocando la columna title en una tabla nueva y vi­n­cu­lá­n­do­la mediante una clave foránea (album_id) con la tabla de salida:

La tabla album revisada solo contiene una clave primaria y cumple au­to­má­ti­ca­me­n­te las co­n­di­cio­nes de la segunda forma normal. La nueva tabla title contiene la columna sin clave title, que es, a nivel funcional, to­ta­l­me­n­te de­pe­n­die­n­te de ambas partes de la clave primaria (album_id y track) y se co­rre­s­po­n­de, así, con la segunda forma normal.

En la segunda forma normal, la tabla de datos album también contiene entradas re­du­n­da­n­tes.

Tercera forma normal

Si una tabla se co­rre­s­po­n­de con la tercera forma normal, deben cumplirse todas las co­n­di­cio­nes de la segunda forma normal y de la primera forma normal. Además, se debe comprobar que ningún atributo no clave dependa de forma tra­n­si­ti­va de un atributo clave. Puede que esta condición parezca muy co­m­pli­ca­da, pero se puede explicar en pocas palabras: se da una de­pe­n­de­n­cia tra­n­si­ti­va siempre que un atributo no clave dependa de otro atributo no clave.

Pre­ci­sa­me­n­te esto es lo que se aplica a la tabla album del ejemplo. Aquí se en­cue­n­tran las columnas interpret y years_active. Mientras que se puede ide­n­ti­fi­car al in­té­r­pre­te por medio de album_id, el año de formación de la banda depende tanto de los in­té­r­pre­tes como, a nivel tra­n­si­ti­vo, del album_id. Esto tiene como de­s­ve­n­ta­ja que cada vez que se añada un álbum nuevo a un in­té­r­pre­te que ya se ha me­n­cio­na­do, el sistema de gestión de bases de datos guarda au­to­má­ti­ca­me­n­te un valor re­du­n­da­n­te en la columna years_active.  

Para cumplir las co­n­di­cio­nes de la tercera forma normal y eliminar todas las re­du­n­da­n­cias de nuestra tabla, se deben incluir las columnas interpret y years_active en una tabla separada y vi­n­cu­lar­las con la tabla de salida album mediante la clave foránea (interpret_id).

Así, se obtienen tres tablas no­r­ma­li­za­das: interpret, album y title.

Si se quiere tra­n­s­mi­tir un título de­te­r­mi­na­do en nuestra base de datos, además de la in­fo­r­ma­ción sobre el álbum y el in­té­r­pre­te, estas deben estar co­m­bi­na­das entre sí en tres tablas separadas con ayuda del comando JOIN y de la clave foránea co­rre­s­po­n­die­n­te.

De­fi­ni­ción de claves foráneas a través de ph­p­M­yA­d­min

Si eliges el motor de bases de datos InnoDB, puedes definir có­mo­da­me­n­te re­la­cio­nes de claves foráneas a través de la interfaz gráfica del software de ad­mi­ni­s­tra­ción ph­p­M­yA­d­min. Asimismo, la clave primaria de una tabla puede uti­li­zar­se como clave foránea en muchas otras tablas.

En nuestro ejemplo se requieren dos co­ne­xio­nes para enlazar las tres tablas no­r­ma­li­za­das album, interpret y title:

  • Para la primera conexión se utiliza la clave primaria album_id de la tabla album como clave foránea en la tabla title.
  • Para la segunda conexión se utiliza la clave primaria interpret_id de la tabla interpret como clave foránea en la tabla album.

Las re­la­cio­nes de las claves foráneas pueden co­n­te­m­plar­se en el siguiente gráfico:

A la hora de es­ta­ble­cer el vínculo entre las tablas de datos una columna que funciona como clave foránea debe contener los atributos UNIQUE o INDEX.

La relación entre las claves primarias y las foráneas se co­rre­s­po­n­de, por lo general, con la relación del tipo 1:n. Cada campo de datos en la columna clave primaria de la tabla A está re­la­cio­na­do con varios campos de datos (n) en la columna clave foránea de la tabla B. Cada campo de datos en la columna clave foránea de la tabla B hace re­fe­re­n­cia a un campo de datos de la columna clave primaria de la tabla A. Como ejemplo, en la columna de clave primaria album_id de la tabla album hay cuatro entradas vi­n­cu­la­das a través de la clave foránea title.album_id con las ocho entradas de la tabla title.

Para es­ta­ble­cer las re­la­cio­nes deseadas, creamos las tablas album, interpret y title en ph­p­M­yA­d­min y definimos nuestras claves primarias en el marco de la creación de la tabla como ya se ha indicado a través de la opción “Index”. Es re­co­me­n­da­ble tener en cuenta aquí el hecho de que las columnas que fu­n­cio­na­rán po­s­te­rio­r­me­n­te como claves foráneas se tienen que marcar como INDEX o UNIQUE mediante la opción Index. Para las re­la­cio­nes 1:n solo es apto INDEX, puesto que los valores en el campo UNIQUE no deben repetirse.

A co­n­ti­nua­ción se definen las claves foráneas. Para ello, regresa a la tabla album del ejemplo. Elige la tabla en el panel de na­ve­ga­ción y haz clic en la pestaña Structure en la barra de menú, que contiene el botón “Relation view” (vista de la relación):

Las re­la­cio­nes de claves foráneas se definen en la vista de re­la­cio­nes de las tablas de datos por medio del campo de entrada “Foreign key co­n­s­trai­nts”:

En la tabla album, la columna interpret_id debe funcionar como clave foránea, que se basa en la clave primaria interpret_id de la tabla interpret.

Por lo tanto, se debe se­le­c­cio­nar la columna interpret_id como clave foránea en el menú de­s­ple­ga­ble bajo “Column”. Ten en cuenta que aquí solo se es­pe­ci­fi­can columnas marcadas como INDEX, UNIQUE o PRIMARY. En el campo de entrada triple “Foreign key co­n­s­trai­nt (InnoDB)” se debe es­pe­ci­fi­car en qué clave primaria, de qué tabla y en qué base de datos debe basarse la clave foránea. Para ello, realiza la siguiente selección:

base de datos: test

tabla: interpret

clave primaria: interpret_id

El campo “Co­n­s­trai­nt name” puede quedarse vacío, aunque el sistema de gestión de bases de datos le otorga un nombre au­to­má­ti­ca­me­n­te. Sin embargo, debe definirse el modo en que se comporta una tabla con claves foráneas en cuanto la clave primaria su­b­ya­ce­n­te para la clave foránea se modifique o se elimine.

Si, por ejemplo, se elimina un in­té­r­pre­te de la tabla padre interpret, también des­apa­re­ce la clave primaria re­la­cio­na­da con dicha entrada. Se trata, por lo tanto, de de­te­r­mi­nar lo que debe pasar con las entradas re­la­cio­na­das con esta entrada por medio de una clave foránea, lo que en nuestro ejemplo se refiere a los álbumes de un in­té­r­pre­te.

Para de­te­r­mi­nar el co­m­po­r­ta­mie­n­to de una tabla con claves foráneas en caso de que se den las opciones UPDATE o DELETE, tanto en MySQL como en MariaDB se puede recurrir a cuatro opciones.

  • RESTRICT: la opción RESTRICT impide la mo­di­fi­ca­ción de la tabla padre siempre que existan otras tablas que remitan a esta. En el ejemplo que pre­se­n­ta­mos en nuestro tutorial para aprender a usar MySQL, no se puede eliminar un conjunto de datos en la tabla interpret si hay conjuntos de datos en la tabla album que estén ligados a esta.
  • CASCADE: la opción CASCADE se ocupa de que se transmita el cambio realizado en la tabla padre al resto de tablas que re­fe­re­n­cian a dicha tabla padre. Si, por ejemplo, cambiamos el interpret_id del in­té­r­pre­te The Rolling Stones de 2 a 8, mediante la opción de clave foránea CASCADE este cambio se aplicará a todas las tablas. Si se elimina una entrada en la tabla padre, esto da lugar, además, a que todos los conjuntos de datos vi­n­cu­la­dos se eliminen de las otras tablas. Es re­co­me­n­da­ble prestar atención al hecho de que la eli­mi­na­ción de una única entrada puede conllevar la des­apa­ri­ción de numerosos conjuntos de datos.
  • SET NULL: si se­le­c­cio­nas la opción SET NULL, se le asignará NULL al valor en la columna de clave foránea cuando se modifique o elimine la clave primaria en la tabla padre. 
  • NO ACTION: en MySQL, la opción NO ACTION equivale a la opción RESTRICT.

Una vez definida la opción deseada para la relación de claves foráneas, haz clic en “Save” para confirmar la entrada. El sistema de gestión de bases de datos otorga un nombre de forma au­to­má­ti­ca a la nueva relación.

Tipos de JOIN en MySQL y MariaDB

Las re­la­cio­nes de claves foráneas te permiten acceder a los datos de di­fe­re­n­tes tablas con una única sentencia SQL. Para ello, puedes recurrir a cuatro tipos de JOIN en MySQL y MariaDB:

  • INNER JOIN: con INNER JOIN, el sistema de bases de datos busca entradas comunes en tablas enlazadas con JOIN. Solo se leen los conjuntos de datos que guardan coin­ci­de­n­cias entre sí, es decir, en los que los valores de las columnas enlazadas (de clave primaria y clave foránea) de ambas tablas coinciden.
  • OUTER JOIN: con OUTER JOIN se pueden es­ta­ble­cer di­fe­re­n­cias entre tablas de datos situadas a la izquierda o a la derecha. A di­fe­re­n­cia de INNER JOIN, no solo se leen los conjuntos de datos que coinciden entre sí en ambas tablas, sino también todos los conjuntos de datos restantes de las tablas de la derecha y de la izquierda.
  • LEFT JOIN: se leen todos los conjuntos de datos de la tabla izquierda y de la derecha en los que se en­cue­n­tran coin­ci­de­n­cias.
  • RIGHT JOIN: se leen todos los conjuntos de datos de la tabla derecha y de la izquierda en los que se en­cue­n­tran coin­ci­de­n­cias.

En este tutorial de MySQL nos centramos en INNER JOIN.

La sintaxis de INNER JOIN sigue el siguiente esquema:

SELECT * FROM tabla1
INNER JOIN tabla2 ON tabla1.clave foránea = tabla2.clave primaria
WHERE columna = valor

El comando SELECT combinado con el marcador * indica al sistema de gestión de bases de datos que lea los valores de todas las columnas para los que son de apli­ca­ción la co­n­di­cio­nes de las cláusulas ON y WHERE.

Puesto que en este caso se habla de INNER JOIN, solo se extraen de la base de datos los conjuntos de datos en los que existan coin­ci­de­n­cias entre la clave foránea de la tabla1 y la clave primaria de la tabla2. Además, se puede definir una función de filtro opcional con ayuda de la cláusula WHERE.

Te aclaramos esto a co­n­ti­nua­ción con un ejemplo basado en nuestras tablas no­r­ma­li­za­das album, interpret y title:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id";
foreach ($pdo->query($sql) as $row) {
    echo $row['album_title'] . " was released by " . $row['interpret'] . " in " . $row['released'] . ".<br /><br />";
}
?>

El script del ejemplo muestra una operación INNER JOIN en la que la tabla album está re­la­cio­na­da con la tabla interpret y se se­le­c­cio­nan los conjuntos de datos en los que se da una coin­ci­de­n­cia entre la clave primaria y la foránea.

album.interpret_id = interpret.interpret_id

Nuestra base de datos recoge todos los conjuntos de datos (un LEFT o RIGHT JOIN tendría el mismo resultado). A co­n­ti­nua­ción se generan los valores leídos con ayuda de un bucle foreach y de la co­n­s­tru­c­ción del lenguaje echo en el navegador.

En el marco de una sentencia SQL, se extraen los datos del in­té­r­pre­te de la tabla interpret y los relativos al título del álbum y al año de pu­bli­ca­ción del disco que aparecen en la tabla album.

La decisión de los conjuntos de datos del join que deben vi­sua­li­zar­se viene de­li­mi­ta­da con una condición en la cláusula WHERE. Si, por ejemplo, se quiere acceder a los álbumes pu­bli­ca­dos en el año 1968, se puede proceder de la siguiente manera:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id WHERE released = 1968";
foreach ($pdo->query($sql) as $row) {
    echo $row['album_title'] . " was released by " . $row['interpret'] . " in " . $row['released'] . ".<br /><br />";
}
?>

Mediante la condición WHERE released = 1968 se limita dicha emisión en el navegador a un solo álbum. Beggars Banquet de los Rolling Stones es el único álbum en nuestra todavía base de datos abarcable que se publicó en el año 1968.

Con ayuda del comando JOIN se pueden unir varias tablas en una red de datos. En el ejemplo siguiente se combina la tabla album en un INNER JOIN con las tablas interpret y title para arrojar in­fo­r­ma­ción completa sobre los títulos musicales al­ma­ce­na­dos en la base de datos.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id 
INNER JOIN title ON album.album_id = title.album_id";
foreach ($pdo->query($sql) as $row) {
    echo $row['title'] . " was released by " . $row['interpret'] . " as Track " . $row['track'] . " on " . $row['album_title'] . " in " . $row['released'] . ".<br /><br />";
}
?>

De ser necesario, es posible definir una cláusula WHERE con una función de filtrado. Un ejemplo de ello es si se quiere emitir in­fo­r­ma­ción sobre la pista 7 del álbum “Abbey Road”.

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$sql = "SELECT * FROM album
INNER JOIN interpret ON album.interpret_id = interpret.interpret_id 
INNER JOIN title ON album.album_id = title.album_id WHERE track = 7 AND album_title = 'Abbey Road'";
foreach ($pdo->query($sql) as $row) {
    echo $row['title'] . " was released by " . $row['interpret'] . " as Track " . $row['track'] . " on " . $row['album_title'] . " in " . $row['released'] . ".<br /><br />";
}
?>

Es re­co­me­n­da­ble prestar atención al hecho de que en la tabla title nos en­fre­n­ta­mos a una clave primaria múltiple. Si se quiere hacer re­fe­re­n­cia a un título de­te­r­mi­na­do, además del número de la pista, se necesita el album_id que aparece en la tabla junto al título del álbum.

Desde afi­cio­na­dos hasta pro­fe­sio­na­les

Este manual de MySQL para pri­n­ci­pia­n­tes está concebido como un curso intensivo que explica los fu­n­da­me­n­tos sobre los sistemas de bases de datos basados en SQL y presenta ejemplos de interés práctico de sencillas ope­ra­cio­nes de bases de datos. Si la in­fo­r­ma­ción expuesta sobre las po­si­bi­li­da­des de la apli­ca­ción objeto de la presente guía es de tu interés, también puedes consultar la do­cu­me­n­ta­ción enlazada en el capítulo in­tro­du­c­to­rio sobre los sistemas de gestión de bases de datos MySQL y MariaDB. Asimismo, Internet también alberga gran cantidad de páginas web que ofrecen tu­to­ria­les y ejemplos de uso sobre el popular sistema de gestión de bases de datos. Es re­co­me­n­da­ble, asimismo, visitar la pla­ta­fo­r­ma online Stack Overflow, en la que una amplia comunidad de usuarios formada por más de 6,5 millones de de­sa­rro­lla­do­res in­te­r­ca­m­bian in­fo­r­ma­ción y hablan de los problemas relativos al de­sa­rro­llo del software. La Digital Guide de IONOS, asimismo, contiene otros muchos artículos sobre bases de datos, a los que puedes acceder haciendo clic sobre las etiquetas que aparecen más abajo.

Ir al menú principal