Ir al contenido principal

Filtros y ordenamiento

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.

Resultado de SELECT nombre, localidad FROM socios WHERE localidad = 'La Plata': la grilla con los socios de La Plata y el aviso de 17 filas
El 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 = 1 no «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
ANDSe cumplen las dos condiciones.WHERE activo = 1 AND alta > '2026-01-01'
ORSe cumple al menos una.WHERE localidad = 'La Plata' OR localidad = 'Berisso'
NOTNo se cumple la condición.WHERE NOT activo = 1

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.

Resultado de la condición escrita sin paréntesis: la cuenta da 20, porque el AND se aplicó antes que el OR
Escrita sin paréntesis, la condición devuelve 20: el AND se aplicó primero y el activo = 1 terminó afectando solo a Berisso.
Resultado de la misma condición con paréntesis: la cuenta da 19, que es lo que se buscaba
Con paréntesis, la misma condición devuelve 19: primero se resuelve el 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.

Resultado de SELECT nombre FROM socios WHERE nombre LIKE '%ana%': la grilla con los quince nombres que contienen ana
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:

  • NULL significa «no hay dato».
  • '' —el texto vacío— significa «el dato existe y está vacío».
  • 0 significa «el valor es cero».

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.

Resultado de SELECT id, libro_id, salida FROM prestamos WHERE vuelta IS NULL: la grilla con los nueve préstamos que no se devolvieron
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
ASCAscendente: de menor a mayor, de la A a la Z. Es el valor por defecto, así que se puede omitir.
DESCDescendente: de mayor a menor, de la Z a la A.
Varias columnasLa 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.

Resultado de SELECT nombre, alta FROM socios ORDER BY alta DESC: las altas ordenadas de la más reciente a la más antigua
Con 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;
nombreprecio
Barrita de cereal2200.00
Alfajor de chocolate1200.00
Caramelos surtidos350.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;
nombreprecio
Barrita de cereal2200.00
Gaseosa de 500 ml2500.00
Sándwich de miga3200.00
Cigarrillos4500.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;
nombrealta
Ezequiel Moreno2026-01-15
Antonella Ruiz2026-02-03
Gonzalo Prieto2026-02-20
Malena Aguirre2026-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;
titulogeneroanio
El secreto de sus ojosPolicial2009
Tiempo de valientesPolicial2005

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;
tituloanio
El hijo de la novia2001
El secreto de sus ojos2009

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;
rubronombreprecio
BebidasAgua mineral1800.00
BebidasCafé en vaso1500.00
BebidasGaseosa de 500 ml2500.00
GolosinasAlfajor de chocolate1200.00
GolosinasBarrita de cereal2200.00
GolosinasCaramelos surtidos350.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;
nombredocumentotelefono
Roberto Vidal30111222NULL

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_idperiodoimporte
42026-07-0132000.00
22026-08-0128000.00
52026-09-0135000.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;
rubronombreprecio
BebidasAgua mineral1800.00
BebidasCafé en vaso1500.00
BebidasGaseosa de 500 ml2500.00
CigarrillosCigarrillos4500.00
GolosinasAlfajor de chocolate1200.00
GolosinasBarrita de cereal2200.00
GolosinasCaramelos surtidos350.00
PanaderíaSándwich de miga3200.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:

  1. Descargar el archivo desde la tabla de arriba y fijarse dónde quedó: casi siempre, en la carpeta Descargas.
  2. 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.
  3. Elegir el archivo con el botón Seleccionar archivo. No hace falta crear antes la base de datos: el archivo la crea.
  4. 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

  • WHERE filtra: 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, OR y NOT combinan condiciones, y NOT va primero, después AND y al final OR.
  • Cuando se mezclan AND y OR, usar paréntesis: sin ellos la condición dice otra cosa y no da error.
  • LIKE busca parecidos con % (cualquier cosa) y _ (un carácter).
  • BETWEEN es inclusivo y equivale a dos comparaciones.
  • IN compara contra una lista y reemplaza una cadena de OR.
  • NULL es ausencia de valor, no cero ni texto vacío.
  • = NULL nunca devuelve nada: se usa IS NULL y IS NOT NULL.
  • Los registros con NULL quedan afuera de <>, NOT IN y NOT LIKE.
  • Sin ORDER BY el orden no está garantizado. ASC es el valor por defecto y la segunda columna desempata.

Ver modificación y eliminación