Aprende SQL · lección gratuita
Cuando una subconsulta devuelve varias filas ya no puedes compararla con = o > directamente: necesitas un operador que trabaje sobre un conjunto de valores. Los tres clásicos del estándar SQL son IN (¿el valor está en la lista?), ANY/SOME (¿cumple la comparación con al menos uno?) y ALL (¿la cumple con todos?). SQLite implementa IN y NOT IN de forma nativa, pero no soporta la sintaxis op ANY/ALL (subconsulta); veremos el equivalente exacto y portable con agregados.
IN (subconsulta): verdadero si el valor coincide con alguna fila devuelta. Ideal para semi-joins (filtrar por pertenencia).NOT IN (subconsulta): verdadero si el valor no aparece. Perfecto para anti-joins (lo que no está), p. ej. productos nunca vendidos.ANY / SOME: x > ANY(...) ≡ "mayor que al menos uno" ≡ x > (SELECT MIN(...) ...).ALL: x > ALL(...) ≡ "mayor que todos" ≡ x > (SELECT MAX(...) ...).NOT IN con NULL: si la subconsulta produce algún NULL, NOT IN devuelve cero filas. En la semilla no hay NULLs, pero conviene saberlo.IN y NOT INcolumna IN (SELECT ...) compara la columna contra el conjunto completo de valores. Es la forma más legible de expresar "pertenece a este grupo". NOT IN es su negación y se usa muchísimo para encontrar filas sin correspondencia (anti-join). El motor evalúa la subconsulta una vez y mantiene el conjunto de valores.
ANY / ALL y su equivalente en SQLiteEn motores como PostgreSQL o MySQL puedes escribir precio > ALL (SELECT precio FROM productos WHERE categoria_id = 1). Conceptualmente:
x > ALL(conjunto) → x es mayor que el máximo del conjunto.x > ANY(conjunto) → x es mayor que el mínimo del conjunto.x < ALL(conjunto) → x es menor que el mínimo del conjunto.x < ANY(conjunto) → x es menor que el máximo del conjunto.x = ANY(conjunto) → equivale exactamente a x IN (conjunto).x <> ALL(conjunto) → equivale a x NOT IN (conjunto).⚠️ No asumas queANYsiempre se traduce aMINniALLaMAX: depende del operador. La regla general es queANYusa el extremo que más fácilmente cumple la comparación (el más permisivo) yALLel más exigente. Por eso con>se invierte respecto a<: para> ANYbasta superar el mínimo, pero para< ANYbasta quedar por debajo del máximo.
SQLite no acepta > ANY (...) ni > ALL (...): lanza un error de sintaxis. La traducción correcta y portable es usar el agregado equivalente:
| Estándar | Equivalente en SQLite |
|---|---|
x > ALL(SELECT c FROM t) | x > (SELECT MAX(c) FROM t) |
x < ALL(SELECT c FROM t) | x < (SELECT MIN(c) FROM t) |
x > ANY(SELECT c FROM t) | x > (SELECT MIN(c) FROM t) |
x < ANY(SELECT c FROM t) | x < (SELECT MAX(c) FROM t) |
x = ANY(SELECT c FROM t) | x IN (SELECT c FROM t) |
x <> ALL(SELECT c FROM t) | x NOT IN (SELECT c FROM t) |
SELECT empresa, (SELECT COUNT(*) FROM pedidos WHERE cliente_id = c.id) AS total_pedidos FROM clientes c ORDER BY total_pedidos DESC LIMIT 5
-- IN: clientes que han hecho ALGÚN pedido cancelado (semi-join por pertenencia).
SELECT empresa, pais
FROM clientes
WHERE id IN (SELECT cliente_id FROM pedidos WHERE estado = 'Cancelado')
ORDER BY empresa;
-- NOT IN: productos NUNCA vendidos (no aparecen en ninguna línea de detalle). Anti-join clásico.
SELECT id, nombre
FROM productos
WHERE id NOT IN (SELECT producto_id FROM detalle_pedidos)
ORDER BY id;
-- ANY (estándar: precio > ANY de la categoría 3) => en SQLite: > MIN de esa categoría.
-- "Productos más caros que AL MENOS UNO de los servidores (categoría 3)".
SELECT nombre, precio
FROM productos
WHERE precio > (SELECT MIN(precio) FROM productos WHERE categoria_id = 3)
ORDER BY precio
LIMIT 5;
-- ALL (estándar: precio > ALL del soporte) => en SQLite: > MAX de la categoría 5.
-- "Productos más caros que TODOS los de Soporte y Mantenimiento".
SELECT nombre, precio
FROM productos
WHERE precio > (SELECT MAX(precio) FROM productos WHERE categoria_id = 5)
ORDER BY precio;
💡 Si intentasWHERE precio > ALL (SELECT ...)en SQLite obtendrásnear "ALL": syntax error. Convierte mentalmente> ALL→> (SELECT MAX ...)y> ANY→> (SELECT MIN ...). Para igualdad usa directamenteIN/NOT IN.
| Operador | Qué hace | En SQLite |
|---|---|---|
IN (subq) | ¿pertenece al conjunto? | ✅ nativo |
NOT IN (subq) | ¿no pertenece? (anti-join) | ✅ nativo (ojo con NULLs) |
> ALL (subq) | mayor que todos | usar > (SELECT MAX ...) |
> ANY (subq) | mayor que alguno | usar > (SELECT MIN ...) |
= ANY (subq) | igual a alguno | equivale a IN (subq) |
---
← Subconsultas escalaresSubconsultas correlacionadas y EXISTS →