Filtrando filas

esquema de la base de datos: esquema.txt
datos: datos.txt

Sabemos cómo obtener el contenido de una tabla, incluso seleccionar algunas de sus columnas y descartar otras, ordenar…

Vamos a darle potencia a la consulta. Ahora filtramos por filas, no queremos todo lo que hay sino aquello que cumple una o varias condiciones. Así que en este momento ya nos manejamos con

select columnas from tabla where condiciones

En estas condiciones normalmente usamos operadores de comparación: igualdad (=), desigualdad (!=, <>), mayor (>), mayor o igual (>=), menor (<), menor o igual (<=), no (NOT)

Examinemos primero el contenido de la tabla ARTICULO.

select * from articulo;

De esas filas, todas, queremos ver solo algunas que cumplen con un cierto criterio de selección.

Que "unidad" sea igual a la letra 'k':

select * from articulo where unidad = 'k';

Que el precio sea 11.75 (euros). Como siempre, fíjate que el punto de decimales es eso, punto:

select * from articulo where precio = 11.75;

Que el código sea 16. Cuidado, el código es un cadena de caracteres y tiene 3 ceros a la izquierda. Y comillas simples:

select * from articulo where cod = '00016';

También tenemos el "distinto". Hay 2 operadores idénticos en su resultado. Da igual que uses uno u otro:

select * from articulo where cod != '00016' order by cod;

select * from articulo where cod <> '00016' order by cod;

Aunque lo haya parecido, aquello por lo que preguntamos no necesariamente es lo que finalmente vemos.

Aquí buscamos datos de la fila con código '00016' pero el código no lo necesitamos como resultado:

select nombre,precio from articulo where cod = '00016';

Si no te lo crees, incorpóralo a la salida:

select cod,nombre,precio from articulo where cod = '00016';

Las condiciones pueden complicarse, podrían combinarse varias comparaciones. Necesitamos algo de álgebra de boole y de lógica básicas.

Las tablas de verdad del AND y el OR nos dicen qué podemos esperar de su uso.

Cuando deben cumplirse 2 condiciones hablamos de AND: A y B solo es verdadero si ambas A y B se evalúan a cierto:

-- tablas de verdad

select true,false;

select 0 A,0 B,0 and 0 "A and B"

  union select 0,1,0 and 1

  union select 1,0,1 and 0

  union select 1,1,1 and 1;

Cuando basta con que alguna se cumpla, es un OR: A o B solo es falso cuando ambas son falsas.:

select 0 A,0 B,0 or 0 "A or B"

  union select 0,1,0 or 1

  union select 1,0,1 or 0

  union select 1,1,1 or 1;

Cuando queremos seleccionar la filas cuyo precio está dentro de un rango escribiríamos:

select * from articulo where precio >= 10 and precio <= 20 order by precio;

select * from articulo where precio between 10 and 20 order by precio;

Between es un operador lógico totalmente equivalente al "mayor o igual y menor o igual que".

También existen el menor y mayor estrictos. Si en esta primera consulta vemos los precios 11.75 y 12.50:

select * from articulo where precio >= 11.75 and precio <= 12.50 order by precio;

en esta segunda eliminamos los extremos del rango, no nos interesan:

select * from articulo where precio > 11.75 and precio < 12.50  order by precio;

Ahora llegamos a las situaciones difíciles. Si ejecutamos esto, con menor y mayor estrictos ¿qué ha pasado?

select * from articulo where precio > 11.75 or precio < 12.50 order by precio;

Estamos pidiendo filas que tengan precios mayores que 11.75 o menores que 12.50. Recuerda, el "o" lógico se hace cierto en cuanto una de las condiciones es verdadera. Dicho de otra forma, 

Esta condición del where no filtra nada, nos salen todas las filas. 

Seguramente porque lo que queríamos era otro tipo de rangos, aquellas filas cuyo precio sea inferior a 11.75 o superior a 12.50:

select * from articulo where precio < 11.75 or precio > 12.50 order by precio;

Ten en cuenta que esto son ejemplos, las condiciones no son "buenas" o "malas", sean las que sean, son válidas siempre que obtengas lo que quieras. Esto es especialmente delicado cuando se combinan AND y OR. 

La ejecución de esta consulta, curiosamente, no obtiene nada, cero filas:

select * from articulo where cod = '00019' and precio = 9.75;

Estamos exigiendo que se cumplan al mismo tiempo las condiciones de que la fila tenga el código '00019' y el precio 9.75. No existe ninguna fila que cumpla con el AND.

select * from articulo where cod = '00019' or precio = 9.75;

Sin embargo ahora simplemente pedimos que se cumpla una o la otra. Pues sí, me salen 2 filas.

Esta consulta no devuelve nada, cero filas. ¿Entiendes por qué?

select * from articulo where cod = '00019' and cod = '00002';

Efectivamente, cada componente de una fila solo puede contener un único valor. Entonces ¿cómo puede tener una fila los códigos '00019' y '00002' al mismo tiempo? No puede, es imposible. Posiblemente, lo que necesitas es un OR:

select * from articulo where cod = '00019' or cod = '00002';

Esto ya tiene más sentido, quiero las filas etiquetadas con un código o con el otro.

La cosa puede complicarse, y mucho, cuando tenemos que combinar AND y OR. Este resultado obtiene las filas con un rango de precios o que tiene código '00019'. Como lo hemos hecho con un between no hay ambigüedad:

select * from articulo where cod = '00019' or precio between 6.75 and 12.60 order by precio;

Sin embargo, ahora utilizamos la expresión equivalente y tenemos OR y AND combinados:

select * from articulo 

where cod = '00019' or precio >= 6.75 and precio <= 12.60 

order by precio;

Por supuesto, el resultado es el mismo pero ¿sabemos realmente por qué?

Partimos de este resultado que no tiene dificultad. Obtenemos 2 filas:

select * from articulo 

where cod = '00019' or cod = '00004' 

order by precio;

Supongamos ahora que queremos refinar un poco más el resultado y queremos solo la fila con precio 9.70. Nos lanzamos:

select * from articulo 

where cod = '00019' or cod = '00004' and precio = 9.70 

order by precio;

¿Qué ha pasado? Pues que en la mayoría de lenguajes de programación, y también en SQL, hay una cosa que se llama precedencia de operadores. Unos se ejecutan antes que otros. Se supone que ayuda al programa informático a evaluar la expresión y obtener el resultado final. También en todos los lenguajes, los paréntesis rompen ese orden de evaluación. 

Para tenerlo más claro, la consulta anterior es idéntica a ésta:

select * from articulo 

where cod = '00019' or (cod = '00004' and precio = 9.70) 

order by precio;

Primero se evalúa que la fila tenga código '00004' y precio 9.70) y después lo que haya salido OR código igual a '00019'.

A or (B and C) = A or D, siendo D = (B and C).

La segunda fila cumple con el AND. La primera fila hace cierta la parte izquierda del OR. Resumiendo, lo que hemos estado preguntando es "dame el artículo 19, y también el 4 si tiene precio 9.70".

Aquí cambiamos el orden de evaluación: (A or B) and C = D and C, siendo D = (A or B)

select * from articulo where (cod = '00019' or cod = '00004') and precio = 9.70 order by precio;

Más o menos el resultado de esta consulta es "de entre los artículos 4 o 19, el que tenga precio 9.70".

La moraleja de todo este asunto es: no te fíes de la precedencia de operadores, utiliza paréntesis. Tampoco en demasía, no pongas paréntesis inútiles, a MariaDB no le gusta y a tí te "ensucia" la consulta:

select * from articulo where (cod = '00019' or (cod = '00004' and precio = 9.70)) order by precio;

Terminamos. Por ahora, después habrá más.

Hemos dicho que el nulo (null) es un valor especial. De hecho no está claro que lo podamos llamar "valor". Da igual, el caso es que los operadores de comparación habituales no nos valen. Partimos del contenido actual de la tabla TRANSPORTE:

select * from transporte;

Sorprendemente, estas dos consultas no consiguen nada:

select * from transporte where url = null;

select * from transporte where url != null;

El nulo tiene su propio operador de igualdad, y ningún otro. IS o IS NOT, cuando queremos comprobar si algo es o no nulo:

select * from transporte where url IS null;

select * from transporte where url IS NOT null;

3C where, selección de filas.pdf