Intro a SQL y MySQL - Tema 2 - Consultas básicas con dos tablas

2.1 Formalizando conceptos

Hay algunas cosas que hemos pasado por alto y que no estaría mal formalizar un poco.

SQL es un lenguaje de consulta a bases de datos. Sus siglas vienen de Structured Query Language (lenguaje de consulta estructurado).

MySQL es un "gestos de bases de datos", es decir, una aplicación informática que se usa para crear y manipular bases de datos (realmente, se les exige una serie de cosas más, pero por ahora nos basta con eso).

En MySQL, las órdenes que tecleamos deben terminar en punto y coma (;). Si tecleamos una orden como "select * from personas" y pulsamos Intro, MySQL responderá mostrando "->" para indicar que todavía no hemos terminado la orden.

 

2.2 ¿Por qué varias tablas?

Puede haber varios motivos.

Por una parte, podemos tener bloques de información claramente distintos. Por ejemplo, en una base de datos que guarde la información de una empresa tendremos datos como los artículos que distribuimos y los clientes que nos los compran, que no deberían guardarse en una misma tabla.

Por otra parte, habrá ocasiones en que veamos que los datos, a pesar de que se podrían clasificar dentro de un mismo "bloque de información" (tabla), serían redundantes: existiría gran cantidad de datos repetitivos, y esto puede dar lugar a dos problemas:

 

Veamos un ejemplo:

+---------+-----------+-----------+
| nombre  | direccion | ciudad    |
+---------+-----------+-----------+
| juan    | su casa   | alicante  |
| alberto | calle uno | alicante  |
| pedro   | su calle  | alicantw  |
+---------+-----------+-----------+

Si en vez de repetir "alicante" en cada una de esas fichas, utilizásemos un código de ciudad, por ejemplo "a", gastaríamos menos espacio (en este ejemplo, 7 bytes menos en cada ficha).

Por otra parte, hemos tecleado mal uno de los datos: en la tercera ficha no hemos indicado "alicante", sino "alicantw", de modo que si hacemos consultas sobre personas de Alicante, la última de ellas no aparecería. Al teclear menos, es también más difícil cometer este tipo de errores.

A cambio, necesitaremos una segunda tabla, en la que guardemos los códigos de las ciudades, y el nombre al que corresponden (por ejemplo: si códigoDeCiudad = "a", la ciudad es "alicante").

 

2.3 Las claves primarias

Generalmente, será necesario tener algún dato que nos permita distinguir de forma clara los datos que tenemos almacenados. Por ejemplo, el nombre de una persona no es único: pueden aparecer en nuestra base de datos varios usuarios llamados "Juan López". Si son nuestros clientes, debemos saber cual es cual, para no cobrar a uno de ellos un dinero que corresponde a otro. Eso se suele solucionar guardando algún dato adicional que sí sea único para cada cliente, como puede ser el Documento Nacional de Identidad, o el Pasaporte. Si no hay ningún dato claro que nos sirva, en ocasiones añadiremos un "código de cliente", inventado por nosotros, o algo similar.

Estos datos que distinguen claramente unas "fichas" de otras los llamaremos "claves primarias".

 

2.4 Creando datos

Comenzaremos creando una nueva base de datos, de forma similar al ejemplo anterior:

CREATE DATABASE ejemplo2;
USE ejemplo2;

Después creamos la tabla de ciudades, que guardará su nombre y su código. Este código será el que actúe como "clave primaria", para distinguir otra ciudad. Por ejemplo, hay una ciudad llamado "Toledo" en España, pero también otra en Argentina, otra en Uruguay, dos en Colombia, una en Ohio (Estados Unidos)... el nombre claramente no es único, así que podríamos usar código como "te" para Toledo de España, "ta" para Toledo de Argentina y así sucesivamente.

La forma de crear la tabla con esos dos campos y con esa clave primaria sería:

CREATE TABLE ciudades (
  codigo varchar(3),
  nombre varchar(30),
  PRIMARY KEY (codigo)
);

Mientras que la tabla de personas sería casi igual al ejemplo anterior, pero añadiendo un nuevo dato: el código de la ciudad

CREATE TABLE personas (
  nombre varchar(20),
  direccion varchar(40),
  edad decimal(3),
  codciudad varchar(3)
);

Para introducir datos, el hecho de que exista una clave primaria no supone ningún cambio, salvo por el hecho de que no se nos dejaría introducir dos ciudades con el mismo código:

INSERT INTO ciudades VALUES ('a', 'alicante');
INSERT INTO ciudades VALUES ('b', 'barcelona');
INSERT INTO ciudades VALUES ('m', 'madrid');
 
INSERT INTO personas VALUES ('juan', 'su casa', 25, 'a');
INSERT INTO personas VALUES ('pedro', 'su calle', 23, 'm');
INSERT INTO personas VALUES ('alberto', 'calle uno', 22, 'b');

 

2.5 Mostrando datos

Cuando queremos mostrar datos de varias tablas a la vez, deberemos hacer unos pequeños cambios en las órdenes "select" que hemos visto:

Por eso, una consulta básica sería algo parecido (sólo parecido) a:

SELECT personas.nombre, direccion, ciudades.nombre FROM personas, ciudades;

Pero esto todavía tiene problemas: estamos combinando TODOS los datos de la tabla de personas con TODOS los datos de la tabla de ciudades, de modo que obtenemos 3x3 = 9 resultados:

+---------+-----------+-----------+
| nombre | direccion | nombre |
+---------+-----------+-----------+
| juan | su casa | alicante |
| pedro | su calle | alicante |
| alberto | calle uno | alicante |
| juan | su casa | barcelona |
| pedro | su calle | barcelona |
| alberto | calle uno | barcelona |
| juan | su casa | madrid |
| pedro | su calle | madrid |
| alberto | calle uno | madrid |
+---------+-----------+-----------+
9 rows in set (0.00 sec)

Pero esos datos no son reales: si "juan" vive en la ciudad de código "a", sólo debería mostrarse junto al nombre "alicante". Nos falta indicar esa condición: "el código de ciudad que aparece en la persona debe ser el mismo que el código que aparece en la ciudad", así:

SELECT personas.nombre, direccion, ciudades.nombre
FROM personas, ciudades 
WHERE personas.codciudad = ciudades.codigo;

Esta será la forma en que trabajaremos normalmente. Este último paso se puede evitar en ciertas circunstancias, pero ya lo veremos más adelante. El resultado de esta consulta sería:

+---------+-----------+-----------+
| nombre | direccion | nombre |
+---------+-----------+-----------+
| juan | su casa | alicante |
| alberto | calle uno | barcelona |
| pedro | su calle | madrid |
+---------+-----------+-----------+

Ese sí es el resultado correcto. Cualquier otra consulta que implique las dos tablas deberá terminar comprobando que los dos códigos coinciden. Por ejemplo, para ver qué personas viven en la ciudad llamada "madrid", haríamos:

SELECT personas.nombre, direccion, edad 
FROM personas, ciudades 
WHERE ciudades.nombre='madrid' 
AND personas.codciudad = ciudades.codigo;

+--------+-----------+------+
| nombre | direccion | edad |
+--------+-----------+------+
| pedro | su calle | 23 |
+--------+-----------+------+

Y para saber las personas de ciudades que comiencen con la letra "b", haríamos:

SELECT personas.nombre, direccion, ciudades.nombre 
FROM personas, ciudades 
WHERE ciudades.nombre LIKE 'b%' 
AND personas.codciudad = ciudades.codigo;

+---------+-----------+-----------+
| nombre | direccion | nombre |
+---------+-----------+-----------+
| alberto | calle uno | barcelona |
+---------+-----------+-----------+

Si en nuestra tabla puede haber algún dato que se repita, como la dirección, podemos pedir un listado sin duplicados, usando la palabra "distinct":

SELECT DISTINCT direccion FROM personas;

 

2.6 Ejecutando un lote de órdenes

Hasta ahora hemos tecleado todas las órdenes desde dentro del entorno de MySQL, una por una. Tenemos otra opción que también puede ser cómoda: crear un fichero de texto que contenga todas las órdenes y cargarlo después desde MySQL. Lo podemos hacer de dos formas:

source ejemplo2.sql;

mysql -u root < ejemplo2.sql;

Pero esta última alternativa tiene un problema: se darán los pasos que indiquemos en "ejemplo2.sql" y se abandonará el entorno de MySQL, sin que nos dé tiempo de comprobar si ha existido algún mensaje de error.