Desarrollo Web Lección 7 - Bases de Datos
Uniones
Objetivo
En esta actividad, usará datos de dos tablas y aprenderá por qué es una buena idea separar los datos en varias tablas.
Preparación
Valor Nulo
Imagina que queremos ampliar nuestra base de datos de héroes de Marvel para incluir las películas en las que aparecen.
Una forma de hacer esto sería agregar campos (columnas) para cada película como esta.
character_name | hero_villain | first_appeared | power | iron_man | first_avenger | thor | avengers |
Black Widow | Hero | 1964 | Skill | NULL | NULL | NULL | True |
Captain America | Hero | 1941 | Biological | NULL | True | NULL | True |
Hulk | Hero | 1962 | Biological | NULL | NULL | NULL | True |
Iron Man | Hero | 1963 | Technology | True | NULL | NULL | True |
Loki | Villain | 1949 | Magic | NULL | NULL | True | True |
Scarlet Witch | Hero | 1964 | Magic | NULL | NULL | NULL | NULL |
Tablas Múltiples
Ahora considere si agregamos una columna para las 27 películas lanzadas hasta ahora en la MCU, sin mencionar las 4 series de televisión. Nuestra mesa sería enorme y la mayor parte estaría vacía (NULL).
Eso llevaría más tiempo de administrar, ralentizaría nuestras consultas y ocuparía más espacio en nuestro servidor. Ninguno de estos son buenos resultados, pero ¿qué podemos hacer al respecto?
Podríamos hacer que cada combinación de héroe y película tenga su propia fila de datos, como esta.
character_name | hero_villain | first_appeared | power | movie |
Black Widow | Hero | 1964 | Skill | Iron Man |
Black Widow | Hero | 1964 | Skill | Avengers |
Captain America | Hero | 1941 | Biological | First Avenger |
Captain America | Hero | 1941 | Biological | Avengers |
Hulk | Hero | 1962 | Bilogical | Avengers |
Iron Man | Hero | 1963 | Technology | Iron Man |
Iron Man | Hero | 1963 | Technology | Avengers |
Loki | Villain | 1949 | Magic | Avengers |
Esto se ve mucho mejor. Ocupa menos espacio y sería más rápido, pero aún tiene problemas.
La primera es que se repiten muchos datos. Mira como tenemos la información de Black Widow (hero_villain, first_appeared y power) dos veces. Si queremos actualizar esos datos, tendríamos que actualizar cada fila con Black Widow. Eso ralentizará las cosas y podría conducir a errores.
La segunda es más sutil. ¿Notaste que nos falta la Bruja Escarlata? Podríamos incluir una fila para ella sin datos de la película, pero ¿cómo nos las arreglaríamos más tarde cuando agregáramos Age of Ultron? ¿Dejaríamos una fila NULL ocupando espacio en la base de datos sin ningún propósito? Podríamos buscar filas NULL y reemplazarlas cuando agreguemos la primera película, pero no es lo ideal.
La respuesta es separar los datos en dos tablas. Uno con información sobre el héroe y otro rastreando en qué películas han estado.
Ya has visto el primero.
Echemos un vistazo a la segunda. Haga clic en la tabla llamada 'apariencias' en el panel izquierdo de PHPMyAdmin.
Aquí vemos una tabla con una lista de pares héroe/película.
Índice único
Recuerde que cada tabla debe tener un índice único. Pero en esta lista, Iron Man y Loki aparecen más de una vez en 'character_name' y 'The Avengers' aparece cinco veces en 'movie', por lo que ninguno es apropiado para nuestro índice principal.
En su lugar, creamos un nuevo campo que será único. Por costumbre, lo llamamos 'id'.
Haga clic en la pestaña 'Estructura' para ver cómo se define.
Es un número entero con el atributo 'sin signo'. Esto significa que solo pueden ser números positivos. En computación, donde los números se almacenan como una colección de unos y ceros, cada uno llamado 'bit', generalmente se usa un bit para indicar si un número es positivo o negativo. Al hacer que nuestro índice no esté firmado, estamos duplicando el número de filas que podemos tener.
Observe también en 'Extra' que el campo 'id' está configurado en 'AUTO_INCREMENT', lo que significa que cuando agregamos filas, la nueva fila tendrá un número uno más alto que el número más alto utilizado anteriormente. Esto nos evita tener que encontrar un número no utilizado nosotros mismos.
Entonces, ¿cómo combinamos los datos de estas tablas?
Uniones
Haga clic en la pestaña 'SQL'.
Sobrescriba la consulta con esta. Puede mantenerlo todo en una sola línea, pero extenderlo hace que sea más fácil de leer.
SELECT *
FROM heroes
LEFT JOIN appearances ON heroes.character_name=appearances.character_name
Los resultados ahora muestran toda la información que teníamos en nuestra tabla más reciente.
La ventaja aquí es que solo necesitamos actualizar una línea en nuestra tabla de héroes para cada héroe, por lo que hay mucho menos riesgo de error y ocupamos menos espacio de almacenamiento. También es más rápido de procesar, pero eso probablemente no se note para una base de datos tan pequeña.
Desglosemos la consulta.
SELECT *
es el mismo que antes. Dice mostrar todos los campos. En este caso, muestre todos los campos de todas las tablas incluidas en la consulta.
FROM heroes
es lo mismo que antes, aunque he omitido los acentos graves. Los acentos graves no son necesarios en la mayoría de los casos.
La última línea es nueva. LEFT JOIN
significa que estamos conectando todas las filas de la segunda tabla que coinciden con la primera tabla, y estamos usando el campo 'character_name' de ambas tablas para identificar la coincidencia (heroes.character_name=appearances.character_name
).
Observe que identificamos los campos a usar nombrando primero la base de datos y separándolos por un .
. Esto asegura que la base de datos no se confunda porque los nombres de los campos son los mismos en ambas tablas.
Observe también que Scarlet Witch aparece en los resultados porque está en la primera tabla, aunque no aparece en la segunda tabla. La información que falta aparece como NULL
. Veamos qué pasa si cambiamos las mesas.
Podríamos intercambiar los nombres de las tablas en la consulta FROM appearances LEFT JOIN heroes ON heroes.character_name=appearances.character_name
, pero también podemos simplemente cambiar el tipo de unión de LEFT
a RIGHT
.
SELECT *
FROM heroes
RIGHT JOIN appearances ON heroes.character_name=appearances.character_name
Cuando ejecuta esta consulta, Scarlet Witch desaparece porque la segunda tabla (apariencias) es nuestra base y ella no aparece en esa tabla.
Volvamos a la consulta original para que podamos ver a Scarlet Witch.
SELECT *
FROM heroes
LEFT JOIN appearances ON heroes.character_name=appearances.character_name
Antes de continuar, observe que tenemos información innecesaria en nuestros resultados. La base de datos requiere 'id', pero por ahora no tiene ningún valor para nosotros. Tampoco tiene ningún valor repetir los datos de 'character_name'.
Tomemos solo los campos que realmente necesitamos.
SELECT character_name, hero_villain, first_appeared, power, movie
FROM heroes
LEFT JOIN appearances ON heroes.character_name=appearances.character_name
Observe que hemos reemplazado * con la lista de campos específicos que queremos incluir.
Desafortunadamente, cuando ejecuta la consulta, verá un error que dice que la columna 'character_name' es ambigua. Aunque son exactamente iguales en ambas tablas, la base de datos necesita saber cuál elegir.
SELECT heroes.character_name, hero_villain, first_appeared, power, movie
FROM heroes
LEFT JOIN appearances ON heroes.character_name=appearances.character_name
Siguiente: Conexiones de Base de Datos PHP