SQL Lab: filtros¶
Ejercicio sql-lab-filters-annex.pdf · PDF
Alguien te da una caja de zapatos con 949 cartas de Pokémon y te pregunta: ¿cuál es el eléctrico más rápido? No te pones a leer 949 cartas en voz alta. Sacas las eléctricas, las ordenas por velocidad, miras la carta de arriba y lees su nombre.
Cuatro movimientos con las manos. En una base de datos esos cuatro movimientos tienen nombre, y juntos forman una frase que la base de datos entiende. En la lección anterior conectaste Superset a la base de datos de clase y subiste la tabla pokemon. Ahora aprendes a pedirle menos: menos columnas, menos filas, en un orden útil. En el videojuego de crafteo de este módulo sigues en la fase de farmear y lootear, pero un buen jugador no se lleva el mapa entero a casa, solo lo que necesita la receta.
Qué aprenderás
- Escribir una consulta que devuelva solo las columnas que necesitas, en lugar de
SELECT * - Quedarte solo con las filas que necesitas con
WHERE, usando operadores de comparación yAND/OR - Ordenar un resultado con
ORDER BYy recortarlo conLIMITpara responder preguntas de "top N" - Listar los valores distintos de una columna con
DISTINCT - Elegir el filtro especial adecuado:
LIKE,IN,BETWEENoIS NULL - Desactivar la trampa del día: por qué
= NULLno encuentra nada, y por qué una consulta incorrecta casi nunca da error
Una caja de zapatos con 949 cartas¶
Una consulta es una pregunta escrita en un lenguaje que la base de datos entiende. Ese lenguaje es SQL (Structured Query Language).
Una analogía para toda esta página: la tabla pokemon es una caja de zapatos con cartas coleccionables.
| En la base de datos | En la caja de zapatos |
|---|---|
La tabla pokemon |
La caja |
| Una fila | Una carta: un Pokémon |
| Una columna | Una línea impresa en todas las cartas: nombre, tipo, ataque... |
| Una consulta | Lo que haces con las manos para responder una pregunta |
La caja contiene 949 cartas, y cada carta tiene 22 líneas impresas. En esta lección usamos ocho de ellas:
| Columna | Qué contiene | Ejemplo (charizard) |
|---|---|---|
pokemon |
El nombre, en minúsculas | charizard |
type_1 |
Primer tipo | fire |
type_2 |
Segundo tipo, en blanco si no tiene | flying |
hp, attack, speed |
Estadísticas base, números enteros | 78, 84, 100 |
weight |
Peso en kilogramos | 90.5 |
generation_id |
Número de generación, de 1 a 7, en blanco en algunos | 1 |
Abre SQL → SQL Lab en Superset, elige la base de datos Dades classe y el esquema public, y ya puedes escribir. Ejecutas una consulta con el botón Run o con Ctrl+Enter.
Por qué SQL parece inglés
En 1974, Donald Chamberlin y Raymond Boyce, dos investigadores de IBM en San José (California), publicaron un lenguaje para hacer preguntas a las bases de datos. Querían que personas que no eran programadoras pudieran leerlo, así que lo llamaron SEQUEL: Structured English Query Language. El nombre ya era una marca registrada de una empresa de aviones, así que perdió las vocales y se quedó en SQL. Más de cincuenta años después sigues escribiendo SELECT, FROM y WHERE exactamente como ellos.
La forma de una consulta¶
Una consulta tiene hasta cinco líneas, siempre en este orden. Cada una es un movimiento de tus manos.
| Línea | Con la caja de zapatos | Regla |
|---|---|---|
SELECT |
Qué líneas de la carta lees | Nombres de columnas, separados por comas. * significa todas. |
FROM |
Qué caja | La tabla. |
WHERE |
Qué cartas sacas | Una condición que es verdadera o falsa para cada fila. |
ORDER BY |
Cómo ordenas el montón | ASC (primero lo pequeño, el valor por defecto) o DESC (primero lo grande). |
LIMIT |
Cuántas cartas te quedas desde arriba | Un número. Siempre al final. |
flowchart LR
A["<b>FROM</b><br>abre la caja:<br>949 cartas"] --> B["<b>WHERE</b><br>saca las cartas<br>que quieres"]
B --> C["<b>ORDER BY</b><br>ordena el montón"]
C --> D["<b>LIMIT</b><br>quédate con las<br>de arriba"]
D --> E["<b>SELECT</b><br>lee solo las líneas<br>que has pedido"]
Esta es la pregunta del principio de la página, escrita como consulta:
SELECT pokemon, speed
FROM pokemon
WHERE type_1 = 'electric'
ORDER BY speed DESC
LIMIT 1;
| pokemon | speed |
|---|---|
| electrode | 150 |
Una fila, dos columnas. La respuesta, y nada más. Las siguientes secciones construyen esa frase línea a línea.
SELECT: lee solo las líneas que necesitas¶
SELECTelige las columnas del resultado.FROMnombra la tabla de la que vienen.
SELECT * FROM pokemon te entrega las 22 columnas de las 949 filas. Sirve para echar un primer vistazo, y no sirve como respuesta. Nombra las columnas en su lugar:
SELECT pokemon, type_1, attack FROM pokemon;
Sigue devolviendo 949 filas, pero solo tres columnas. Las cuatro primeras:
| pokemon | type_1 | attack |
|---|---|---|
| bulbasaur | grass | 49 |
| ivysaur | grass | 62 |
| venusaur | grass | 82 |
| charmander | fire | 52 |
Cuidado: una coma que falta no da error
Escribe SELECT pokemon type_1 FROM pokemon (sin coma) y la consulta se ejecuta. Pero obtienes una columna, llamada type_1, llena de nombres. Sin la coma, SQL lee la segunda palabra como un nuevo nombre para la primera columna. Si falta una columna que has pedido, cuenta las comas.
WHERE: saca cartas de la caja¶
WHEREse queda solo con las filas para las que una condición es verdadera. Una condición es una pregunta de sí o no que se hace a cada fila, una por una.
SELECT pokemon, type_1, attack FROM pokemon WHERE type_1 = 'fire';
Esto devuelve 59 filas: charmander, charmeleon, charizard, vulpix... La condición type_1 = 'fire' se preguntó 949 veces, y 59 cartas dijeron que sí.
El = es un operador de comparación. Hay seis:
| Operador | Significado | Ejemplo | Filas |
|---|---|---|---|
= |
igual a | type_1 = 'fire' |
59 |
<> |
distinto de | type_1 <> 'fire' |
890 |
> |
mayor que | attack > 150 |
19 |
>= |
mayor o igual que | attack >= 150 |
30 |
< |
menor que | speed < 20 |
17 |
<= |
menor o igual que | speed <= 20 |
33 |
Dos reglas causan la mayoría de los errores de la primera semana de SQL:
| Regla | Bien | Mal | Qué pasa |
|---|---|---|---|
| El texto va entre comillas simples, los números no | type_1 = 'fire', attack > 150 |
type_1 = fire |
Un error: la base de datos cree que fire es una columna. SQL Lab dice We can't seem to resolve the column "fire". |
| El texto distingue mayúsculas de minúsculas | 'fire' |
'Fire' |
Ningún error, y 0 filas. |
Cuidado: cero filas no siempre significa 'no hay ninguna'
WHERE type_1 = 'Fire' no devuelve nada porque ninguna carta dice Fire con F mayúscula. La base de datos no se quejó: respondió a la pregunta que le hiciste. Antes de creerte un resultado vacío, revisa la ortografía. Y las comillas dobles no son una salida: en PostgreSQL "fire" significa un nombre de columna, 'fire' significa texto.
Dos condiciones: AND, OR¶
Un operador lógico une dos condiciones.
AND: las dos deben ser verdaderas.OR: basta con una.
SELECT pokemon, type_1, type_2 FROM pokemon
WHERE type_1 = 'dragon' AND type_2 = 'flying';
Seis filas: dragonite, altaria, salamence, rayquaza y las formas Mega de los dos últimos. Cambia AND por OR y pregunta algo más amplio:
SELECT pokemon, type_1, type_2 FROM pokemon
WHERE type_1 = 'dragon' OR type_2 = 'dragon';
61 filas: todos los Pokémon que son dragón en cualquiera de las dos casillas. AND hace el resultado más pequeño, OR lo hace más grande.
ORDER BY y LIMIT: ordena el montón, quédate con lo de arriba¶
ORDER BYordena el resultado por una columna.LIMITse queda solo con las primeras filas.
SELECT pokemon, type_1, attack FROM pokemon
WHERE attack > 150
ORDER BY attack DESC;
19 filas, las más fuertes primero. Juntas, estas dos líneas responden a cualquier pregunta de "top N":
SELECT pokemon, attack FROM pokemon ORDER BY attack DESC LIMIT 5;
| pokemon | attack |
|---|---|
| mewtwo-mega-x | 190 |
| heracross-mega | 185 |
| kartana | 181 |
| deoxys-attack | 180 |
| groudon-primal | 180 |
Fíjate en las dos últimas filas. Tres Pokémon tienen un ataque de 180: deoxys-attack, groudon-primal y rayquaza-mega. Solo caben dos en un top 5, y la base de datos eligió como quiso. Tu pantalla puede mostrar otra pareja, y los dos tendríais razón.
Cuidado: LIMIT corta los empates de forma arbitraria
Cuando varias filas comparten el valor por el que ordenas, su orden no está definido. Para decidirlo tú, añade una segunda columna de ordenación: ORDER BY attack DESC, pokemon ordena los empates por nombre. Y recuerda que LIMIT 5 solo, sin ORDER BY, te da cinco filas cualesquiera, no las cinco mejores.
DISTINCT: conocer una columna¶
DISTINCTelimina las filas repetidas del resultado, de modo que cada valor distinto aparece una sola vez.
Antes de filtrar por una columna, averigua qué valores puede tomar:
SELECT DISTINCT type_1 FROM pokemon ORDER BY type_1;
18 filas, de bug a water: los 18 tipos, cada uno una vez. Ahora sabes que el tipo se escribe fire, no Fire ni FIRE. DISTINCT es la cura para la trampa de las cero filas de más arriba.
Cuatro filtros especiales¶
Los operadores de comparación cubren los números y el texto exacto. Otros cuatro filtros cubren el resto.
| Filtro | Con la caja de zapatos | Ejemplo | Filas |
|---|---|---|---|
LIKE |
Cartas cuyo texto se parece a un patrón | pokemon LIKE 'pika%' |
13 |
IN |
Cartas cuyo valor está en mi lista | type_1 IN ('fire', 'water', 'grass') |
269 |
BETWEEN |
Cartas dentro de un rango, con los dos extremos incluidos | speed BETWEEN 100 AND 120 |
126 |
IS NULL |
Cartas donde esa línea está en blanco | type_2 IS NULL |
439 |
LIKE usa dos comodines, símbolos que representan texto que no conoces: % es cualquier texto (también ninguno), _ es exactamente un carácter. 'pika%' significa "empieza por pika": pikachu y sus doce disfraces (pikachu-rock-star, pikachu-phd...).
IN es una forma corta de varios OR: type_1 = 'fire' OR type_1 = 'water' OR type_1 = 'grass'. Pon NOT delante para lo contrario: type_1 NOT IN ('fire', 'water', 'grass') devuelve 680 filas, y 269 + 680 = 949. Nadie se ha perdido.
BETWEEN incluye los dos extremos: speed BETWEEN 100 AND 120 es lo mismo que speed >= 100 AND speed <= 120. Los dos devuelven 126. Con > y < obtendrías 83, porque los Pokémon con exactamente 100 y exactamente 120 quedan fuera.
IS NULL encuentra los blancos: los 439 Pokémon con un solo tipo. IS NOT NULL encuentra los otros 510. Este merece su propia sección.
NULL: la línea en blanco de la carta¶
NULLsignifica desconocido: aquí no hay nada escrito. No es cero, y no es un texto vacío.
Ejecuta esto y cuenta:
SELECT pokemon, type_1, type_2 FROM pokemon WHERE type_2 = NULL;
0 filas. Ningún error. ¿Han desaparecido los 439 Pokémon de un solo tipo?
No. Piensa en dos cartas con una línea en blanco. ¿Los dos blancos son iguales? No puedes saberlo: puede que ahí vaya algo, y no sabes qué. Así que cuando comparas cualquier cosa con NULL, la base de datos responde "desconocido", y WHERE solo conserva las filas donde la respuesta es un sí claro.
| Escribes | La base de datos responde | Filas |
|---|---|---|
type_2 = NULL |
"desconocido" para cada fila | 0 |
type_2 = '' |
"no": un blanco tampoco es un texto vacío | 0 |
type_2 IS NULL |
"sí" para las que están en blanco | 439 |
type_2 IS NOT NULL |
"sí" para las que están rellenas | 510 |
Por eso SQL tiene una pregunta aparte para los blancos. Recuérdalo: IS NULL, nunca = NULL.
Respuestas incorrectas que parecen correctas¶
Todas las trampas de esta página tienen algo en común: ninguna da error. La base de datos hace exactamente lo que escribiste y devuelve un número creíble.
| Trampa | Qué obtienes | Lección |
|---|---|---|
LIKE '%mega%' para contar las evoluciones Mega |
50 en lugar de 48: se cuelan meganium y yanmega |
Un patrón coincide con texto, no con significado. Usa '%-mega%' y mira las filas, no solo el recuento. |
= NULL |
0 filas | Los blancos necesitan IS NULL. |
Mezclar AND y OR sin paréntesis |
79 en lugar de 34 | AND se aplica primero, como × antes que +. Usa paréntesis. |
LIMIT con un empate |
Un top 5 distinto al de tu compañero | Añade una segunda columna de ordenación. |
La tercera, con detalle. Quieres los Pokémon de fuego o agua con un ataque superior a 100:
SELECT COUNT(*) FROM pokemon
WHERE type_1 = 'fire' OR type_1 = 'water' AND attack > 100;
79. La base de datos lo leyó como "todos los Pokémon de fuego (59), más los de agua con ataque superior a 100 (20)". Añade paréntesis alrededor de la parte del OR:
SELECT COUNT(*) FROM pokemon
WHERE (type_1 = 'fire' OR type_1 = 'water') AND attack > 100;
34. Las mismas palabras, otra pregunta. (COUNT(*) cuenta filas; abre la siguiente lección.)
Pruébalo: la caza de la Pokédex¶
Abre SQL Lab y responde a cada pregunta con una consulta. Escribe primero la consulta, luego abre la solución y compara tu número.
| N. | Pregunta |
|---|---|
| 1 | Nombre y peso de los tres Pokémon más pesados. |
| 2 | ¿Cuántas evoluciones Mega hay? Su nombre contiene -mega. |
| 3 | ¿Cuántos Pokémon no tienen generación (generation_id)? |
| 4 | Eléctricos, de un solo tipo, con velocidad de 100 a 120: nombres, el más rápido primero. |
| 5 | Nombre y ataque de los Pokémon de un solo tipo con ataque superior a 150, el más fuerte primero. |
Soluciones
1 — SELECT pokemon, weight FROM pokemon ORDER BY weight DESC LIMIT 3; → celesteela y cosmoem (999.9), y después groudon-primal (999.7).
2 — SELECT COUNT(*) FROM pokemon WHERE pokemon LIKE '%-mega%'; → 48.
3 — SELECT COUNT(*) FROM pokemon WHERE generation_id IS NULL; → 147. Ten ese número en la cabeza: vuelve en las dos lecciones siguientes.
4 — SELECT pokemon, speed FROM pokemon WHERE type_1 = 'electric' AND type_2 IS NULL AND speed BETWEEN 100 AND 120 ORDER BY speed DESC; → 6 filas, desde zebstrika (116) hasta voltorb (100).
5 — SELECT pokemon, attack FROM pokemon WHERE type_2 IS NULL AND attack > 150 ORDER BY attack DESC; → 5 filas, la primera deoxys-attack (180).
Si SQL Lab responde con un cuadro rojo, lee el mensaje: normalmente nombra la palabra con la que tropezó. Mira justo antes de esa palabra por si falta una comilla o hay una palabra clave mal escrita. Y ejecuta una consulta cada vez: con varias en el editor, solo ves el resultado de la última.
Ideas clave¶
- Una consulta tiene una forma fija:
SELECT(columnas) ·FROM(tabla) ·WHERE(filas) ·ORDER BY(orden) ·LIMIT(recorte). WHEREhace una condición a cada fila. El texto va entre comillas simples y distingue mayúsculas de minúsculas; los números no llevan comillas.ANDestrecha,ORamplía.ANDse aplica primero: cuando los mezcles, usa paréntesis.ORDER BY ... DESC LIMIT nresponde a "top N", peroLIMITcorta los empates de forma arbitraria.DISTINCTmuestra los valores distintos de una columna: úsalo antes de filtrar.LIKEbusca un patrón,INuna lista,BETWEENun rango con los dos extremos,IS NULLun blanco.= NULLnunca es verdadero.- Los errores peligrosos no dan error. Devuelven un número que parece correcto.
Comprobación rápida¶
1. ¿Qué consulta devuelve solo el nombre y el ataque de cada Pokémon?
-
SELECT * FROM pokemon; -
SELECT pokemon, attack FROM pokemon; -
SELECT pokemon attack FROM pokemon; -
FROM pokemon SELECT pokemon, attack;
SELECT lista las columnas, separadas por comas, y va antes de FROM. * devuelve las 22 columnas. Sin la coma, la consulta se ejecuta pero renombra la primera columna: una columna en lugar de dos.
2. WHERE type_1 = 'Fire' devuelve 0 filas y ningún error. ¿Cuál es la razón más probable?
- La tabla no tiene Pokémon de fuego
- El texto debe ir entre comillas dobles
- El texto distingue mayúsculas de minúsculas, y la tabla guarda
fireen minúsculas -
WHEREno puede comparar texto
La base de datos comparó cada carta con Fire y ninguna coincidió, porque el texto distingue mayúsculas de minúsculas. Cero filas no prueba que "no hay ninguna": ejecuta SELECT DISTINCT type_1 para ver cómo están escritos realmente los valores.
3. ¿Qué devuelve SELECT pokemon, speed FROM pokemon ORDER BY speed DESC LIMIT 3;?
- Tres Pokémon cualesquiera
- Los tres Pokémon más lentos
- Los tres Pokémon más rápidos
- Todos los Pokémon con una velocidad superior a 3
ORDER BY speed DESC ordena el montón con la mayor velocidad arriba, y LIMIT 3 se queda con las tres primeras cartas. Sin ORDER BY, LIMIT 3 daría tres filas cualesquiera.
4. ¿Por qué WHERE type_2 = NULL devuelve 0 filas?
- Porque todos los Pokémon tienen un segundo tipo
- Porque
NULLdebe escribirse entre comillas - Porque NULL significa desconocido, así que una comparación con él nunca es un sí claro
- Porque
=solo funciona con números
NULL no es un valor: significa "aquí no hay nada escrito". Compararlo da "desconocido", y WHERE solo conserva las filas que responden que sí. La pregunta correcta es type_2 IS NULL, que encuentra 439 filas.
5. Cuentas las evoluciones Mega con LIKE '%mega%' y obtienes 50. El número real es 48. ¿Qué ha pasado?
-
LIKEignora la primera y la última letra de un nombre - El patrón también coincide con
meganiumyyanmega, que no son evoluciones Mega -
%solo coincide con un carácter - Dos evoluciones Mega están guardadas dos veces
Un patrón coincide con texto, no con significado. %mega% significa "contiene mega en cualquier sitio". Las evoluciones Mega se escriben con un guion, así que '%-mega%' devuelve los 48 correctos. Mira siempre las filas, no solo el recuento.
6. WHERE type_1 = 'fire' OR type_1 = 'water' AND attack > 100 devuelve 79 filas. Con paréntesis alrededor de la parte del OR devuelve 34. ¿Por qué?
- Los paréntesis hacen la consulta más rápida, así que llegan menos filas
-
ORse aplica primero, así que la primera versión ignora el ataque -
ANDse aplica primero: sin paréntesis la condición del ataque solo vale para agua - La primera versión cuenta algunos Pokémon dos veces
AND se aplica antes que OR, como × antes que +. Sin paréntesis obtienes los 59 Pokémon de fuego más los 20 de agua fuertes. Con paréntesis, la condición del ataque vale para los dos tipos: 34.
7. Una tabla songs tiene las columnas title, artist, year y streams. Elige las dos consultas que devuelven correctamente las cinco canciones más reproducidas de 2020 a 2023.
-
SELECT title, streams FROM songs WHERE year BETWEEN 2020 AND 2023 ORDER BY streams DESC LIMIT 5; -
SELECT title, streams FROM songs WHERE year >= 2020 AND year <= 2023 ORDER BY streams DESC LIMIT 5; -
SELECT title, streams FROM songs WHERE year BETWEEN 2020 AND 2023 LIMIT 5; -
SELECT title, streams FROM songs WHERE year > 2020 AND year < 2023 ORDER BY streams DESC LIMIT 5;
BETWEEN incluye los dos extremos, así que equivale a >= y <=. La tercera consulta no tiene ORDER BY: devuelve cinco canciones cualesquiera. La cuarta usa > y <, que descarta sin avisar 2020 y 2023.
Hasta ahora cada respuesta era una lista de cartas. ¿Y si la pregunta es "¿cuántos Pokémon de fuego hay?", y luego lo mismo para los 18 tipos a la vez? Siguiente: SQL Lab: grupos.