1. Faro

Nivel 7 · Los más vendidos que rara vez se rebajan

La solicitud

De Thomas Whitaker, Director de Merchandising

“Quiero proteger a los productos que se venden a precio completo en el plan de promociones del próximo año. Encuéntrame los productos que venden mucho, al menos 500 líneas de pedido, pero cuyo descuento promedio es menor al 2 %.”

Tu objetivo

Desde order_items, considerando solo las filas con quantity > 0, devuelve una fila por producto con: product_id, lines (número de líneas de pedido), units (suma de quantity) y avg_discount_pct (promedio de discount_pct, redondeado a 2). Conserva solo los productos con al menos 500 líneas y un discount_pct promedio menor a 2.

  • Lee de la tabla order_itemspendiente
  • Muestra exactamente product_id lines units avg_discount_pctpendiente
  • 6 filaspendiente

Tu consulta

SQL

Cargando el editor…

    ResultadoVista previa de order_items · presiona Ejecutar para ver el tuyo
    Vista previa de order_items
    order_item_idorder_idproduct_idquantityunit_price
    11001121931.99
    21001154311.99
    31002154411.99
    410021941149.00

    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.

    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
    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
    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
    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

    HAVING filtra grupos como WHERE filtra filas. Este ticket necesita ambos: WHERE para limpiar las filas de entrada, HAVING con dos condiciones de agregado para elegir los productos.

    Primero WHERE, después HAVING

    SELECT product_id, COUNT(*) AS lines, ROUND(AVG(discount_pct), 2) AS avg_discount_pct
    FROM order_items
    WHERE quantity > 0
    GROUP BY product_id
    HAVING COUNT(*) >= 500;

    La consulta se ejecuta en este orden: FROM, WHERE (descarta las 24 líneas con cantidad cero o negativa), GROUP BY y luego HAVING. Eso devuelve 14 productos con al menos 500 líneas. Sus descuentos promedio están entre 1.58 % y 2.45 %: la mayoría de las líneas no llevan ningún cupón, así que el promedio se mantiene bajo.

    Dos condiciones en un HAVING

    HAVING acepta los mismos AND, OR y paréntesis que WHERE, siempre que cada condición trate del grupo: un agregado o una columna por la que agrupaste.

    HAVING COUNT(*) >= 500
       AND AVG(discount_pct) < 2

    Seis de los 14 también tienen un descuento promedio menor al 2 %. El más concurrido de ellos es el producto 154 con 836 líneas.

    Puedes repetir el agregado en HAVING en lugar de usar el alias. DuckDB también acepta HAVING lines >= 500, pero muchas bases de datos no, así que volver a escribir el agregado es el hábito portable.

    Errores comunes

    • Poner AVG(discount_pct) < 2 en WHERE. No se permiten agregados en WHERE, porque WHERE se ejecuta antes de que existan los grupos.
    • Mover quantity > 0 a HAVING. Es una condición sobre líneas sueltas, así que pertenece a WHERE; HAVING solo ve grupos.
    • Unir las dos condiciones con OR. “Al menos 500 líneas o un descuento bajo” conserva a todo vendedor pequeño sin cupones, que es lo opuesto a una lista de más vendidos.
    • Filtrar por la columna redondeada. 1.99 se redondea a 1.99 aquí, pero un valor como 1.996 se mostraría como 2.00 y aun así pasaría, lo que le parece un error a quien lee.

    En SQL Server

    Mismas cláusulas y mismo orden. SQL Server nunca acepta un alias de SELECT en HAVING, así que repite el agregado. Ojo también con el promedio: AVG de una columna INT devuelve un INT allí, pero discount_pct es un decimal, así que 1.99 sigue siendo 1.99:

    HAVING COUNT(*) >= 500 AND AVG(discount_pct) < 2

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