La solicitud

De Megan Collins, Directora de Ventas
Tu objetivo
Desde orders, devuelve customer_id y completed_orders (número de pedidos con status 'completed') de los clientes con al menos 30 pedidos completados.
- Lee de la tabla
orderspendiente - Muestra exactamente
customer_idcompleted_orderspendiente - 49 filaspendiente
Tu consulta
Cargando el editor…
Solución de referencia
| order_id | customer_id | order_date | status | channel |
|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Lo que recibe Megan, y por qué
Tablas
Datos de la tienda Harbor & Pine, listos para consultar.
orders12,000 filas · 7 columnas
order_idintegercustomer_idintegerorder_datetimestampstatusvarcharchannelvarcharshipping_feedecimalcoupon_codevarchar
| order_id | customer_id | order_date | status | channel | shipping_fee | coupon_code |
|---|---|---|---|---|---|---|
| 1001 | 125 | 2023-01-01 20:51:19 | completed | web | 0.00 | NULL |
| 1002 | 156 | 2023-01-02 07:58:46 | completed | web | 0.00 | NULL |
| 1003 | 22 | 2023-01-02 10:25:27 | completed | marketplace | 4.99 | WELCOME10 |
| 1004 | 48 | 2023-01-02 11:21:51 | completed | marketplace | 4.99 | WELCOME10 |
| 1005 | 82 | 2023-01-02 11:43:36 | completed | web | 0.00 | WELCOME10 |
customers2,018 filas · 9 columnas
customer_idintegerfirst_namevarcharlast_namevarcharemailvarcharcityvarcharstatevarcharsignup_datedatesegmentvarcharreferral_sourcevarchar
| customer_id | first_name | last_name | city | state | signup_date | segment | referral_source | |
|---|---|---|---|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco | CA | 2022-06-02 | consumer | search |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta | GA | 2022-06-02 | consumer | NULL |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles | CA | 2022-06-04 | business | NULL |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle | WA | 2022-06-06 | business | |
| 5 | Kyle | Jimenez | kjimenez27@yahoo.com | Indianapolis | IN | 2022-06-07 | consumer | NULL |
products120 filas · 8 columnas
product_idintegernamevarcharcategoryvarcharsubcategoryvarcharunit_pricedecimalunit_costdecimallaunched_ondateis_activeboolean
| product_id | name | category | subcategory | unit_price | unit_cost | launched_on | is_active |
|---|---|---|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 | NULL | 2023-09-11 | true |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 | 503.50 | 2024-05-16 | true |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 | 429.87 | 2023-05-27 | true |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 | 214.70 | 2022-12-20 | true |
| 105 | Avery Leather Bed Frame | Furniture | Beds | 449.00 | 232.70 | 2021-08-31 | true |
order_items30,338 filas · 6 columnas
order_item_idintegerorder_idintegerproduct_idintegerquantityintegerunit_pricedecimaldiscount_pctdecimal
| order_item_id | order_id | product_id | quantity | unit_price | discount_pct |
|---|---|---|---|---|---|
| 1 | 1001 | 121 | 9 | 31.99 | 0.00 |
| 2 | 1001 | 154 | 3 | 11.99 | 0.00 |
| 3 | 1002 | 154 | 4 | 11.99 | 0.00 |
| 4 | 1002 | 194 | 1 | 149.00 | 0.00 |
| 5 | 1002 | 193 | 2 | 19.99 | 0.00 |
returns1,673 filas · 5 columnas
return_idintegerorder_item_idintegerreturned_ondatereasonvarcharrefund_amountdecimal
| return_id | order_item_id | returned_on | reason | refund_amount |
|---|---|---|---|---|
| 1 | 14 | 2023-01-10 | damaged | 83.69 |
| 2 | 42 | 2023-01-10 | wrong_item | 19.99 |
| 3 | 105 | 2023-01-14 | wrong_item | 10.99 |
| 4 | 24 | 2023-01-16 | changed_mind | 26.99 |
| 5 | 126 | 2023-01-16 | late | 20.99 |
employees60 filas · 8 columnas
employee_idintegerfirst_namevarcharlast_namevarchardepartmentvarchartitlevarcharmanager_idintegerhire_datedatesalaryinteger
| employee_id | first_name | last_name | department | title | manager_id | hire_date | salary |
|---|---|---|---|---|---|---|---|
| 1 | Catherine | Hale | Executive | Chief Executive Officer | NULL | 2016-03-01 | 285000 |
| 2 | Robert | Kim | Executive | Chief Operating Officer | 1 | 2017-01-09 | 235000 |
| 3 | Megan | Collins | Sales | Head of Sales | 1 | 2018-04-16 | 172000 |
| 4 | Chloe | Gutierrez | Sales | Account Executive | 3 | 2020-01-21 | 72000 |
| 5 | Frank | Bell | Sales | Account Executive | 3 | 2023-05-21 | 73000 |
web_sessions40,000 filas · 7 columnas
session_idintegercustomer_idintegerstarted_attimestampdevicevarcharsourcevarcharpages_viewedintegerconvertedboolean
| session_id | customer_id | started_at | device | source | pages_viewed | converted |
|---|---|---|---|---|---|---|
| 1 | NULL | 2023-01-01 23:02:24 | mobile | paid | 1 | false |
| 2 | 18 | 2023-01-01 15:07:49 | desktop | paid | 1 | false |
| 3 | NULL | 2023-01-01 16:55:46 | desktop | direct | 7 | false |
| 4 | NULL | 2023-01-01 12:43:28 | mobile | organic | 3 | false |
| 5 | NULL | 2023-01-01 13:06:21 | mobile | social | 2 | false |
marketing_spend3,276 filas · 3 columnas
spend_datedatechannelvarcharamountdecimal
| spend_date | channel | amount |
|---|---|---|
| 2023-01-01 | paid | 492.99 |
| 2023-01-01 | 37.38 | |
| 2023-01-01 | social | 200.53 |
| 2023-01-02 | paid | 709.13 |
| 2023-01-02 | 64.13 |
La lección
WHERE filtra filas antes de agruparlas. HAVING filtra grupos después de la agregación.
Esta consulta usa ambos:
SELECT customer_id, COUNT(*) AS completed_orders
FROM orders
WHERE status = 'completed' -- which orders count
GROUP BY customer_id
HAVING COUNT(*) >= 30; -- which customers qualify
Orden lógico de una consulta
La base de datos no ejecuta tus cláusulas en el orden en que las escribes. Lógicamente sigue:
FROM(y los joins)WHERE, sobre filas individualesGROUP BY- se calculan los agregados
HAVING, sobre gruposSELECT, incluidos los aliasORDER BYLIMIT
Esto explica tres reglas a la vez:
- No puedes usar
COUNT(*)enWHERE, porque los conteos todavía no existen en el paso 2. HAVINGpuede usar agregados, porque existen en el paso 5.- Los alias de SELECT normalmente no son visibles en
WHEREni enHAVING, pero sí enORDER BY. Postgres exigeHAVING COUNT(*) >= 30, noHAVING completed_orders >= 30. DuckDB, MySQL y BigQuery aceptan el alias, así que repite el agregado si quieres SQL portable.
Pon las condiciones de fila en WHERE
Podrías escribir el filtro de status en HAVING con agregación condicional, pero una condición de fila simple
pertenece a WHERE. Es más claro y elimina filas antes del costoso paso de agrupación. Una condición
sobre una columna agrupada, como HAVING channel = 'app', funciona pero se escribe mejor como
WHERE channel = 'app'. Usa HAVING solo para condiciones que necesiten un agregado.
WHERE cambia lo que ve HAVING
Estas dos preguntas suenan parecido y dan respuestas distintas:
- “Clientes con 30+ pedidos, contando solo los completados” (WHERE status, luego HAVING del conteo).
- “Clientes con 30+ pedidos de cualquier estado, mostrando su conteo de completados” (sin WHERE; la condición cuenta todos los pedidos, la columna cuenta solo los completados).
Lee el ticket dos veces y decide qué filtro se aplica antes o después de agrupar. Megan pidió el primero: los pedidos cancelados y reembolsados no cuentan para la lealtad.
Nota de entrevista
“¿Cuál es la diferencia entre WHERE y HAVING?” se pregunta en casi todas las entrevistas de analista. Una buena respuesta es una frase más una razón: WHERE filtra filas antes de agrupar, HAVING filtra grupos después de la agregación, porque los agregados no existen hasta el paso de agrupación. Mencionar que WHERE también es más barato, porque reduce los datos antes de agrupar, demuestra que piensas en el rendimiento.
Errores comunes
- Poner el conteo en WHERE.
WHERE COUNT(*) >= 30falla, porque los conteos todavía no existen cuando WHERE se ejecuta. Muévelo aHAVING COUNT(*) >= 30. - Mover el filtro de status a HAVING.
HAVING status = 'completed'falla porquestatusno está agrupado, y aunque se ejecute es la cláusula equivocada. Las condiciones de fila van en WHERE. - Usar el alias en HAVING.
HAVING completed_orders >= 30funciona en DuckDB pero falla en Postgres y SQL Server. Repite el agregado. - Usar
>cuando el ticket dice “30 o más”.HAVING COUNT(*) > 30descarta en silencio a todos los clientes con exactamente 30 pedidos.
En SQL Server
HAVING y el orden lógico son idénticos. SQL Server nunca acepta un alias de SELECT en HAVING, así que
escribe HAVING COUNT(*) >= 30, no HAVING completed_orders >= 30, y usa TOP en lugar de LIMIT
si recortas la lista.
¡Linterna encendida!
Practicaste: havingwhere vs havinggroup by
- Resuelto
- Sin pistas
- Al primer intento
