- Publicado el
¿Qué es SQL? Su propósito, sus orígenes y cómo utilizarlo
- Autores
¿Qué es SQL? Su propósito, sus orígenes y cómo utilizarlo
La mayoría de las aplicaciones modernas, desde blogs sencillos hasta enormes redes sociales, almacenan y recuperan datos mediante un lenguaje llamado SQL.
En este artículo, aprenderás:
- Qué es SQL y por qué se creó
- Cómo organizan la información las bases de datos relacionales
- Un ejemplo concreto con dos tablas
- Cómo ejecutar consultas SQL reales, incluidas operaciones
JOIN - Por qué SQL sigue siendo el estándar después de más de 50 años
Vamos a empezar.
¿Qué es SQL?
SQL (Structured Query Language) es un lenguaje de programación declarativo creado para gestionar bases de datos relacionales, bases de datos construidas alrededor de tablas, filas y relaciones entre ellas.
SQL permite:
- Crear tablas
- Insertar y modificar datos
- Consultar datos mediante filtros
- Unir datos relacionados de múltiples tablas
- Aplicar restricciones como correos electrónicos únicos o claves foráneas
SQL se utiliza en muchos sistemas populares de bases de datos relacionales. Estos son algunos ejemplos:
- PostgreSQL: base de datos relacional avanzada de código abierto con sólido soporte de los estándares SQL.
- MySQL: muy utilizado en aplicaciones web, conocido por su velocidad y sencillez.
- MariaDB: versión derivada de MySQL impulsada por la comunidad con funciones de rendimiento mejoradas.
- SQLite: base de datos ligera basada en archivos, utilizada en aplicaciones móviles, navegadores y sistemas integrados.
- Microsoft SQL Server: base de datos comercial de nivel empresarial con herramientas potentes en Windows y entornos de nube.
- Servicios SQL en la nube totalmente administrados como:
- Amazon RDS
- Google Cloud SQL
- PlanetScale
Estas plataformas (Amazon RDS, Google Cloud SQL, PlanetScale) se encargan de copias de seguridad, escalado, actualizaciones y mantenimiento, conservando toda la potencia de SQL.
Por qué se creó SQL
En la década de 1970, en IBM, los investigadores Edgar F. Codd y Donald Chamberlin trabajaban en una mejor forma de almacenar y recuperar datos. Por entonces, las bases de datos eran jerárquicas o basadas en redes, e interactuar con ellas era:
- Complejo
- Propenso a errores
- Estrechamente vinculado al almacenamiento físico
- Difícil de consultar con flexibilidad
Para resolverlo, introdujeron el modelo relacional, que organiza los datos en tablas con relaciones bien definidas.
SQL se creó porque:
- Los desarrolladores necesitaban un lenguaje sencillo y legible para consultar datos.
- Las empresas necesitaban reglas coherentes para almacenar relaciones.
- Era necesario acceder a los datos sin conocer los detalles internos del almacenamiento.
¿El resultado?
SQL se convirtió en estándar en 1986 (ANSI SQL) y casi todas las grandes bases de datos actuales siguen construidas en torno a él.
Comprender las tablas, columnas y relaciones
En una base de datos relacional:
- Una tabla es como una hoja de cálculo (filas + columnas)
- Una fila representa un único registro
- Una columna representa una propiedad
- Una clave primaria identifica de forma única cada fila
- Una clave foránea enlaza una tabla con otra
Veamos un ejemplo sencillo con usuarios y pedidos.
Esquema de ejemplo: Usuarios y pedidos
Tabla 1: users
| id | nombre | correo electrónico |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
Tabla 2: orders
| id | id de usuario | producto | precio |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
Relación
- Cada pedido pertenece a un usuario (
orders.user_id → users.id). - Un usuario puede tener varios pedidos (relación de uno a muchos).
Creación de las tablas (ejemplo SQL)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
product TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL
);
Esto crea dos tablas relacionales con tipado fuerte y una restricción de clave foránea. Pero ¿qué significa "tipado fuerte"? En bases de datos SQL como PostgreSQL y MySQL, cada columna tiene un tipo de datos estricto:
- Las cadenas deben ser cadenas
- Los números deben ser números
- Las fechas deben ser fechas
- Los tipos incorrectos provocan errores
Esto evita errores como:
- Guardar "veinte dólares" en una columna numérica
- Guardar 123 en un campo de correo electrónico
- Guardar fechas inválidas
SQLite es diferente: tiene tipado flexible (“tipado dinámico”), lo que significa que aplica los tipos de forma menos estricta. PostgreSQL es la base de datos con tipado más fuerte entre las principales.
Explicación de la creación de tablas línea por línea
Tabla 1: users
CREATE TABLE users (
Explicación: Esto inicia la creación de una nueva tabla llamada users.
- Funciona en: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server
- “Tabla" = conjunto estructurado de filas y columnas en la base de datos.
id SERIAL PRIMARY KEY,
Explicación:
id → el nombre de la columna.
SERIAL → entero que se incrementa automáticamente. Específico de PostgreSQL.
Equivalente en otros motores:
- MySQL:
INT AUTO_INCREMENT - SQLite:
INTEGER PRIMARY KEY AUTOINCREMENT
- MySQL:
PRIMARY KEY → garantiza la unicidad e identifica cada fila.
name TEXT NOT NULL,
Explicación:
- name → columna para almacenar texto.
- TEXT → texto de longitud variable (cadena).
- NOT NULL → no puede estar vacío.
Funciona en: PostgreSQL, MySQL, MariaDB, SQLite (pero SQL Server utiliza VARCHAR(MAX) en lugar de TEXT).
email TEXT UNIQUE NOT NULL
Explicación:
- email → almacena la dirección de correo electrónico del usuario.
- TEXT → tipo de texto.
- UNIQUE → evita correos electrónicos duplicados.
- NOT NULL → campo obligatorio.
Funciona en: PostgreSQL, MySQL, MariaDB, SQLite (teniendo en cuenta diferencias en el funcionamiento interno del tipo TEXT).
);
Explicación: Cierra la definición de la tabla.
Tabla 2: orders
CREATE TABLE orders (
Inicia una nueva tabla llamada orders. Funciona en todos los motores principales.
id SERIAL PRIMARY KEY,
Tiene el mismo significado que en la tabla users:
- Clave primaria que se incrementa automáticamente
- Tipo específico de PostgreSQL (
SERIAL) - MySQL utiliza
INT AUTO_INCREMENT - SQLite utiliza
INTEGER PRIMARY KEY AUTOINCREMENT
user_id INTEGER NOT NULL REFERENCES users(id),
Explicación:
- user_id → un número que enlaza el pedido con una fila de
users. - INTEGER → número entero.
- NOT NULL → cada pedido debe pertenecer a un usuario.
- REFERENCES users(id) → restricción de clave foránea.
Significado de clave foránea:
- Garantiza que
user_idcoincida con unidreal de la tablausers. - Si se elimina un usuario, la base de datos hará cumplir la relación según las reglas de la clave foránea.
Compatibilidad con motores:
- PostgreSQL: Totalmente compatible.
- MySQL/MariaDB: Funciona, pero requiere que el motor de almacenamiento sea InnoDB.
- SQLite: Compatible solo cuando está habilitado el soporte de claves foráneas (
PRAGMA foreign_keys = ON;).
product TEXT NOT NULL,
Almacena el nombre del producto comprado.
- Tipo TEXT → funciona de forma diferente entre motores, pero siempre almacena cadenas.
- Obligatorio (
NOT NULL).
price NUMERIC(10,2) NOT NULL
Explicación:
price → el coste del producto.
NUMERIC(10,2) → número de precisión fija con:
- hasta 10 dígitos en total
- 2 dígitos después del decimal
Esto es ideal para el dinero porque evita errores de coma flotante.
Compatibilidad con motores:
- PostgreSQL: Soporte perfecto.
- MySQL:
DECIMAL(10,2)es equivalente. - SQLite: Lo acepta, pero almacena internamente como TEXT, INT o REAL (tipado dinámico).
);
Finaliza la tabla.
Inserción de los datos de ejemplo
Ahora que nuestras tablas están creadas, insertemos los datos de ejemplo que mostramos antes. No proporcionaremos manualmente ningún valor id, la base de datos los generará automáticamente mediante el tipo SERIAL (incremento automático).
Inserción de los datos de users
INSERT INTO users (name, email)
VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Caroline', 'caroline@example.com');
Explicación de la inserción en users línea por línea
INSERT INTO users (name, email)
INSERT INTO usersEstamos añadiendo nuevas filas a la tabla users.(name, email)Solo proporcionamosnameyemail, noid.¿Por qué no proporcionar el identificador? Como la columna
idutiliza un tipo serial/de incremento automático (SERIALde PostgreSQL), la base de datos creará los identificadores automáticamente:Primer usuario →
id = 1Segundo usuario →
id = 2Tercer usuario →
id = 3
Esto es predecible en PostgreSQL, MySQL, MariaDB y SQLite (mediante sus respectivos mecanismos de incremento automático).
VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Caroline', 'caroline@example.com');
VALUESintroduce una lista de filas para insertar.- Cada grupo
( ... )corresponde a una nueva fila. - La base de datos asignará los identificadores automáticamente en el orden en que aparecen estas filas.
Tabla users resultante tras la inserción:
| id | nombre | correo electrónico |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
Aunque no escribimos ningún identificador manualmente, el mecanismo de incremento automático garantiza este orden.
Inserción de los datos de orders
Para insertar orders, sí necesitamos proporcionar user_id, porque un pedido debe pertenecer a un usuario concreto.
Aunque no establecimos manualmente los identificadores de los usuarios, sabemos cuáles son:
- Alice → ID 1
- Bob → ID 2
- Caroline → ID 3
Se generaron automáticamente en el orden de inserción, y el incremento automático siempre asigna identificadores secuencialmente salvo que se eliminen filas.
Esta es la inserción:
INSERT INTO orders (user_id, product, price)
VALUES
(1, 'Coffee Mug', 12.99),
(1, 'Hoodie', 39.99),
(2, 'Stickers', 4.99);
Explicación de la inserción en orders línea por línea
INSERT INTO orders (user_id, product, price)
Estamos insertando nuevas filas en la tabla orders.
Especificamos:
user_id→ a qué usuario pertenece el pedidoproduct→ nombreprice→ coste
Observa de nuevo: no proporcionamos id (orders.id), porque se incrementa automáticamente.
VALUES
(1, 'Coffee Mug', 12.99),
(1, 'Hoodie', 39.99),
(2, 'Stickers', 4.99);
(1, 'Coffee Mug', 12.99)- Este pedido pertenece al usuario 1 (Alice).
(1, 'Hoodie', 39.99)- Otro pedido de Alice.
(2, 'Stickers', 4.99)- Pedido del usuario 2 (Bob).
¿Cómo sabemos qué identificador tiene cada usuario? Porque:
Los usuarios se insertaron primero.
Los identificadores de incremento automático siempre asignan enteros secuenciales.
Por tanto:
- Primer usuario insertado → ID 1
- Segundo usuario insertado → ID 2
- Tercer usuario insertado → ID 3
Esto hace que referirse a los usuarios por su identificador sea completamente predecible.
Tablas finales resultantes
users
| id | nombre | correo electrónico |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
orders
| id | id de usuario | producto | precio |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
Consultar datos con SQL
Estas son consultas SQL que podemos ejecutar sobre las dos tablas que definimos antes.
1. Seleccionar todos los usuarios
SELECT * FROM users;
Resultado:
| id | nombre | correo electrónico |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
2. Seleccionar usuarios con un filtro
SELECT * FROM users
WHERE email LIKE '%alice@example.com';
Qué significa:
LIKEes un operador SQL utilizado para comparar texto mediante patrones.%es un comodín que coincide con cualquier número de caracteres, incluidos cero caracteres.
Así:
'%alice@example.com'significa: “Coincide con cualquier texto que termine enalice@example.com.”
Esto devolverá la fila en la que el correo electrónico del usuario sea exactamente:
alice@example.com
pero también coincidiría con valores como:
test-alice@example.comhello.alice@example.comanythingalice@example.com
si existieran.
Resultado:
| id | nombre | correo electrónico |
|---|---|---|
| 1 | Alice | alice@example.com |
Esta consulta devuelve solo al usuario cuyo correo electrónico coincide con el filtro.
3. Insertar un nuevo pedido
INSERT INTO orders (user_id, product, price)
VALUES (2, 'T-Shirt', 29.99);
Tabla orders resultante tras la inserción:
| id | id de usuario | producto | precio |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
| 4 | 2 | T-Shirt | 29.99 |
- La nueva fila tiene
id = 4, generado automáticamente.user_id = 2enlaza este pedido con Bob.
JOIN: Una parte potente de SQL
Los JOIN permiten consultar datos relacionados entre múltiples tablas.
Explorémoslos con nuestras tablas users y orders.
Ejemplo 1: INNER JOIN (el más habitual)
Obtén cada pedido con el usuario que lo realizó.
SELECT users.name, orders.product, orders.price
FROM orders
INNER JOIN users
ON orders.user_id = users.id;
Resultado:
| name | producto | price |
|---|---|---|
| Alice | Coffee Mug | 12.99 |
| Alice | Hoodie | 39.99 |
| Bob | Stickers | 4.99 |
| Bob | T-Shirt | 29.99 |
Bob aparece ahora dos veces, por el nuevo pedido insertado antes:
INSERT INTO orders (user_id, product, price)
VALUES (2, 'T-Shirt', 29.99);
Ejemplo 2: LEFT JOIN
Obtén todos los usuarios, incluso los que no tienen pedidos.
SELECT users.name, orders.product
FROM users
LEFT JOIN orders
ON users.id = orders.user_id;
Resultado:
| name | product |
|---|---|
| Alice | Coffee Mug |
| Alice | Hoodie |
| Bob | Stickers |
| Bob | T-Shirt |
| Caroline | null |
Caroline aparece con null porque no tiene pedidos.
Ejemplo 3: Consulta de agregación
Dinero total gastado por cada usuario:
SELECT users.name, SUM(orders.price) AS total_spent
FROM users
LEFT JOIN orders ON users.id = orders.user_id
GROUP BY users.name;
Resultado:
| name | total_spent |
|---|---|
| Alice | 52.98 |
| Bob | 34.98 |
| Caroline | null |
Explicación
- Alice: 12.99 + 39.99 = 52.98
- Bob: 4.99 + 29.99 = 34.98
- Caroline: sin pedidos → NULL
Dónde aprender más
- Documentación de PostgreSQL
- Manual de referencia de MySQL
- Tutorial de SQL de W3Schools
- Documentación de SQLite
Reflexiones finales
SQL existe desde hace más de 50 años y sigue impulsando las aplicaciones más importantes del mundo porque es:
- Declarativo
- Coherente
- Predecible
- Escalable
- Respaldado por décadas de investigación
Una vez que comprendes cómo funcionan las tablas y las uniones, SQL se convierte en una de las herramientas más potentes de tu repertorio de programación.