Programación · Lenguajes de programación
Tutorial de SQL para principiantes
Respuesta corta
SQL es el lenguaje estándar para crear, consultar y modificar datos en bases de datos relacionales como PostgreSQL, MySQL y SQLite. Escribes instrucciones declarativas como SELECT, INSERT, UPDATE y DELETE, y la base de datos decide cómo ejecutarlas. Esta página enseña el diseño de tablas, el filtrado, los joins, la agrupación, las subconsultas, las funciones de ventana y las transacciones, y enumera las principales diferencias entre PostgreSQL, MySQL y SQLite.
Qué es SQL y para qué se usa
SQL (Structured Query Language, lenguaje de consulta estructurado) es el lenguaje estándar para trabajar con bases de datos relacionales, donde los datos se guardan en tablas de filas y columnas. Tú describes qué datos quieres y el motor de la base de datos decide cómo encontrarlos. SQL está normalizado por ISO/IEC 9075, y todos los gestores importantes implementan el núcleo del estándar más sus propias extensiones.
Desarrolladores, analistas de datos, ingenieros de datos y administradores de bases de datos usan SQL a diario: para guardar los datos de una aplicación, responder preguntas de negocio, crear informes y alimentar pipelines de datos. Los motores de código abierto más comunes son PostgreSQL, MySQL (y su fork MariaDB) y SQLite; entre los comerciales están Microsoft SQL Server y Oracle Database.
Obtén una base de datos y ejecuta tu primera consulta
La forma más fácil de empezar es SQLite, una base de datos sin servidor que se guarda en un solo archivo. Viene preinstalada en macOS y en la mayoría de los sistemas Linux. PostgreSQL y MySQL funcionan como servidores e incluyen los clientes de línea de comandos psql y mysql.
sqlite3 shop.db # SQLite: opens or creates a file
psql -U postgres -d shop # PostgreSQL client
mysql -u root -p shop # MySQL clientSELECT 'Hello, world!' AS greeting;
-- greeting
-- Hello, world!Las palabras clave de SQL no distinguen mayúsculas de minúsculas. Escribir las palabras clave en mayúsculas y los nombres en minúsculas es una convención habitual que hace las consultas más fáciles de leer. Una instrucción termina con punto y coma, y -- inicia un comentario.
Crear tablas: tipos de datos y restricciones
La definición de una tabla enumera las columnas, sus tipos y las restricciones que mantienen los datos válidos. Esta página usa dos tablas, customers y orders, en todos los ejemplos.
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
city VARCHAR(50)
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id),
amount DECIMAL(10, 2) NOT NULL,
ordered_on DATE NOT NULL
);
INSERT INTO customers (id, name, city) VALUES
(1, 'Ann', 'Oslo'), (2, 'Bo', 'Rome'), (3, 'Cy', 'Oslo');
INSERT INTO orders (id, customer_id, amount, ordered_on) VALUES
(1, 1, 30.00, '2026-01-05'),
(2, 1, 45.50, '2026-02-10'),
(3, 2, 12.00, '2026-02-11');| Tipo | Almacena | Notas |
|---|---|---|
INTEGER, BIGINT | números enteros | usa BIGINT para IDs grandes |
DECIMAL(p, s) | decimales exactos | úsalo para dinero, nunca FLOAT |
VARCHAR(n), TEXT | cadenas de texto | TEXT no está en el estándar, pero tiene un amplio soporte |
DATE, TIMESTAMP | fechas y horas | escribe los literales como '2026-01-05' |
BOOLEAN | verdadero o falso | MySQL lo trata como TINYINT(1) |
Las restricciones incluyen PRIMARY KEY (identificador único de fila), NOT NULL, UNIQUE, CHECK (condition) y FOREIGN KEY / REFERENCES (el valor debe existir en otra tabla). Las cadenas usan comillas simples; las comillas dobles son para identificadores en SQL estándar.
SELECT: filtrar, ordenar y limitar
SELECT lee datos. WHERE filtra filas, ORDER BY las ordena y LIMIT (o el estándar FETCH FIRST n ROWS ONLY) restringe cuántas se devuelven.
SELECT name, city
FROM customers
WHERE city = 'Oslo'
ORDER BY name;
-- Ann | Oslo
-- Cy | Oslo
SELECT id, amount,
CASE WHEN amount >= 40 THEN 'large'
WHEN amount >= 20 THEN 'medium'
ELSE 'small' END AS size
FROM orders
WHERE amount BETWEEN 10 AND 50
ORDER BY amount DESC
LIMIT 2;
-- 2 | 45.50 | large
-- 1 | 30.00 | medium
SELECT name FROM customers WHERE city IS NULL; -- not: city = NULL
SELECT name FROM customers WHERE name LIKE 'A%'; -- AnnJOIN: combinar tablas
Un join une las filas de dos tablas mediante una condición, normalmente una clave foránea. INNER JOIN (o simplemente JOIN) conserva solo las filas que coinciden; LEFT JOIN conserva todas las filas de la tabla izquierda y rellena con NULL las columnas que faltan.
SELECT c.name, o.amount
FROM customers AS c
JOIN orders AS o ON o.customer_id = c.id
ORDER BY o.amount DESC;
-- Ann | 45.50
-- Ann | 30.00
-- Bo | 12.00
SELECT c.name, o.id AS order_id
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
WHERE o.id IS NULL; -- customers with no orders
-- Cy | NULL| Join | Devuelve |
|---|---|
INNER JOIN | filas que coinciden en ambas tablas |
LEFT JOIN | todas las filas de la izquierda, más las coincidencias de la derecha o NULL |
RIGHT JOIN | todas las filas de la derecha, más las coincidencias de la izquierda o NULL |
FULL OUTER JOIN | todas las filas de ambos lados; MySQL no lo admite |
CROSS JOIN | todas las combinaciones de filas (un producto cartesiano) |
GROUP BY y funciones de agregación
Las funciones de agregación como COUNT, SUM, AVG, MIN y MAX resumen muchas filas en una. GROUP BY genera una fila de resultado por grupo, y HAVING filtra los grupos después de la agregación, mientras que WHERE filtra las filas antes de ella.
SELECT c.name,
COUNT(o.id) AS orders,
COALESCE(SUM(o.amount), 0) AS total
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
GROUP BY c.id, c.name
ORDER BY total DESC;
-- Ann | 2 | 75.50
-- Bo | 1 | 12.00
-- Cy | 0 | 0
SELECT city, COUNT(*) AS n
FROM customers
GROUP BY city
HAVING COUNT(*) > 1;
-- Oslo | 2COUNT(*) cuenta filas, mientras que COUNT(column) cuenta solo los valores no nulos, por eso Cy obtiene 0 pedidos en el ejemplo anterior. COALESCE devuelve su primer argumento no nulo.
Subconsultas, CTE y funciones de ventana
Una subconsulta es una consulta dentro de otra consulta. Una expresión de tabla común (CTE), escrita con WITH, da nombre a una subconsulta y hace legibles las consultas largas. Las funciones de ventana como ROW_NUMBER, RANK y SUM(...) OVER calculan valores sobre filas relacionadas sin colapsarlas en grupos.
SELECT name FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE amount > 20);
-- Ann
WITH totals AS (
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
)
SELECT c.name, t.total,
RANK() OVER (ORDER BY t.total DESC) AS rnk
FROM totals AS t
JOIN customers AS c ON c.id = t.customer_id;
-- Ann | 75.50 | 1
-- Bo | 12.00 | 2Las CTE y las funciones de ventana están disponibles en PostgreSQL, en SQLite 3.25 y posteriores, y en MySQL 8.0 y posteriores.
Modificar datos: INSERT, UPDATE, DELETE y transacciones
UPDATE y DELETE afectan a todas las filas que cumplen la cláusula WHERE. Una transacción agrupa varias instrucciones para que todas se completen o todas fallen juntas.
UPDATE customers SET city = 'Paris' WHERE id = 2;
DELETE FROM orders WHERE amount < 15;
BEGIN;
UPDATE orders SET amount = amount - 10 WHERE id = 1;
UPDATE orders SET amount = amount + 10 WHERE id = 2;
COMMIT; -- or ROLLBACK; to undo both
SELECT id, amount FROM orders;
-- 1 | 20.00
-- 2 | 55.50Diferencias entre PostgreSQL, MySQL y SQLite
El núcleo de SQL es portable, pero cada motor difiere en los detalles. Estas son las diferencias que los principiantes encuentran primero.
| Característica | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| ID autoincremental | GENERATED ALWAYS AS IDENTITY o SERIAL | AUTO_INCREMENT | INTEGER PRIMARY KEY |
| Entrecomillado de nombres | "name" | acentos graves alrededor del nombre | "name" |
| Limitar filas | LIMIT n o FETCH FIRST n ROWS ONLY | LIMIT n | LIMIT n |
| Concatenación de cadenas | a || b | CONCAT(a, b) | a || b |
| Booleanos | tipo BOOLEAN real | TINYINT(1) | se guardan como 0 y 1 |
| Tipado | estricto | estricto en el modo estricto por defecto | flexible, salvo que la tabla se declare STRICT |
| Upsert | ON CONFLICT ... DO UPDATE | ON DUPLICATE KEY UPDATE | ON CONFLICT ... DO UPDATE |
Otras diferencias incluyen las funciones de fecha (NOW() en PostgreSQL y MySQL, datetime('now') en SQLite), la distinción de mayúsculas en las comparaciones de cadenas y la compatibilidad con el full outer join, del que carece MySQL.
Errores comunes y siguientes pasos
- Construir consultas concatenando la entrada del usuario, lo que permite la inyección SQL. Usa siempre consultas parametrizadas (marcadores como
?o$1) desde tu lenguaje de programación. - Seleccionar en una consulta agrupada una columna que no está en
GROUP BYni dentro de una función de agregación. - Usar
FLOATpara dinero en lugar deDECIMAL. - Olvidar los índices en las columnas que se usan en joins y filtros;
CREATE INDEX idx_orders_customer ON orders(customer_id);yEXPLAINayudan en esto. - Suponer que las filas se devuelven en un orden fijo sin
ORDER BY.
Para seguir estudiando, lee los manuales oficiales: el tutorial de PostgreSQL, el MySQL Reference Manual y la referencia del lenguaje SQL de SQLite. Practica con un conjunto de datos real escribiendo preguntas en lenguaje natural y convirtiendo cada una en una consulta.
Preguntas frecuentes
¿Es SQL un lenguaje de programación?
SQL es un lenguaje de consulta declarativo: describes el resultado que quieres en lugar de los pasos para calcularlo. Las extensiones de cada fabricante, como PL/pgSQL y T-SQL, añaden variables, bucles y procedimientos.
¿Con qué base de datos debería empezar un principiante?
SQLite no necesita servidor y es ideal para aprender. PostgreSQL es una gran opción para aprender SQL conforme al estándar en aplicaciones reales, y MySQL es habitual en el alojamiento web.
¿Cuál es la diferencia entre WHERE y HAVING?
WHERE filtra filas individuales antes de agrupar, y HAVING filtra grupos después de calcular las funciones de agregación.
¿Cuál es la diferencia entre INNER JOIN y LEFT JOIN?
INNER JOIN devuelve solo las filas que tienen coincidencia en ambas tablas. LEFT JOIN devuelve todas las filas de la tabla izquierda y rellena con NULL las columnas de la tabla derecha cuando no hay coincidencia.
¿Cómo evito la inyección SQL?
Nunca insertes la entrada del usuario en el texto SQL. Usa consultas parametrizadas o sentencias preparadas, en las que la base de datos recibe la consulta y los valores por separado.