1. Qué es
WHERE descarta filas. Se ejecuta fila por fila, una a una, y hace una sola pregunta: ¿esta condición es verdadera para esta fila? Si la respuesta es sí, la fila sigue. Si es no —o si es desconocida, que es un caso aparte y tiene su propio tema— la fila desaparece.
Es el paso 2 del orden-logico-de-ejecucion y de lejos el más importante para el rendimiento: todo lo que descartes aquí es trabajo que no se hace después.
2. Sintaxis
SELECT columnas
FROM tabla
WHERE condición;
Operadores de comparación
| Operador | Significado |
|---|---|
= |
Igual |
<> o != |
Distinto |
< > <= >= |
Menor, mayor, menor o igual, mayor o igual |
<> es el del estándar SQL; != es un sinónimo que PostgreSQL acepta. Usa <>: funciona en todos los motores.
Operadores lógicos
| Operador | Significado | Precedencia |
|---|---|---|
NOT |
Niega | 1ª (la más alta) |
AND |
Ambas | 2ª |
OR |
Alguna | 3ª (la más baja) |
AND se evalúa antes que OR. Esa línea es la causa de más informes silenciosamente equivocados que ninguna otra en SQL. Se ve en § 3.
Atajos
| Forma | Equivale a |
|---|---|
x IN (a, b, c) |
x = a OR x = b OR x = c |
x BETWEEN a AND b |
x >= a AND x <= b |
x NOT IN (...), x NOT BETWEEN ... |
Lo contrario |
BETWEEN es inclusivo por los dos extremos. No es opinable, es la definición.
3. Ejemplos sobre Northwind
Comparación simple:
SELECT product_name, unit_price
FROM products
WHERE unit_price > 50
ORDER BY unit_price DESC;
product_name | unit_price
-------------------------+------------
Côte de Blaye | 263.50
Thüringer Rostbratwurst | 123.79
Mishi Kobe Niku | 97.00
Sir Rodney's Marmalade | 81.00
Carnarvon Tigers | 62.50
Raclette Courdavault | 55.00
Manjimup Dried Apples | 53.00
7 productos de 77. El WHERE acaba de ahorrar el 91% del trabajo.
La trampa de la precedencia
Esta consulta parece decir "clientes de Alemania o de Francia, que estén en París":
SELECT count(*) FROM customers
WHERE country = 'Germany' OR country = 'France' AND city = 'Paris';
count
-------
13
Y esta, con paréntesis, dice lo mismo en español:
SELECT count(*) FROM customers
WHERE (country = 'Germany' OR country = 'France') AND city = 'Paris';
count
-------
2
13 contra 2. ¿Qué pasó? Como AND va primero, la primera consulta se lee en realidad:
WHERE country = 'Germany' OR (country = 'France' AND city = 'Paris')
Es decir: todos los alemanes (11) más los franceses de París (2) = 13. Ninguna de las dos consultas da error. Las dos devuelven un número creíble. Solo una responde la pregunta.
Regla sin excepción: si en un
WHEREconvivenANDyOR, pon paréntesis. Aunque sepas la precedencia. Los paréntesis no cuestan nada y quitan la duda al que lo lea, que puede ser tú dentro de un mes.
IN — la forma legible del OR
SELECT count(*) FROM customers
WHERE country IN ('Germany', 'France', 'Spain');
count
-------
27
Equivale a tres OR encadenados, pero no puede sufrir el problema de precedencia: IN es un solo operador, así que no hay nada que agrupar mal.
BETWEEN — y su orden obligatorio
SELECT count(*) FROM products WHERE unit_price BETWEEN 20 AND 50; -- 31
SELECT count(*) FROM products WHERE unit_price >= 20 AND unit_price <= 50; -- 31
Idénticas. Pero:
SELECT count(*) FROM products WHERE unit_price BETWEEN 50 AND 20;
count
-------
0
Cero, y sin ningún aviso. BETWEEN 50 AND 20 se traduce a >= 50 AND <= 20, que no puede ser cierto nunca. El menor va primero, siempre.
Booleanos: sin = true
discontinued es de tipo boolean, así que ya es una condición:
SELECT product_name, discontinued FROM products WHERE discontinued;
product_name | discontinued
------------------------+--------------
Chai | t
Chang | t
Chef Anton's Gumbo Mix | t
10 productos descatalogados. Y el contrario:
SELECT count(*) FROM products WHERE NOT discontinued; -- 67
WHERE discontinued = true funciona, pero es como decir "¿es verdad que es verdad?". WHERE discontinued y WHERE NOT discontinued es como se escribe.
El texto distingue mayúsculas
SELECT count(*) FROM customers WHERE country = 'germany';
count
-------
0
Once clientes alemanes, y la consulta devuelve cero. En PostgreSQL la comparación de texto es sensible a mayúsculas y a acentos. Las formas de no depender de eso están en buscar-texto-like-y-regex.
El aviso que hay que dar ya
SELECT count(*) FROM customers WHERE region = 'SP'; -- 6
SELECT count(*) FROM customers WHERE region <> 'SP'; -- 25
6 + 25 = 31, y la tabla tiene 91 clientes. Faltan 60.
No es un error del motor: 60 clientes tienen region en NULL, y NULL no es igual ni distinto a nada. Ni entra en el =, ni entra en el <>. Es el tema siguiente y hay que leerlo entero: null-y-la-logica-de-tres-valores.
4. Errores comunes
- Mezclar
ANDyORsin paréntesis. Ver arriba. No da error; da un número equivocado. BETWEENcon los extremos al revés. Devuelve 0 filas sin avisar.BETWEENcon fechas que llevan hora.order_dateen Northwind esdate, así queBETWEEN '1997-01-01' AND '1997-12-31'está bien (408 pedidos). Si la columna fueratimestamp, ese mismoBETWEENperdería todo el 31 de diciembre a partir de las 00:00:01, porque'1997-12-31'significa1997-12-31 00:00:00. La forma a prueba de balas es>= '1997-01-01' AND < '1998-01-01'.- Comillas dobles en un valor de texto.
WHERE country = "Germany"busca una columna llamadaGermanyy dacolumn "Germany" does not exist. Simples para texto, dobles para identificadores. - Comparar texto sin cuidar mayúsculas ni acentos.
'méxico','Mexico'y'México'son tres valores distintos. - Restar todo con
<>y esperar el complemento. No lo es, por losNULL.
5. Diferencias con SQL Server
| PostgreSQL | SQL Server | |
|---|---|---|
| Sensibilidad a mayúsculas | Siempre sensible | Depende del collation de la base — por defecto suele ser insensible |
| Tipo booleano | boolean nativo, WHERE activo |
No hay boolean; se usa bit y hay que escribir WHERE activo = 1 |
| Distinto | <> o != |
<> o != |
La primera fila es la diferencia que más sorprende al venir de SQL Server: allí WHERE country = 'germany' normalmente sí devuelve los alemanes. En PostgreSQL nunca. No es que un motor esté mal: es que SQL Server esconde la decisión en una propiedad de la base y PostgreSQL te obliga a tomarla en la consulta.
6. Ejercicios
- Productos con precio entre 10 y 20, ambos incluidos, ordenados de más caro a más barato.
- Clientes de Alemania, Austria o Suiza (
Switzerland), usando un solo operador. - Pedidos de 1997 con un flete superior a 100. ¿Cuántos son?
- Productos no descatalogados con menos de 10 unidades en stock. Son los que hay que reponer.
- Sin ejecutarla, di qué devuelve y por qué:
sql SELECT count(*) FROM products WHERE units_in_stock BETWEEN 100 AND 10;
7. Pregunta de negocio
Compras pide "el listado de productos en riesgo": los que se están quedando sin stock y todavía se venden. Ellos consideran riesgo si quedan menos unidades de las que marca el punto de reposición, o si hay pedidos pendientes de recibir.
La trampa está en el "o". Léela dos veces antes de escribir.