La cláusula WHERE
Sin WHERE, un SELECT devuelve todos los registros de la tabla. Con WHERE, devuelve solo los que cumplen una condición.
SELECT nombre, localidad
FROM socios
WHERE localidad = 'La Plata';
Para entender bien qué hace, hay que verlo como lo ve la base de datos. La condición se evalúa una vez por cada registro, y la base se queda con los que dieron verdadero. No es que «busque» el socio de La Plata: recorre los registros y, para cada uno, se pregunta si su localidad es La Plata. Los que sí pasan van al resultado; los que no, se descartan.
Ese modelo mental explica varias cosas que de otro modo sorprenden. Explica por qué una consulta puede devolver varios registros aunque uno espere uno solo: si la condición es amplia, muchos dan verdadero. Explica también por qué el orden del resultado no tiene por qué ser el que uno imaginaba, y por qué una condición que se cumple «casi siempre» devuelve casi toda la tabla. Y explica por qué el WHERE es la cláusula que más veces se escribe mal: no es una búsqueda, es un filtro.
WHERE deja afuera todo lo que no cumple la condición: de 50 socios, la grilla muestra los 17 de La Plata.Operadores de comparación
Son los seis que arman cualquier condición. Cada uno compara un valor de la tabla con algo y devuelve verdadero o falso:
| Operador | Significa | Ejemplo |
|---|---|---|
= | Igual a. | WHERE activo = 1 |
<> | Distinto de. | WHERE localidad <> 'La Plata' |
> | Mayor que. | WHERE precio > 10000 |
< | Menor que. | WHERE edad < 18 |
>= | Mayor o igual que. | WHERE alta >= '2026-01-01' |
<= | Menor o igual que. | WHERE nota <= 10 |
Tres cosas de esa tabla merecen un comentario:
- El
=compara, no asigna. En casi todos los lenguajes de programación, el signo igual guarda un valor en una variable; en SQL pregunta si dos cosas son iguales. Confundirlo es el error más frecuente de quien viene de programar:WHERE activo = 1no «pone» el activo en 1, sino que pregunta si vale 1. Para modificar valores estáUPDATE, que se ve en el tema siguiente. - Lo distinto se escribe
<>—el estándar— aunque casi todos los gestores también aceptan!=. Conviene usar el primero, que funciona en cualquier lado. - Con los textos, la comparación depende de cómo esté configurado el gestor. Si las mayúsculas cuentan como distintas y cómo se ordenan los acentos son cuestiones de configuración, no del lenguaje. Lo práctico es saber que existe la duda y comprobarlo con un par de consultas antes de dar por hecho que
'ana'y'Ana'son lo mismo.
Y una buena noticia sobre las fechas: si el campo se declaró con el tipo correcto, las comparaciones funcionan como fechas, no como texto. WHERE alta >= '2026-01-01' devuelve lo cargado desde el 1.º de enero, y el 15 de febrero queda después del 3 de marzo de 2025 aunque alfabéticamente sea al revés. Esa es la ventaja concreta de haber declarado bien el tipo: si la fecha se hubiera guardado como texto, la misma comparación daría cualquier cosa.
Operadores lógicos
Cuando la condición tiene más de una parte, las partes se unen con AND, OR y NOT:
| Operador | El registro pasa si… | Ejemplo |
|---|---|---|
AND | Se cumplen las dos condiciones. | WHERE activo = 1 AND alta > '2026-01-01' |
OR | Se cumple al menos una. | WHERE localidad = 'La Plata' OR localidad = 'Berisso' |
NOT | No se cumple la condición. | WHERE NOT activo = 1 |
NOT se evalúa primero, después AND y por último OR. Es decir que OR tiene la menor prioridad y queda «abierto» a los dos lados, y eso hace que una condición sin paréntesis diga algo distinto de lo que parece.
El caso clásico, que conviene leer con atención porque le pasa a todo el mundo:
-- Lo que se escribe, creyendo que dice "socios de La Plata o Berisso, activos"
WHERE localidad = 'La Plata' OR localidad = 'Berisso' AND activo = 1
-- Lo que REALMENTE dice, porque AND se evalúa antes:
WHERE localidad = 'La Plata' OR (localidad = 'Berisso' AND activo = 1)
-- Lo que probablemente se quería:
WHERE (localidad = 'La Plata' OR localidad = 'Berisso') AND activo = 1
La diferencia es real y no da ningún error: la primera versión devuelve todos los socios de La Plata, incluso los dados de baja, y de Berisso solo los activos. Un resultado que «parece» razonable y está mal. La conclusión práctica es simple y no cuesta nada: usar paréntesis. Cuando una condición mezcla AND con OR, los paréntesis dicen cuál es la agrupación y evitan tener que recordar la prioridad.
AND se aplicó primero y el activo = 1 terminó afectando solo a Berisso.
OR y después se pide que estén activos. La diferencia de un registro es todo lo que cambia.Búsquedas con LIKE, BETWEEN e IN
Los tres operadores que resuelven casos que con los seis de comparación saldrían larguísimos. Los tres tienen su versión negada con NOT adelante.
LIKE: buscar por parecido
= exige coincidencia exacta. LIKE busca parecidos, y para eso usa comodines: el % representa cualquier cosa, de cualquier largo y el _ representa un solo carácter.
nombre LIKE 'Ana%' -- empieza con "Ana": Ana, Ana María, Anabel...
nombre LIKE '%Pérez' -- termina con "Pérez"
nombre LIKE '%ana%' -- contiene "ana": Ana, Susana, Mariana
nombre LIKE '_na' -- un carácter y después "na": Ana, Una... no "Anna"
nombre NOT LIKE 'a%' -- no empieza con "a"
La posición del % cambia por completo el resultado, y el más usado es el del medio: '%texto%'. Es el que corresponde a un cuadro de búsqueda, porque es el más permisivo.
BETWEEN: entre dos valores
WHERE alta BETWEEN '2026-01-01' AND '2026-06-30'
WHERE precio BETWEEN 1000 AND 5000
Es inclusivo: los dos extremos entran en el resultado. BETWEEN 1000 AND 5000 incluye el 1000 y el 5000. Y es exactamente equivalente a >= 1000 AND <= 5000, pero se lee mucho mejor. Funciona con números, fechas y textos.
IN: contra una lista
WHERE localidad IN ('La Plata', 'Berisso', 'Ensenada')
WHERE localidad NOT IN ('La Plata', 'Berisso')
Evita encadenar OR: IN ('La Plata', 'Berisso') es lo mismo que = 'La Plata' OR = 'Berisso', y con diez valores la diferencia de legibilidad es enorme. Es también la forma natural de escribir «está en esta lista», que es una pregunta que aparece todo el tiempo.
LIKE '%ana%' trae quince nombres: los que empiezan con Ana y también los que la tienen en el medio, como Susana o Mariana.Valores nulos: IS NULL e IS NOT NULL
En SQL, NULL no es un valor: es la ausencia de valor. Significa «no se sabe», «no se cargó», «no corresponde». Y no es lo mismo que un cero ni que un texto vacío:
NULLsignifica «no hay dato».''—el texto vacío— significa «el dato existe y está vacío».0significa «el valor es cero».
WHERE vuelta = NULL no devuelve nada, nunca. No es un error de sintaxis: la consulta se ejecuta y devuelve cero registros, siempre, aunque haya cientos de campos vacíos. Para preguntar por los vacíos hay que usar IS NULL, y para los que tienen dato, IS NOT NULL. Es el error más famoso de SQL y lo comete todo el mundo alguna vez.
La razón es que una comparación con algo desconocido da desconocido, no verdadero. Y como el WHERE se queda solo con lo que da verdadero, un registro cuya vuelta es NULL nunca pasa el filtro vuelta = NULL —porque NULL = NULL no es verdadero: es desconocido, ya que no se sabe si dos datos que no se conocen son iguales—. De ahí que NULL necesite operadores propios:
SELECT id, libro_id, salida FROM prestamos WHERE vuelta IS NULL; -- los que no se devolvieron
SELECT id, libro_id, salida FROM prestamos WHERE vuelta IS NOT NULL; -- los que ya volvieron
Y hay una consecuencia de lo mismo que sorprende mucho más y no es tan conocida: los registros con NULL quedan afuera de las comparaciones negativas. Porque para descartar un registro hay que saber que la comparación es falsa, y con un valor desconocido no se puede afirmar que sea falsa.
-- Devuelve los socios que NO son de La Plata...
SELECT nombre FROM socios WHERE localidad <> 'La Plata';
-- ...pero NO devuelve los que tienen la localidad vacía.
-- Esos no son "de La Plata" ni "de otro lado": no se sabe. Hay que pedirlos aparte:
SELECT nombre FROM socios WHERE localidad <> 'La Plata' OR localidad IS NULL;
Lo mismo vale para NOT IN y para NOT LIKE. Y un último detalle para tener presente: cualquier cuenta aritmética con NULL da NULL —NULL + 5 no es 5, es desconocido—, así que un solo campo vacío puede arruinar un total. Eso enlaza directo con el tema de las funciones de agregación, donde el manejo de los nulos cambia el resultado de un promedio.
IS NULL encuentra los nueve préstamos que todavía no volvieron. Con = NULL la misma consulta habría devuelto cero filas.Ordenamiento con ORDER BY, ASC y DESC
Sin ORDER BY, el orden del resultado no está garantizado. La base puede devolverlo como le resulte más cómodo, y ese orden puede cambiar de una consulta a la siguiente sin que nadie haya tocado nada. Para una persona que lee una lista, eso es inaceptable: una lista de socios sin orden alfabético es inservible.
SELECT nombre, localidad FROM socios ORDER BY nombre; -- ascendente (por defecto)
SELECT nombre, precio FROM productos ORDER BY precio DESC; -- descendente
SELECT nombre, localidad FROM socios ORDER BY localidad, nombre; -- por dos columnas
| Cláusula | Qué hace |
|---|---|
ASC | Ascendente: de menor a mayor, de la A a la Z. Es el valor por defecto, así que se puede omitir. |
DESC | Descendente: de mayor a menor, de la Z a la A. |
| Varias columnas | La segunda ordena dentro de cada grupo de la primera. En el ejemplo, los socios se ordenan por localidad y, los de la misma localidad, alfabéticamente entre sí. |
El ordenamiento por varias columnas es más importante de lo que parece. Ordenar solo por localidad deja a los socios de cada localidad en un orden arbitrario; agregando nombre como segundo criterio, la lista queda estable y previsible. Es la diferencia entre un listado que se puede revisar y uno que hay que leer entero cada vez.
Dos detalles más. Se puede ordenar por columnas que no se muestran: SELECT nombre FROM socios ORDER BY localidad es válido, y a veces es exactamente lo que se necesita. Y el lugar de los vacíos en el orden depende del gestor —en MySQL los NULL van primero al ordenar ascendente y últimos al ordenar descendente—, así que si una lista tiene campos vacíos conviene mirar dónde aparecen antes de darlo por hecho.
DESC la más reciente queda arriba. Sin DESC, el mismo ORDER BY las ordena al revés, porque el orden por omisión es ascendente.Ejemplos resueltos
Los siguientes ejemplos se resuelven con los datos de los archivos para practicar, así que se pueden ejecutar tal cual están escritos y dan el mismo resultado que aparece acá. Ningún resultado está escrito a mano: salen de correr estas mismas consultas contra la base. Abajo de cada uno está el nombre de la base, para saber de dónde salen los datos.
1. Filtrar por una condición
El WHERE se queda solo con las filas que cumplen la condición, y el ORDER BY ordena el resultado por la columna que se indique. Las dos cláusulas van después del FROM, y en ese orden. Acá se piden las golosinas, de la más cara a la más barata.
SELECT nombre, precio
FROM productos
WHERE rubro = 'Golosinas'
ORDER BY precio DESC;
| nombre | precio |
|---|---|
| Barrita de cereal | 2200.00 |
| Alfajor de chocolate | 1200.00 |
| Caramelos surtidos | 350.00 |
Base kiosco · 3 filas.
2. Comparar con un número
Los operadores de comparación funcionan igual con números que con texto, y un DECIMAL admite decimales. Acá se piden los productos que cuestan dos mil pesos o más, del más barato al más caro.
SELECT nombre, precio
FROM productos
WHERE precio >= 2000
ORDER BY precio;
| nombre | precio |
|---|---|
| Barrita de cereal | 2200.00 |
| Gaseosa de 500 ml | 2500.00 |
| Sándwich de miga | 3200.00 |
| Cigarrillos | 4500.00 |
Base kiosco · 4 filas.
3. Comparar con una fecha
Las fechas se escriben entre comillas simples y con el formato 'AAAA-MM-DD'. Acá se piden los socios que se asociaron en el primer trimestre del año, y el BETWEEN incluye los dos extremos.
SELECT nombre, alta
FROM socios
WHERE alta BETWEEN '2026-01-01' AND '2026-03-31'
ORDER BY alta;
| nombre | alta |
|---|---|
| Ezequiel Moreno | 2026-01-15 |
| Antonella Ruiz | 2026-02-03 |
| Gonzalo Prieto | 2026-02-20 |
| Malena Aguirre | 2026-03-08 |
Base gimnasio · 4 filas.
4. Combinar dos condiciones con AND
Con AND se tienen que cumplir las dos condiciones. Este ejemplo busca dos cosas a la vez: que la película sea policial y que no sea anterior a 2005. Si el filtro se escribiera con OR, alcanzaría con una sola y el resultado traería también los dramas y las comedias.
SELECT titulo, genero, anio
FROM peliculas
WHERE genero = 'Policial' AND anio >= 2005;
| titulo | genero | anio |
|---|---|---|
| El secreto de sus ojos | Policial | 2009 |
| Tiempo de valientes | Policial | 2005 |
Base videoclub · 2 filas.
5. Buscar por un patrón de texto con LIKE
LIKE compara contra un patrón, no contra un valor exacto: el % significa «cualquier cosa». Acá se piden los títulos que empiezan con «El». El espacio después de la palabra es a propósito: así «El secreto…» entra, pero «Esperando la carroza» no.
SELECT titulo, anio
FROM peliculas
WHERE titulo LIKE 'El %'
ORDER BY titulo;
| titulo | anio |
|---|---|
| El hijo de la novia | 2001 |
| El secreto de sus ojos | 2009 |
Base videoclub · 2 filas.
6. Varios valores posibles con IN
IN es la forma corta de escribir varios OR sobre la misma columna: se cumple si el valor coincide con alguno de la lista. Es más fácil de leer y de modificar que la cadena de OR. Acá se piden dos rubros.
SELECT rubro, nombre, precio
FROM productos
WHERE rubro IN ('Bebidas', 'Golosinas')
ORDER BY rubro, nombre;
| rubro | nombre | precio |
|---|---|---|
| Bebidas | Agua mineral | 1800.00 |
| Bebidas | Café en vaso | 1500.00 |
| Bebidas | Gaseosa de 500 ml | 2500.00 |
| Golosinas | Alfajor de chocolate | 1200.00 |
| Golosinas | Barrita de cereal | 2200.00 |
| Golosinas | Caramelos surtidos | 350.00 |
Base kiosco · 6 filas.
7. Los que no tienen el dato cargado
Un campo vacío no se busca con = NULL: eso no devuelve nunca ninguna fila, ni siquiera cuando hay vacíos. Se pregunta con IS NULL, y lo contrario es IS NOT NULL. Acá aparecen los dueños de los que no se anotó el teléfono.
SELECT nombre, documento, telefono
FROM duenos
WHERE telefono IS NULL;
| nombre | documento | telefono |
|---|---|---|
| Roberto Vidal | 30111222 | NULL |
Base veterinaria · Una fila.
8. Los que todavía no pagaron
Ojo con un detalle: pagada es una columna que vale 1 o 0, no un campo vacío, así que se pregunta con = 0 y no con IS NULL. Confundir las dos cosas es uno de los errores más comunes, y el primero no devuelve nada.
SELECT socio_id, periodo, importe
FROM cuotas
WHERE pagada = 0
ORDER BY periodo, socio_id;
| socio_id | periodo | importe |
|---|---|---|
| 4 | 2026-07-01 | 32000.00 |
| 2 | 2026-08-01 | 28000.00 |
| 5 | 2026-09-01 | 35000.00 |
Base gimnasio · 3 filas.
9. Ordenar por dos columnas
El ORDER BY puede llevar varias columnas, separadas por comas, y cada una con su ASC o DESC (ASC es lo que viene por omisión). Acá se ordena por rubro y, dentro de cada rubro, por nombre: los productos de cada rubro quedan juntos y alfabetizados entre sí.
SELECT rubro, nombre, precio
FROM productos
ORDER BY rubro, nombre;
| rubro | nombre | precio |
|---|---|---|
| Bebidas | Agua mineral | 1800.00 |
| Bebidas | Café en vaso | 1500.00 |
| Bebidas | Gaseosa de 500 ml | 2500.00 |
| Cigarrillos | Cigarrillos | 4500.00 |
| Golosinas | Alfajor de chocolate | 1200.00 |
| Golosinas | Barrita de cereal | 2200.00 |
| Golosinas | Caramelos surtidos | 350.00 |
| Panadería | Sándwich de miga | 3200.00 |
Base kiosco · 8 filas.
Archivos para practicar
Todo lo que se explica en esta página se puede probar de verdad, sin copiar y pegar sentencia por sentencia. Cada archivo crea su propia base, con las tablas y los datos ya cargados, y trae al final —comentadas— las consultas para practicar.
| Archivo | Descargar | Qué trae |
|---|---|---|
kiosco.sql |
Descargar | Un kiosco, con dos tablas: los productos con su precio y su stock, y las ventas. Es la base que más se usa en los ejemplos de esta página. |
videoclub.sql |
Descargar | Un videoclub, con tres tablas: películas, socios y alquileres. |
veterinaria.sql |
Descargar | Una veterinaria, con tres tablas: dueños, mascotas y consultas. |
gimnasio.sql |
Descargar | Un gimnasio, con dos tablas: los socios y sus cuotas, con importes y fechas. |
Cómo se importa
Importar es abrir el archivo .sql dentro del gestor para que él ejecute todas las sentencias de una sola vez. Son cuatro pasos, y son siempre los mismos:
- Descargar el archivo desde la tabla de arriba y fijarse dónde quedó: casi siempre, en la carpeta Descargas.
- Abrir phpMyAdmin —en XAMPP, el botón Admin que está en la fila de MySQL— y entrar a la pestaña Importar, la que está arriba de todo.
- Elegir el archivo con el botón Seleccionar archivo. No hace falta crear antes la base de datos: el archivo la crea.
- Apretar Importar y esperar el mensaje verde: «La importación se ha finalizado correctamente».
Si el mensaje es verde, ya está: la base aparece en la lista de la izquierda, con sus tablas y con los datos cargados. Si sale en rojo, phpMyAdmin dice en qué línea del archivo se trabó, y las dos causas más comunes son que la base ya existía o que el archivo se cortó al descargarse.
Cuidado con el nombre de la base. Al importar, cada archivo borra la base con ese nombre si ya existía y la vuelve a crear vacía. Es lo que permite repetir la práctica todas las veces que haga falta —siempre arranca igual—, pero también significa que no conviene guardar datos propios en una base que se llame biblioteca_1_tabla, biblioteca_2_tablas o biblioteca_3_tablas.
Resumen
WHEREfiltra: la condición se evalúa registro por registro y se queda con los que dan verdadero.- Los seis operadores de comparación son
=,<>,>,<,>=y<=. - En SQL,
=compara, no asigna. AND,ORyNOTcombinan condiciones, yNOTva primero, despuésANDy al finalOR.- Cuando se mezclan
ANDyOR, usar paréntesis: sin ellos la condición dice otra cosa y no da error. LIKEbusca parecidos con%(cualquier cosa) y_(un carácter).BETWEENes inclusivo y equivale a dos comparaciones.INcompara contra una lista y reemplaza una cadena deOR.NULLes ausencia de valor, no cero ni texto vacío.= NULLnunca devuelve nada: se usaIS NULLyIS NOT NULL.- Los registros con
NULLquedan afuera de<>,NOT INyNOT LIKE. - Sin
ORDER BYel orden no está garantizado.ASCes el valor por defecto y la segunda columna desempata.