SQL · Capítulo 2 · Lección de conceptos · 6 min de lectura

Cómo decide WHERE qué filas se quedan

WHERE le hace una pregunta de sí o no a cada fila y conserva solo las filas donde la respuesta es un sí definitivo. Cuando ves la tercera respuesta, desconocido, la trampa del NULL deja de ser un misterio.

La idea

WHERE es un portero. Para cada fila de la tabla hace una pregunta de sí o no, armada con tus condiciones, y solo pasan las filas que reciben un sí definitivo. La fila no se cambia, no se mueve y no se compara con sus vecinas. Se prueba sola y se conserva o se queda fuera.

Suena a dos resultados, pero SQL tiene tres. Una prueba puede ser verdadera, falsa o desconocida, y lo desconocido aparece en cuanto interviene un NULL. WHERE conserva solo lo verdadero. Casi todos los errores de filtrado que encontrarás vienen de olvidar la tercera respuesta o de confundir el orden en que se combinan AND y OR.

Figura 1

WHERE prueba cada fila por separado, y AND exige que pasen las dos pruebas

orders
order_idchannelstatus
1003marketplacecompleted
1004marketplacecompleted
1005webcompleted
1006marketplacecancelled
1007marketplacecompleted
1008webcompleted
Resultado
order_idchannelstatus
1003marketplacecompleted
1004marketplacecompleted
1007marketplacecompleted

Paso 1 de 4 Seis filas de orders. La pregunta: WHERE channel = 'marketplace' AND status = 'completed'. La base de datos se la hace a cada fila por separado, como si las demás no existieran.

  1. Seis filas de orders. La pregunta: WHERE channel = 'marketplace' AND status = 'completed'. La base de datos se la hace a cada fila por separado, como si las demás no existieran.
  2. Primera prueba: channel = 'marketplace'. Los pedidos 1005 y 1008 son de la web, así que fallan. Pasan cuatro filas.
  3. Segunda prueba: status = 'completed'. El pedido 1006 pasó la primera prueba, pero fue cancelado. AND necesita que ambas respuestas sean sí, así que falla.
  4. Quedan tres filas. Las filas conservadas se copian sin cambios al resultado: WHERE solo decide quién se queda, nunca edita un valor.

Por qué existe

Harbor & Pine tiene unos 12,000 pedidos y nadie los lee todos. Necesitan los reembolsos del mes pasado, los clientes de negocio en Texas, los productos sin costo. WHERE es la forma de reducir la tabla a las filas que le importan a la pregunta, antes de contar, sumar u ordenar nada. Filtrar pronto también importa para la velocidad: cada paso posterior tiene menos filas con las que trabajar.

Sin él tendrías que exportarlo todo y filtrar a ojo en una hoja de cálculo, y la respuesta cambiaría en cuanto cambiaran los datos. Una cláusula WHERE es la pregunta escrita, así que puedes volver a ejecutarla la próxima semana y obtener el mismo tipo de respuesta.

Piensa en una cláusula WHERE como una frase que podrías leerle en voz alta a un colega: "pedidos del marketplace que se completaron". Si no puedes decirla con palabras simples, probablemente la condición no es lo que querías. Ese hábito atrapa la mayoría de los errores antes de ejecutar nada.

Cómo funciona por dentro

Cada fila pasa por la condición de forma independiente:

  1. La base de datos toma una fila y evalúa cada comparación con los valores de esa fila.
  2. Combina los resultados con AND y OR en una sola respuesta: verdadero, falso o desconocido.
  3. Solo lo verdadero conserva la fila. Falso y desconocido la descartan.

Esa última regla es toda la historia del NULL. Un NULL significa "valor no conocido", así que unit_cost = 503.50 es desconocido cuando el costo es NULL, y también lo es unit_cost <> 503.50. La fila falla ambas pruebas.

Figura 2

Comparar con NULL da desconocido, y WHERE conserva solo lo verdadero

products
product_idnameunit_cost
101Vale Velvet LoveseatNULL
102Alder Oak Coffee Table503.50
103Orchard Rattan Dining Chair429.87
WHERE unit_cost IS NULL
product_idname
101Vale Velvet Loveseat

Paso 1 de 4 ¿Qué productos no tienen costo registrado? El producto 101 sí: su unit_cost es NULL, es decir, desconocido.

  1. ¿Qué productos no tienen costo registrado? El producto 101 sí: su unit_cost es NULL, es decir, desconocido.
  2. WHERE unit_cost = NULL pregunta: ¿un desconocido es igual a otro desconocido? Nadie puede decirlo. La respuesta no es verdadera ni falsa sino desconocida, y WHERE conserva solo lo verdadero.
  3. Para el 101 es desconocido, y para el 102 y el 103 es falso. Ninguna fila es verdadera, así que la consulta no devuelve nada, sin ningún error.
  4. IS NULL es otra prueba. Responde un sí o no claro: ¿falta este valor? La fila 101 es un sí, y se conserva.

La solución es una prueba específica: IS NULL e IS NOT NULL preguntan "¿falta el valor?" y siempre responden sí o no. La misma lógica esconde una segunda trampa. WHERE referral_source <> 'search' descarta en silencio a todos los clientes sin fuente de referencia, porque para ellos la comparación es desconocida. Si quieres incluir a esos clientes, dilo: OR referral_source IS NULL.

Cómo se combina lo desconocido

La lógica de tres valores tiene unas reglas que vale la pena conocer. unknown AND false es falso, porque un solo falso basta para fallar. unknown AND true es desconocido. unknown OR true es verdadero, porque un solo verdadero basta para pasar. unknown OR false es desconocido. Y NOT unknown sigue siendo desconocido, por eso NOT (unit_cost = 503.50) sigue perdiendo las filas con NULL. Rara vez las calculas a mano. Solo recuerda que un NULL únicamente se rescata con algo verdadero.

AND se une con más fuerza que OR

Como la multiplicación antes que la suma, AND se evalúa antes que OR. Este filtro

WHERE channel = 'marketplace' OR status = 'completed' AND channel = 'web'

se lee como channel = 'marketplace' OR (status = 'completed' AND channel = 'web'). Cuando querías decir otra cosa, el resultado es erróneo y ningún error te avisa. Usa paréntesis siempre que una condición mezcle los dos. IN ('web', 'app') es una forma ordenada de escribir varios OR sobre la misma columna.

Fechas en un timestamp

order_date es un timestamp, no una fecha simple. WHERE order_date <= '2024-03-31' significa "hasta la medianoche con que empieza el 31 de marzo", así que se pierden todos los pedidos de ese día. Escribe un rango semiabierto: order_date >= '2024-03-01' AND order_date < '2024-04-01'. Incluye todo marzo y sirve para cualquier mes, sin importar su duración.

Patrones

LIKE '%@gmail.com' busca texto por patrón: % representa cualquier cantidad de caracteres y _ exactamente uno. LIKE normal distingue mayúsculas y minúsculas en la mayoría de las bases de datos, así que pasa la columna a minúsculas primero o usa ILIKE donde exista. Un patrón con % al inicio tiene que leer todos los valores, así que resérvalo para las preguntas que lo necesiten.

Dónde está WHERE

WHERE corre justo después de FROM, sobre las filas sin procesar. Eso tiene dos efectos prácticos. No puede ver los alias que defines en SELECT, porque todavía no existen. Y no puede filtrar por un total como COUNT(*) > 10, porque tampoco existen totales todavía. Las condiciones sobre totales van en HAVING, que verás en el capítulo 4.

Errores comunes

"WHERE columna = NULL encuentra los valores que faltan"

No devuelve nada, y sin ningún error, por eso duele. La comparación es desconocida para todas las filas. Usa IS NULL. Si un filtro que debería coincidir con algo devuelve cero filas, revisa si interviene un NULL antes que cualquier otra cosa.

"Distinto conserva todo lo que no es igual"

Conserva todo lo que se sabe que es diferente. Una fila con NULL no es igual ni distinta, así que desaparece tanto de = 'x' como de <> 'x'. Los dos resultados no suman la tabla entera. Cuenta las filas antes y después, y busca la diferencia.

"Un filtro no puede cambiar los valores de mis filas"

Cierto, y es una regla útil: WHERE conserva o descarta filas completas, nunca las edita. Si un valor se ve mal en la salida, el problema está en SELECT o en los datos, no en el filtro. Un filtro sí puede cambiar qué filas ve un agregado, así que un total puede cambiar cuando agregas una condición.

Ponte a prueba

Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.

Tabla orders: el 1006 es un pedido cancelado de marketplace, el 1008 es un pedido web completado. Ejecutas WHERE channel = 'marketplace' OR status = 'completed' AND channel = 'web'. ¿Cuál de las dos filas está en el resultado?

Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.