1. Faro

Nivel 4 · Nuestros clientes más leales

La solicitud

De Megan Collins, Directora de Ventas

“Queremos enviar un regalo de agradecimiento a nuestros compradores más leales. ¿Quién tiene al menos 30 pedidos completados con nosotros? Los cancelados y reembolsados no cuentan.”

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_id completed_orderspendiente
  • 49 filaspendiente

Tu consulta

SQL

Cargando el editor…

    ResultadoVista previa de orders · presiona Ejecutar para ver el tuyo
    Vista previa de orders
    order_idcustomer_idorder_datestatuschannel
    10011252023-01-01 20:51:19completedweb
    10021562023-01-02 07:58:46completedweb
    1003222023-01-02 10:25:27completedmarketplace
    1004482023-01-02 11:21:51completedmarketplace

    Mote revisa tu resultado

    Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.

    Tablas

    Datos de la tienda Harbor & Pine, listos para consultar.

    orders12,000 filas · 7 columnas
    • order_idinteger
    • customer_idinteger
    • order_datetimestamp
    • statusvarchar
    • channelvarchar
    • shipping_feedecimal
    • coupon_codevarchar
    Primeras 5 filas de orders
    order_idcustomer_idorder_datestatuschannelshipping_feecoupon_code
    10011252023-01-01 20:51:19completedweb0.00NULL
    10021562023-01-02 07:58:46completedweb0.00NULL
    1003222023-01-02 10:25:27completedmarketplace4.99WELCOME10
    1004482023-01-02 11:21:51completedmarketplace4.99WELCOME10
    1005822023-01-02 11:43:36completedweb0.00WELCOME10
    customers2,018 filas · 9 columnas
    • customer_idinteger
    • first_namevarchar
    • last_namevarchar
    • emailvarchar
    • cityvarchar
    • statevarchar
    • signup_datedate
    • segmentvarchar
    • referral_sourcevarchar
    Primeras 5 filas de customers
    customer_idfirst_namelast_nameemailcitystatesignup_datesegmentreferral_source
    1OwenHudsonowen.hudson@icloud.comSan FranciscoCA2022-06-02consumersearch
    2DouglasMendozadmendoza69@gmail.comAtlantaGA2022-06-02consumerNULL
    3HenryBrookshbrooks@lakesidecafe.comLos AngelesCA2022-06-04businessNULL
    4BrianGrahambgraham@bluecedarhomes.comSeattleWA2022-06-06businesspinterest
    5KyleJimenezkjimenez27@yahoo.comIndianapolisIN2022-06-07consumerNULL
    products120 filas · 8 columnas
    • product_idinteger
    • namevarchar
    • categoryvarchar
    • subcategoryvarchar
    • unit_pricedecimal
    • unit_costdecimal
    • launched_ondate
    • is_activeboolean
    Primeras 5 filas de products
    product_idnamecategorysubcategoryunit_priceunit_costlaunched_onis_active
    101Vale Velvet LoveseatFurnitureSofas1659.00NULL2023-09-11true
    102Alder Oak Coffee TableFurnitureTables919.00503.502024-05-16true
    103Orchard Rattan Dining ChairFurnitureChairs789.00429.872023-05-27true
    104Upland Oak BookcaseFurnitureStorage429.00214.702022-12-20true
    105Avery Leather Bed FrameFurnitureBeds449.00232.702021-08-31true
    order_items30,338 filas · 6 columnas
    • order_item_idinteger
    • order_idinteger
    • product_idinteger
    • quantityinteger
    • unit_pricedecimal
    • discount_pctdecimal
    Primeras 5 filas de order_items
    order_item_idorder_idproduct_idquantityunit_pricediscount_pct
    11001121931.990.00
    21001154311.990.00
    31002154411.990.00
    410021941149.000.00
    51002193219.990.00
    returns1,673 filas · 5 columnas
    • return_idinteger
    • order_item_idinteger
    • returned_ondate
    • reasonvarchar
    • refund_amountdecimal
    Primeras 5 filas de returns
    return_idorder_item_idreturned_onreasonrefund_amount
    1142023-01-10damaged83.69
    2422023-01-10wrong_item19.99
    31052023-01-14wrong_item10.99
    4242023-01-16changed_mind26.99
    51262023-01-16late20.99
    employees60 filas · 8 columnas
    • employee_idinteger
    • first_namevarchar
    • last_namevarchar
    • departmentvarchar
    • titlevarchar
    • manager_idinteger
    • hire_datedate
    • salaryinteger
    Primeras 5 filas de employees
    employee_idfirst_namelast_namedepartmenttitlemanager_idhire_datesalary
    1CatherineHaleExecutiveChief Executive OfficerNULL2016-03-01285000
    2RobertKimExecutiveChief Operating Officer12017-01-09235000
    3MeganCollinsSalesHead of Sales12018-04-16172000
    4ChloeGutierrezSalesAccount Executive32020-01-2172000
    5FrankBellSalesAccount Executive32023-05-2173000
    web_sessions40,000 filas · 7 columnas
    • session_idinteger
    • customer_idinteger
    • started_attimestamp
    • devicevarchar
    • sourcevarchar
    • pages_viewedinteger
    • convertedboolean
    Primeras 5 filas de web_sessions
    session_idcustomer_idstarted_atdevicesourcepages_viewedconverted
    1NULL2023-01-01 23:02:24mobilepaid1false
    2182023-01-01 15:07:49desktoppaid1false
    3NULL2023-01-01 16:55:46desktopdirect7false
    4NULL2023-01-01 12:43:28mobileorganic3false
    5NULL2023-01-01 13:06:21mobilesocial2false
    marketing_spend3,276 filas · 3 columnas
    • spend_datedate
    • channelvarchar
    • amountdecimal
    Primeras 5 filas de marketing_spend
    spend_datechannelamount
    2023-01-01paid492.99
    2023-01-01email37.38
    2023-01-01social200.53
    2023-01-02paid709.13
    2023-01-02email64.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:

    1. FROM (y los joins)
    2. WHERE, sobre filas individuales
    3. GROUP BY
    4. se calculan los agregados
    5. HAVING, sobre grupos
    6. SELECT, incluidos los alias
    7. ORDER BY
    8. LIMIT

    Esto explica tres reglas a la vez:

    • No puedes usar COUNT(*) en WHERE, porque los conteos todavía no existen en el paso 2.
    • HAVING puede usar agregados, porque existen en el paso 5.
    • Los alias de SELECT normalmente no son visibles en WHERE ni en HAVING, pero sí en ORDER BY. Postgres exige HAVING COUNT(*) >= 30, no HAVING 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(*) >= 30 falla, porque los conteos todavía no existen cuando WHERE se ejecuta. Muévelo a HAVING COUNT(*) >= 30.
    • Mover el filtro de status a HAVING. HAVING status = 'completed' falla porque status no 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 >= 30 funciona en DuckDB pero falla en Postgres y SQL Server. Repite el agregado.
    • Usar > cuando el ticket dice “30 o más”. HAVING COUNT(*) > 30 descarta 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.

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