SQL es el lenguaje con el que se consultan y modifican las bases de datos relacionales. Su nombre formal como estándar es ISO/IEC 9075, y la revisión vigente es SQL:2023. Es declarativo: describís qué querés obtener, no cómo buscarlo. Con seis instrucciones —SELECT, WHERE, JOIN, GROUP BY, ORDER BY y las de escritura INSERT, UPDATE y DELETE— se cubre casi todo el trabajo real. La clave para dejar de pelearse con los errores es entender que una consulta no se ejecuta en el orden en que se escribe.
Ver índice de contenidos
Qué es SQL y qué tan estándar es
SQL —Structured Query Language— es el lenguaje para consultar y modificar bases de datos relacionales. Su característica más importante es que es declarativo: describís el resultado que querés, no los pasos para conseguirlo. Le pedís «los clientes de Córdoba ordenados por apellido» y el motor decide cómo buscarlos.
Sobre cuán estándar es, la documentación de PostgreSQL da la respuesta precisa: «el nombre formal del estándar SQL es ISO/IEC 9075 Database Language SQL», y la actualización más reciente apareció en 2023, conocida como SQL:2023. Las versiones anteriores fueron SQL:2016, SQL:2011, SQL:2008, SQL:2006, SQL:2003, SQL:1999 y SQL-92, y cada una reemplaza a la anterior.
Pero hay un matiz honesto: ningún motor implementa el estándar completo, y cada uno agrega cosas propias. Eso da lugar a los llamados dialectos. En la práctica, lo que vas a aprender acá funciona igual en MySQL, PostgreSQL, SQL Server, Oracle y SQLite. Lo que cambia son las funciones de fecha y de texto, la forma de limitar resultados y algunos tipos de dato.
Usan las tablas clientes, pedidos y productos que aparecen en la guía del modelo relacional. Si no la leíste, alcanza con saber que pedidos tiene una columna cliente_id que apunta al id de clientes.
Tu primera consulta
Toda consulta de lectura tiene la misma forma mínima: qué columnas querés y de dónde.
-- Todas las columnas de todas las filas
SELECT * FROM clientes;
-- Mejor: solo las columnas que necesitás
SELECT nombre, correo FROM clientes;
-- Con un nombre distinto en el resultado (alias)
SELECT nombre AS cliente, correo AS email FROM clientes;
El SELECT * es cómodo para explorar y una mala costumbre para el código que queda: trae columnas que no necesitás, y el día que alguien agrega una columna pesada tu consulta se vuelve lenta sin que nadie la haya tocado.
Dos convenciones que hacen el código legible: las palabras del lenguaje en mayúsculas y los nombres de tus tablas y columnas en minúsculas, y cada consulta termina en punto y coma.
Filtrar con WHERE
WHERE decide qué filas entran en el resultado. Es donde vas a pasar la mayor parte del tiempo.
SELECT producto, precio FROM pedidos
WHERE precio > 20000;
-- Combinar condiciones. Los paréntesis evitan sorpresas.
SELECT * FROM pedidos
WHERE (precio > 20000 OR cliente_id = 1)
AND fecha >= '2026-01-01';
-- Operadores que conviene conocer de entrada
WHERE precio BETWEEN 1000 AND 5000 -- rango, incluye los extremos
WHERE cliente_id IN (1, 4, 7) -- alguno de esos valores
WHERE producto LIKE 'Teclado%' -- empieza con «Teclado»
WHERE telefono IS NULL -- sin valor cargado
WHERE NOT producto = 'Mouse' -- negación
El primero: = NULL nunca se cumple, porque NULL no es igual a nada, ni a sí mismo. Va IS NULL. El segundo: mezclar AND y OR sin paréntesis, porque AND tiene prioridad y la consulta hace algo distinto de lo que leés. Poné los paréntesis siempre, aunque parezcan innecesarios.
Para ordenar y recortar el resultado se agregan al final:
SELECT producto, precio FROM pedidos
ORDER BY precio DESC, producto ASC
LIMIT 10;
Sobre LIMIT: es la instrucción con más variación entre motores. MySQL, PostgreSQL y SQLite usan LIMIT; SQL Server usa TOP; Oracle usa FETCH FIRST. Es el ejemplo más claro de lo que significa «dialecto».
El orden real de ejecución
Esta sección es la que más frustración ahorra, y casi nunca aparece en los tutoriales de introducción. Una consulta SQL no se ejecuta en el orden en que se escribe.
SELECT se ejecuta quinto, no primero. De ese solo hecho salen los dos errores que más confunden al empezar.La consecuencia más visible es esta: si creás un alias en el SELECT, no podés usarlo en el WHERE, porque cuando el WHERE corre ese nombre todavía no existe.
-- ✕ Falla: «total» no existe todavía cuando corre el WHERE
SELECT precio * cantidad AS total FROM pedido_producto
WHERE total > 50000;
-- ✓ Funciona: se repite la expresión completa
SELECT precio * cantidad AS total FROM pedido_producto
WHERE precio * cantidad > 50000;
-- ✓ También funciona: ORDER BY sí puede usar el alias, porque corre después
SELECT precio * cantidad AS total FROM pedido_producto
ORDER BY total DESC;
Ese último ejemplo es la prueba de que el orden importa: el mismo alias falla en el WHERE y funciona en el ORDER BY, porque uno corre antes del SELECT y el otro después.
Unir tablas con JOIN
Como los datos están repartidos en varias tablas, hace falta una forma de verlos juntos. Eso es JOIN: se indica con qué tabla unir y por qué columnas se corresponden.
SELECT c.nombre, p.producto, p.precio
FROM pedidos p
JOIN clientes c ON p.cliente_id = c.id
WHERE p.precio > 20000;
Las letras p y c son alias de tabla: ahorran escritura y, más importante, dejan claro de qué tabla viene cada columna cuando ambas tienen una columna con el mismo nombre.
Hay dos tipos que cubren casi todo, y elegir mal entre ellos es una causa muy frecuente de informes con datos faltantes:
| Tipo | Qué devuelve | Cuándo usarlo |
|---|---|---|
INNER JOIN(o solo JOIN) | Solo las filas con coincidencia en las dos tablas | Cuando te interesan únicamente los casos completos: pedidos que sí tienen cliente. |
LEFT JOIN | Todas las de la izquierda; las de la derecha con NULL si no hay coincidencia | Cuando querés incluir los casos sin pareja: todos los clientes, incluso los que nunca compraron. |
Si tu informe «tiene menos filas de las que debería», casi siempre es un INNER JOIN donde correspondía un LEFT JOIN. El INNER descarta silenciosamente todo lo que no tiene pareja: los clientes sin pedidos, los productos sin ventas, las categorías vacías. No da error, simplemente no aparecen.
Agrupar, contar y sumar
Las funciones de agregado calculan un valor a partir de muchas filas: COUNT cuenta, SUM suma, AVG promedia, MIN y MAX buscan extremos.
-- Sobre toda la tabla: una sola fila de resultado
SELECT COUNT(*) AS cantidad, SUM(precio) AS facturado
FROM pedidos;
-- Por grupo: una fila por cliente
SELECT c.nombre,
COUNT(p.id) AS pedidos,
SUM(p.precio) AS total
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id, c.nombre
HAVING COUNT(p.id) >= 3 -- filtra grupos, no filas
ORDER BY total DESC;
Tres cosas de esa consulta que vale entender bien:
GROUP BYdefine la unidad del resultado. Agrupando por cliente, cada fila del resultado es un cliente. Toda columna delSELECTque no esté dentro de una función de agregado tiene que estar en elGROUP BY.HAVINGfiltra grupos;WHEREfiltra filas. Para quedarte con los clientes que tienen tres pedidos o más hace falta que los grupos ya existan, y elWHEREcorre antes de que existan.COUNT(*)yCOUNT(columna)no son lo mismo. El primero cuenta filas; el segundo cuenta solo las que tienen valor no nulo en esa columna. En el ejemplo,COUNT(p.id)con unLEFT JOINdevuelve 0 para un cliente sin pedidos, que es lo correcto.COUNT(*)devolvería 1, contando la fila con nulos que genera elLEFT JOIN.
Insertar, actualizar y borrar
Tres instrucciones, y una precaución que vale más que las tres.
-- Insertar. Nombrá siempre las columnas: si mañana se agrega una,
-- la instrucción sigue funcionando.
INSERT INTO clientes (nombre, correo)
VALUES ('Ana López', '[email protected]');
-- Actualizar. El WHERE no es opcional en la práctica.
UPDATE clientes
SET correo = '[email protected]'
WHERE id = 1;
-- Borrar.
DELETE FROM pedidos
WHERE id = 42;
Si te olvidás el WHERE, la instrucción se aplica a todas las filas. Un DELETE FROM clientes sin WHERE borra la tabla entera y no hay deshacer. La práctica que conviene volver reflejo: escribí primero la consulta como SELECT con el mismo WHERE, mirá qué filas devuelve, y solo entonces cambiá SELECT por UPDATE o DELETE. En una base con datos reales, además, hacelo dentro de una transacción.
-- 1. Verificar qué se va a afectar
SELECT * FROM pedidos WHERE fecha < '2020-01-01';
-- 2. Recién entonces, y dentro de una transacción
START TRANSACTION;
DELETE FROM pedidos WHERE fecha < '2020-01-01';
-- Revisás el resultado y decidís:
COMMIT; -- confirmar
-- ROLLBACK; -- o deshacer todo
Dónde practicar con datos reales
Leer SQL no alcanza: hay que escribirlo sobre datos que no controlás. Y practicar con tres filas que cargaste vos es engañoso, porque los datos reales nunca son tan prolijos.
La solución son las bases de muestra. La más conocida del mundo MySQL es Sakila, que según su documentación oficial fue desarrollada inicialmente por Mike Hillyer, del equipo de documentación de MySQL AB, y está pensada «para proveer un esquema estándar que se pueda usar en ejemplos de libros, tutoriales, artículos y muestras». Modela un videoclub: películas, actores, clientes, alquileres y pagos, con la cantidad de tablas suficiente para que los JOIN tengan sentido.
En el foro de Underc0de, DtxdF la recomendó en 2021 con un argumento que sigue valiendo: es muy útil cuando estás empezando o cuando necesitás probar características del motor sobre una estructura estándar.
Instalá MySQL siguiendo la guía de esta serie, cargá Sakila, y proponete responder diez preguntas de negocio por día: cuáles son las diez películas más alquiladas, qué categoría factura más, qué clientes no alquilaron nunca. Ese último tipo de pregunta —los que no hicieron algo— es el que te obliga a dominar el LEFT JOIN.
Errores frecuentes
UPDATEoDELETEsinWHERE. El más caro de todos. Verificá con unSELECTantes, siempre.- Usar
= NULL. Nunca se cumple. VaIS NULL. - Mezclar
ANDyORsin paréntesis.ANDtiene prioridad y la consulta hace otra cosa. INNER JOINdonde correspondíaLEFT JOIN. Esconde en silencio las filas sin pareja.- Usar un alias del
SELECTen elWHERE. Todavía no existe: elSELECTcorre después. - Filtrar por un agregado en el
WHERE. Va enHAVING, cuando los grupos ya existen. - Confundir
COUNT(*)conCOUNT(columna). Con unLEFT JOINdan resultados distintos, y uno de los dos está mal. - Dejar
SELECT *en el código que queda. Trae columnas que no usás y se vuelve lento cuando alguien agrega una. - Suponer un orden sin
ORDER BY. Funciona hasta que deja de funcionar, sin aviso.
Preguntas frecuentes
¿SQL es igual en todos los motores?
El núcleo sí, los detalles no. SQL es un estándar internacional cuyo nombre formal es ISO/IEC 9075 «Database Language SQL», y la revisión más reciente es de 2023, conocida como SQL:2023. Pero la documentación de PostgreSQL aclara que ningún motor implementa el estándar completo, así que cada uno tiene su dialecto. En la práctica, todo lo que se explica en esta guía funciona igual en MySQL, PostgreSQL, SQL Server, Oracle y SQLite. Lo que cambia son las funciones de fecha y texto, la forma de limitar resultados y algunos tipos de dato.
¿Por qué no puedo usar un alias en el WHERE?
Porque el WHERE se ejecuta antes que el SELECT. Una consulta SQL no se ejecuta en el orden en que se escribe: primero se resuelve FROM y JOIN, después WHERE, después GROUP BY y HAVING, y recién en quinto lugar el SELECT. Cuando el motor evalúa el WHERE, el nombre que creaste en el SELECT todavía no existe. La solución es repetir la expresión completa en el WHERE, o envolver la consulta en otra que sí pueda referirse al alias.
¿Cuál es la diferencia entre WHERE y HAVING?
WHERE filtra filas individuales y HAVING filtra grupos ya formados. La diferencia sale del orden de ejecución: WHERE corre antes del GROUP BY, cuando los grupos todavía no existen, así que no puede usar el resultado de un COUNT o un SUM. HAVING corre después, cuando los grupos ya están armados. La regla práctica: si tu condición usa una función de agregado, va en HAVING; si no, va en WHERE, que además es más eficiente porque descarta filas antes de agrupar.
¿Cuál es la diferencia entre INNER JOIN y LEFT JOIN?
INNER JOIN devuelve solo las filas que tienen coincidencia en las dos tablas. LEFT JOIN devuelve todas las filas de la tabla de la izquierda, y para las que no tienen coincidencia completa las columnas de la derecha con NULL. La consecuencia práctica es importante: si querés listar todos los clientes con la cantidad de pedidos que hizo cada uno, un INNER JOIN te va a esconder los clientes sin pedidos, mientras el LEFT JOIN los muestra con cero. Elegir mal el tipo de JOIN es una de las causas más comunes de informes con datos faltantes.
¿Es peligroso ejecutar un UPDATE o un DELETE?
Puede ser, y por un motivo muy concreto: si te olvidás el WHERE, la instrucción se aplica a todas las filas de la tabla. Un DELETE FROM clientes sin WHERE borra todos los clientes, y no hay deshacer. La costumbre que evita el accidente es escribir siempre primero la consulta como un SELECT con el mismo WHERE, verificar que devuelva exactamente las filas que querés afectar, y solo entonces cambiar SELECT por UPDATE o DELETE. En una base de producción, además, conviene hacerlo dentro de una transacción.
¿Dónde puedo practicar SQL con datos reales?
Con una base de muestra. La más conocida en el mundo MySQL es Sakila, que Oracle publica como esquema estándar para ejemplos en libros, tutoriales y artículos, y que además incluye vistas, procedimientos almacenados y disparadores. En el foro de Underc0de, DtxdF la recomendó en 2021 justamente para quien está empezando. Practicar sobre una base ya poblada es mucho mejor que inventar tres filas: te obliga a enfrentarte a datos que no son perfectos.
Fuentes
Documentación oficial y material de la comunidad consultados para esta guía. Fecha de consulta: 27 de julio de 2026.
- PostgreSQL. SQL Conformance. Nombre formal del estándar ISO/IEC 9075, la revisión SQL:2023, el listado de versiones anteriores y la aclaración de que ningún motor lo implementa por completo.
- PostgreSQL. The SQL Language. Introducción oficial al lenguaje.
- MySQL. Sakila Sample Database. Origen de la base de muestra, su propósito y las características del motor que ilustra.
- Underc0de, foro. «MySQL: Base de datos de muestra para practicar», por DtxdF, 26 de mayo de 2021, sección Base de Datos. Recomendación de Sakila para quien está empezando.
- Underc0de, foro. «Manual SQL - Con motor MySQL», por kid_goth, 8 de junio de 2012, sección Base de Datos. Primer manual de SQL aportado a la comunidad; material histórico anterior a las versiones actuales del motor.