
Referencia rápida de los comandos SQL más usados, organizados por categoría. Todos los ejemplos usan el esquema de una tienda online: tablas usuarios, pedidos, productos y pedidos_productos.
Consultas básicas
-- Todas las columnas
SELECT * FROM productos;
-- Columnas específicas
SELECT nombre, precio FROM productos;
-- Alias de columna
SELECT nombre AS producto, precio AS coste FROM productos;
-- Alias de tabla
SELECT u.nombre, u.email FROM usuarios AS u;
-- Eliminar duplicados
SELECT DISTINCT ciudad FROM usuarios;
-- Limitar resultados
SELECT nombre, precio FROM productos LIMIT 10;
-- Paginar resultados (saltar 20, coger 10)
SELECT nombre FROM productos LIMIT 10 OFFSET 20;
Filtros con WHERE
-- Igualdad y desigualdad
SELECT * FROM usuarios WHERE nombre = 'Ana';
SELECT * FROM usuarios WHERE nombre != 'Ana';
-- Comparaciones numéricas
SELECT * FROM productos WHERE precio > 50;
SELECT * FROM productos WHERE precio >= 50 AND precio <= 200;
-- Rango (inclusivo)
SELECT * FROM productos WHERE precio BETWEEN 20 AND 100;
-- Lista de valores
SELECT * FROM usuarios WHERE ciudad IN ('Madrid', 'Barcelona', 'Valencia');
-- Patrón de texto
SELECT * FROM usuarios WHERE email LIKE '%@gmail.com'; -- termina en
SELECT * FROM usuarios WHERE nombre LIKE 'A%'; -- empieza por
SELECT * FROM usuarios WHERE nombre LIKE '_na'; -- un carácter + na
-- Valores nulos
SELECT * FROM pedidos WHERE fecha_envio IS NULL;
SELECT * FROM pedidos WHERE fecha_envio IS NOT NULL;
-- Combinación de condiciones
SELECT * FROM productos WHERE (precio < 10 OR precio > 500) AND nombre LIKE 'A%';
-- Negar condición
SELECT * FROM usuarios WHERE NOT ciudad = 'Madrid';
Ordenar y agrupar
-- Ordenar ascendente (por defecto)
SELECT nombre, precio FROM productos ORDER BY precio ASC;
-- Ordenar descendente
SELECT nombre, precio FROM productos ORDER BY precio DESC;
-- Ordenar por varias columnas
SELECT nombre, ciudad FROM usuarios ORDER BY ciudad ASC, nombre ASC;
-- Contar filas
SELECT COUNT(*) FROM pedidos;
SELECT COUNT(DISTINCT id_usuario) FROM pedidos; -- clientes únicos
-- Funciones de agregación
SELECT
COUNT(*) AS total_pedidos,
SUM(total) AS ingresos,
AVG(total) AS ticket_medio,
MIN(total) AS pedido_min,
MAX(total) AS pedido_max
FROM pedidos;
-- Agrupar por columna
SELECT id_usuario, COUNT(*) AS num_pedidos
FROM pedidos
GROUP BY id_usuario;
-- Filtrar grupos (HAVING va después de GROUP BY, WHERE no aplica aquí)
SELECT id_usuario, COUNT(*) AS num_pedidos
FROM pedidos
GROUP BY id_usuario
HAVING COUNT(*) > 2;
-- Orden habitual de cláusulas
SELECT columnas
FROM tabla
WHERE condición_filas
GROUP BY columnas
HAVING condición_grupos
ORDER BY columnas
LIMIT n;
JOINs
-- INNER JOIN: solo filas con coincidencia en ambas tablas
SELECT u.nombre, p.id_pedido, p.total
FROM usuarios AS u
INNER JOIN pedidos AS p ON u.id_usuario = p.id_usuario;
-- LEFT JOIN: todos de la izquierda + coincidencias de la derecha (NULL si no hay)
SELECT u.nombre, p.id_pedido
FROM usuarios AS u
LEFT JOIN pedidos AS p ON u.id_usuario = p.id_usuario;
-- RIGHT JOIN: todos de la derecha + coincidencias de la izquierda
SELECT u.nombre, p.id_pedido
FROM usuarios AS u
RIGHT JOIN pedidos AS p ON u.id_usuario = p.id_usuario;
-- FULL OUTER JOIN: todos de ambas tablas (no disponible en MySQL, usar UNION)
SELECT u.nombre, p.id_pedido
FROM usuarios AS u
FULL OUTER JOIN pedidos AS p ON u.id_usuario = p.id_usuario;
-- Tres tablas combinadas
SELECT u.nombre, pr.nombre AS producto
FROM usuarios AS u
INNER JOIN pedidos AS p ON u.id_usuario = p.id_usuario
INNER JOIN pedidos_productos AS pp ON p.id_pedido = pp.id_pedido
INNER JOIN productos AS pr ON pp.id_producto = pr.id_producto;
Subconsultas
-- Filtrar con el resultado de otra consulta
SELECT nombre, precio
FROM productos
WHERE id_producto IN (
SELECT id_producto FROM pedidos_productos WHERE id_pedido = 5
);
-- Subconsulta con EXISTS (más eficiente que IN para grandes volúmenes)
SELECT nombre FROM usuarios AS u
WHERE EXISTS (
SELECT 1 FROM pedidos AS p WHERE p.id_usuario = u.id_usuario
);
-- Subconsulta en SELECT (valor escalar)
SELECT
nombre,
precio,
(SELECT AVG(precio) FROM productos) AS media_general
FROM productos;
Insertar datos
-- Una fila
INSERT INTO usuarios (nombre, email, ciudad)
VALUES ('Ana García', 'ana@mail.com', 'Madrid');
-- Varias filas a la vez
INSERT INTO usuarios (nombre, email, ciudad) VALUES
('Luis Martín', 'luis@mail.com', 'Barcelona'),
('María López', 'maria@mail.com', 'Valencia'),
('Carlos Ruiz', 'carlos@mail.com', 'Sevilla');
-- Insertar desde otra tabla
INSERT INTO clientes_vip (nombre, email)
SELECT nombre, email
FROM usuarios
WHERE total_compras > 1000;
Actualizar datos
-- Actualizar una columna
UPDATE productos SET precio = 79.99 WHERE id_producto = 1;
-- Actualizar varias columnas
UPDATE usuarios
SET ciudad = 'Valencia', email = 'nuevo@mail.com'
WHERE id_usuario = 3;
-- Actualizar con cálculo
UPDATE pedidos SET total = total * 0.9 WHERE id_usuario = 2;
-- Actualizar basándose en otra tabla
UPDATE productos AS pr
SET precio = precio * 0.95
WHERE id_producto IN (
SELECT id_producto FROM pedidos_productos
GROUP BY id_producto HAVING COUNT(*) > 10
);
Eliminar datos
-- Eliminar filas concretas
DELETE FROM pedidos WHERE id_usuario = 3;
-- Eliminar con subconsulta
DELETE FROM pedidos_productos
WHERE id_pedido IN (
SELECT id_pedido FROM pedidos WHERE total = 0
);
-- Vaciar tabla conservando la estructura (más rápido que DELETE sin WHERE)
TRUNCATE TABLE log_accesos;
-- CUIDADO: DELETE sin WHERE elimina todas las filas
-- DELETE FROM tabla; ← no ejecutar sin WHERE
Estructura de tablas
-- Crear tabla
CREATE TABLE clientes (
id_cliente INT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
ciudad VARCHAR(100),
fecha_alta DATE DEFAULT CURRENT_DATE
);
-- Añadir columna
ALTER TABLE usuarios ADD COLUMN telefono VARCHAR(20);
-- Modificar tipo de columna
ALTER TABLE productos MODIFY COLUMN precio DECIMAL(10,2) NOT NULL;
-- Renombrar columna (MySQL 8+ / PostgreSQL)
ALTER TABLE usuarios RENAME COLUMN ciudad TO localidad;
-- Eliminar columna
ALTER TABLE usuarios DROP COLUMN telefono;
-- Eliminar tabla (irreversible)
DROP TABLE IF EXISTS log_accesos;
-- Ver estructura de una tabla (MySQL)
DESCRIBE usuarios;
SHOW CREATE TABLE usuarios;
Índices
-- Crear índice simple
CREATE INDEX idx_usuarios_email ON usuarios(email);
-- Índice único (impide duplicados)
CREATE UNIQUE INDEX idx_usuarios_email_unico ON usuarios(email);
-- Índice compuesto (útil cuando filtras por las dos columnas juntas)
CREATE INDEX idx_pedidos_usuario_fecha ON pedidos(id_usuario, fecha);
-- Ver índices de una tabla (MySQL)
SHOW INDEX FROM usuarios;
-- Eliminar índice
DROP INDEX idx_usuarios_email ON usuarios; -- MySQL
DROP INDEX idx_usuarios_email; -- PostgreSQL
-- Analizar si una consulta usa índices
EXPLAIN SELECT * FROM usuarios WHERE email = 'ana@mail.com';
EXPLAIN ANALYZE SELECT * FROM usuarios WHERE email = 'ana@mail.com'; -- PostgreSQL
Transacciones
-- Transacción básica
BEGIN;
UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1;
UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2;
COMMIT;
-- Deshacer si algo falla
BEGIN;
UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1;
-- algo sale mal
ROLLBACK;
-- Punto de guardado parcial
BEGIN;
UPDATE pedidos SET estado = 'enviado' WHERE id = 101;
SAVEPOINT despues_pedido;
UPDATE stock SET cantidad = cantidad - 1 WHERE id_producto = 5;
-- si el UPDATE de stock falla, volvemos al savepoint
ROLLBACK TO SAVEPOINT despues_pedido;
COMMIT;
-- Nivel de aislamiento (antes del BEGIN)
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- operaciones
COMMIT;
Funciones útiles
-- Texto
SELECT UPPER(nombre) FROM usuarios; -- MAYÚSCULAS
SELECT LOWER(nombre) FROM usuarios; -- minúsculas
SELECT LENGTH(nombre) FROM usuarios; -- longitud en caracteres
SELECT TRIM(nombre) FROM usuarios; -- elimina espacios al inicio/fin
SELECT CONCAT(nombre, ' — ', email) FROM usuarios;
SELECT SUBSTRING(email, 1, 5) FROM usuarios; -- primeros 5 caracteres
-- Números
SELECT ROUND(precio, 2) FROM productos; -- redondear
SELECT CEIL(precio) FROM productos; -- redondear arriba
SELECT FLOOR(precio) FROM productos; -- redondear abajo
SELECT ABS(-50); -- valor absoluto
-- Fechas
SELECT NOW(); -- fecha y hora actual
SELECT CURDATE(); -- solo fecha actual (MySQL)
SELECT CURRENT_DATE; -- solo fecha actual (PostgreSQL)
SELECT YEAR(fecha), MONTH(fecha) FROM pedidos;
SELECT DATEDIFF(NOW(), fecha) AS dias_desde_pedido FROM pedidos;
-- Control de nulos
SELECT COALESCE(telefono, 'sin teléfono') FROM usuarios; -- primer valor no nulo
SELECT IFNULL(descuento, 0) FROM productos; -- MySQL
SELECT NULLIF(cantidad, 0) FROM productos; -- devuelve NULL si son iguales
-- Condicional en columna
SELECT
nombre,
CASE
WHEN precio < 20 THEN 'económico'
WHEN precio < 100 THEN 'medio'
ELSE 'premium'
END AS rango_precio
FROM productos;