WHERE en SQL
En esta lección
WHERE filtra las filas: solo aparecen las que cumplen la condición. Es lo que convierte “dame todos los clientes” en “dame los clientes de Santander mayores de edad”. Lo usarás en casi todas las consultas, y también en UPDATE y DELETE para decidir qué filas se cambian o se borran. En esta lección verás los operadores de comparación, cómo combinar condiciones, los atajos IN y BETWEEN, cómo filtrar fechas y cómo buscar texto con LIKE.
SELECT nombre, edad
FROM clientes
WHERE ciudad = 'Santander';
| nombre | edad |
|---|---|
| Ana García | 34 |
| Marta Ruiz | 45 |
La base de datos recorre las filas de clientes y, para cada una, evalúa la condición. Si es verdadera, la fila pasa al resultado; si no, se descarta.
Comparaciones
| Operador | Significado | Ejemplo |
|---|---|---|
= | Igual (un solo =, no ==) | edad = 22 |
<> o != | Distinto | ciudad <> 'Madrid' |
> / < | Mayor / menor | edad > 30 |
>= / <= | Mayor o igual / menor o igual | importe >= 45 |
SELECT id, fecha, importe
FROM pedidos
WHERE importe >= 45;
| id | fecha | importe |
|---|---|---|
| 1 | 2026-09-01 | 45.00 |
| 2 | 2026-09-03 | 120.50 |
| 4 | 2026-09-12 | 60.00 |
Los textos y las fechas van entre comillas simples; los números, sin comillas. <> es el operador estándar para “distinto”; != funciona en todos los sistemas habituales, pero <> es el que verás en la documentación.
Varias condiciones: AND, OR, NOT
AND: se tienen que cumplir las dos condiciones.OR: basta con que se cumpla una.NOT: invierte la condición.
-- Clientes de Madrid mayores de edad
SELECT nombre FROM clientes
WHERE ciudad = 'Madrid' AND edad >= 18;
| nombre |
|---|
| Eva Martín |
-- Clientes de Bilbao o de Madrid
SELECT nombre FROM clientes
WHERE ciudad = 'Bilbao' OR ciudad = 'Madrid';
| nombre |
|---|
| Luis Pérez |
| Pablo Díaz |
| Eva Martín |
-- Clientes que no son de Madrid
SELECT nombre FROM clientes
WHERE NOT ciudad = 'Madrid';
| nombre |
|---|
| Ana García |
| Luis Pérez |
| Marta Ruiz |
Precedencia: AND va antes que OR
Igual que en matemáticas la multiplicación va antes que la suma, en SQL AND se evalúa antes que OR. Si los mezclas sin paréntesis, el resultado puede no ser el que esperas.
Queremos los clientes de Bilbao o Madrid que tengan 25 años o más:
-- ✗ Mal: sin paréntesis
SELECT nombre, ciudad, edad FROM clientes
WHERE ciudad = 'Bilbao' OR ciudad = 'Madrid' AND edad >= 25;
| nombre | ciudad | edad |
|---|---|---|
| Luis Pérez | Bilbao | 22 |
| Eva Martín | Madrid | 29 |
¡Luis tiene 22 años y aparece! La base de datos lo ha leído así: ciudad = 'Bilbao' OR (ciudad = 'Madrid' AND edad >= 25). Es decir, cualquier cliente de Bilbao, o los de Madrid de 25 o más. Con paréntesis dices lo que de verdad quieres:
-- ✓ Bien
SELECT nombre, ciudad, edad FROM clientes
WHERE (ciudad = 'Bilbao' OR ciudad = 'Madrid') AND edad >= 25;
| nombre | ciudad | edad |
|---|---|---|
| Eva Martín | Madrid | 29 |
Consejo: siempre que mezcles
ANDyOR, pon paréntesis, aunque sepas la regla de precedencia. Quien lea la consulta (incluido tú dentro de un mes) no tendrá que pensarlo.
IN y NOT IN: el valor está en una lista
IN es un atajo para varios OR sobre la misma columna:
WHERE ciudad IN ('Bilbao', 'Madrid') -- igual que los dos OR de antes
Y NOT IN busca lo contrario, los valores que no están en la lista:
SELECT nombre, ciudad FROM clientes
WHERE ciudad NOT IN ('Bilbao', 'Madrid');
| nombre | ciudad |
|---|---|
| Ana García | Santander |
| Marta Ruiz | Santander |
La lista también puede salir de otra consulta, como verás en subconsultas.
Cuidado: si la lista de un
NOT INcontiene unNULL, la consulta no devuelve ninguna fila. Lo explicamos en valores NULL.
BETWEEN y NOT BETWEEN: rangos
BETWEEN a AND b comprueba si el valor está entre a y b, incluidos los dos extremos. Es lo mismo que >= a AND <= b:
SELECT id, importe FROM pedidos
WHERE importe BETWEEN 30 AND 60;
| id | importe |
|---|---|
| 1 | 45.00 |
| 4 | 60.00 |
| 5 | 33.25 |
El pedido 4, de 60.00 exactos, entra porque el extremo está incluido. NOT BETWEEN devuelve lo que queda fuera del rango:
SELECT id, importe FROM pedidos
WHERE importe NOT BETWEEN 30 AND 60;
| id | importe |
|---|---|
| 2 | 120.50 |
| 3 | 12.99 |
El valor pequeño va siempre primero: BETWEEN 60 AND 30 no devuelve nada.
Cuidado: con textos,
BETWEENcompara alfabéticamente.nombre BETWEEN 'A' AND 'M'no incluye a Marta Ruiz, porque'Marta Ruiz'va después de'M'en el orden alfabético (igual que “Ma” va después de “M” en un diccionario). Para “nombres que empiezan de la A a la M”, usanombre < 'N'.
Filtrar fechas
Las fechas se escriben como texto entre comillas simples con el formato 'AAAA-MM-DD' (año, mes, día). Es el formato estándar y el único que todas las bases de datos entienden igual. Con él puedes usar los operadores de siempre:
-- Pedidos desde el 10 de septiembre
SELECT id, fecha FROM pedidos
WHERE fecha >= '2026-09-10';
| id | fecha |
|---|---|
| 3 | 2026-09-10 |
| 4 | 2026-09-12 |
| 5 | 2026-09-15 |
-- Pedidos de la primera decena de septiembre
SELECT id, fecha, importe FROM pedidos
WHERE fecha BETWEEN '2026-09-01' AND '2026-09-10';
| id | fecha | importe |
|---|---|---|
| 1 | 2026-09-01 | 45.00 |
| 2 | 2026-09-03 | 120.50 |
| 3 | 2026-09-10 | 12.99 |
Cuidado: si la columna es de tipo
DATETIME(fecha y hora),'2026-09-10'significa “10 de septiembre a las 00:00:00”. Un pedido del día 10 a las 18:30 quedaría fuera delBETWEEN. Para rangos de días con hora, usa>=el primer día y<el día siguiente:WHERE fecha >= '2026-09-01' AND fecha < '2026-09-11'.
Para filtrar por año o por mes existen funciones como YEAR(fecha) en MySQL. Las verás en funciones de texto, números y fechas.
Buscar texto: LIKE
= solo encuentra textos idénticos. Para buscar partes de un texto se usa LIKE con un patrón que admite dos comodines:
| Comodín | Significa | Ejemplo | Encaja con |
|---|---|---|---|
% | Cualquier cantidad de caracteres, incluso ninguno | 'M%' | Marta, Madrid, M |
_ | Exactamente un carácter | '_u%' | Luis, Just (segunda letra “u”) |
WHERE nombre LIKE 'M%' -- empieza por M → Marta Ruiz
WHERE nombre LIKE '%ez' -- termina en "ez" → Luis Pérez
WHERE nombre LIKE '%ar%' -- contiene "ar" → Ana García, Marta Ruiz, Eva Martín
WHERE nombre LIKE '_u%' -- la segunda letra es "u" → Luis Pérez
Puedes combinar los dos comodines. Este patrón busca nombres cuyo apellido tiene exactamente cuatro letras (un espacio y cuatro caracteres al final):
SELECT nombre FROM clientes
WHERE nombre LIKE '% ____';
| nombre |
|---|
| Marta Ruiz |
| Pablo Díaz |
Y NOT LIKE devuelve lo que no encaja con el patrón:
SELECT nombre FROM clientes
WHERE nombre NOT LIKE '%a%';
| nombre |
|---|
| Luis Pérez |
Sin comodines, LIKE 'Madrid' equivale a = 'Madrid'. Si no necesitas patrón, usa =, que es más claro y más rápido.
Buscar un % o un _ de verdad: ESCAPE
¿Y si el texto que buscas contiene un % o un _? Imagina una tabla de códigos de descuento:
CREATE TABLE descuentos (codigo VARCHAR(20), descripcion VARCHAR(50));
INSERT INTO descuentos VALUES
('VERANO_10', '10 % en verano'),
('VERANOX10', 'Prueba'),
('BIENVENIDA', '5 % primera compra'),
('VIP_20', '20 % clientes VIP');
SELECT codigo FROM descuentos
WHERE codigo LIKE '%_10';
| codigo |
|---|
| VERANO_10 |
| VERANOX10 |
Sobra VERANOX10: el _ del patrón es un comodín y encaja con la X. Para que un comodín cuente como carácter normal, se escapa: eliges un carácter de escape con ESCAPE y lo pones delante.
SELECT codigo FROM descuentos
WHERE codigo LIKE '%!_10' ESCAPE '!';
| codigo |
|---|
| VERANO_10 |
Ahora !_ significa “un guion bajo de verdad”. Lo mismo con el porcentaje: descripcion LIKE '%!%%' ESCAPE '!' busca descripciones que contienen un %.
Nota: en MySQL y PostgreSQL, la barra invertida
\ya es el carácter de escape por defecto (LIKE '%\_10'), pero en SQLite no. ConESCAPEfunciona igual en todos.
Mayúsculas y minúsculas
¿Encuentra LIKE 'm%' a Marta? Depende del sistema:
| Sistema | nombre LIKE 'm%' | Cómo buscar sin distinguir |
|---|---|---|
| MySQL / MariaDB | Encuentra a Marta (con la configuración habitual, no distingue) | Ya lo hace |
| PostgreSQL | No la encuentra (distingue) | ILIKE 'm%' o LOWER(nombre) LIKE 'm%' |
| SQLite | La encuentra (no distingue en letras sin tilde) | Ya lo hace |
En MySQL, además, la configuración habitual tampoco distingue tildes: LIKE '%perez%' encuentra a “Luis Pérez”. En PostgreSQL y SQLite, no. La forma portable de buscar sin distinguir mayúsculas es pasar las dos partes a minúsculas: WHERE LOWER(nombre) LIKE '%marta%'.
Consejo: un patrón que empieza por
%('%ez') obliga a la base de datos a revisar todas las filas, porque no puede usar un índice. En tablas grandes se nota.'M%'sí puede aprovecharlo.
Valores vacíos: IS NULL
NULL significa “sin valor, desconocido”. No se puede comparar con =; hay que usar IS NULL o IS NOT NULL:
SELECT nombre FROM clientes WHERE ciudad IS NULL; -- ✓
SELECT nombre FROM clientes WHERE ciudad = NULL; -- ✗ nunca devuelve nada
Es un tema con bastantes trampas, así que tiene su propia lección: valores NULL.
Errores frecuentes
- Mezclar
ANDyORsin paréntesis, como en el ejemplo de Luis. - Escribir
==en lugar de=: en SQL la comparación lleva un solo igual. - Olvidar las comillas en un texto:
WHERE ciudad = Madridhace que la base de datos busque una columna llamadaMadrid. - Repetir la columna mal en un
OR:WHERE ciudad = 'Bilbao' OR 'Madrid'no da error en MySQL, pero no hace lo que crees. Escribeciudad = ... OR ciudad = ...o usaIN.
Resumen
| Quieres… | Escribe |
|---|---|
| Un valor exacto | WHERE ciudad = 'Madrid' |
| Distinto de | WHERE ciudad <> 'Madrid' |
| Varias condiciones a la vez | WHERE a AND b |
| Una u otra | WHERE (a OR b) con paréntesis si hay AND |
| En una lista / fuera de ella | IN ('Bilbao', 'Madrid') / NOT IN (...) |
| En un rango (extremos incluidos) | BETWEEN 30 AND 60 / NOT BETWEEN |
| Fechas | fecha >= '2026-09-10' |
| Empieza, termina o contiene | LIKE 'M%', LIKE '%ez', LIKE '%ar%' |
| Un carácter cualquiera | LIKE '_u%' |
Un _ o % literal | LIKE '%!_10' ESCAPE '!' |
| Sin valor | IS NULL / IS NOT NULL |
En la siguiente lección profundizarás en los valores NULL.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.