Nivel 1 · Ingresos por categoría
¿Es nuevo para ti? Lee primero el concepto: Cómo un JOIN empareja filas
0 de 3
La solicitud

De Jessica Turner, Gerente de Merchandising
Tu objetivo
Une order_items con products por product_id. Considerando solo las líneas de pedido con quantity > 0 (todos los estados de pedido), devuelve category y net_revenue (suma de quantity * unit_price * (1 - discount_pct / 100.0) usando el unit_price de la línea de pedido, redondeada a 2), una fila por categoría.
- Lee de la tabla
productspendiente - Lee de la tabla
order_itemspendiente - Muestra exactamente
categorynet_revenuependiente
Tu consulta
Cargando el editor…
Solución de referencia
| product_id | name | category | subcategory | unit_price |
|---|---|---|---|---|
| 101 | Vale Velvet Loveseat | Furniture | Sofas | 1659.00 |
| 102 | Alder Oak Coffee Table | Furniture | Tables | 919.00 |
| 103 | Orchard Rattan Dining Chair | Furniture | Chairs | 789.00 |
| 104 | Upland Oak Bookcase | Furniture | Storage | 429.00 |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Lo que recibe Jessica, y por qué
Tablas
Datos de la tienda Harbor & Pine, listos para consultar.
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 |
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 |
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 |
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
Un join alinea filas de dos tablas que coinciden según una condición. Cada línea de pedido tiene un
product_id, y cada producto tiene una fila en products, así que puedes adjuntar la categoría del producto
a cada línea:
SELECT i.order_item_id, i.quantity, p.name, p.category
FROM order_items AS i
JOIN products AS p ON p.product_id = i.product_id;
JOIN a secas significa INNER JOIN: solo se conservan las filas que encuentran coincidencia en ambos lados. Una línea
de pedido cuyo producto no esté en products desaparecería. En Harbor & Pine todas las líneas tienen un
producto válido, pero en los almacenes de datos reales eso nunca está garantizado, por eso importa el siguiente nivel.
Alias y nombres calificados
order_items AS i le da a la tabla un nombre corto. Cuando una consulta toca varias tablas, antepón a cada
columna el alias de su tabla (i.quantity, p.category). Es obligatorio cuando ambas tablas tienen una
columna con el mismo nombre. Aquí ambas tienen unit_price: i.unit_price es el precio pagado, mientras que
p.unit_price es el precio de lista de hoy. Usar la equivocada es un error real y común de ingresos.
Escribir unit_price sin prefijo falla con un error de “columna ambigua”, que es la base de datos
haciéndote un favor.
Primero el join, luego agregar
Los joins ocurren primero (son parte de FROM), luego WHERE, luego GROUP BY. Así que puedes filtrar y
agrupar por columnas de cualquiera de las dos tablas:
SELECT p.category,
ROUND(SUM(i.quantity * i.unit_price * (1 - i.discount_pct / 100.0)), 2) AS net_revenue
FROM order_items AS i
JOIN products AS p ON p.product_id = i.product_id
WHERE i.quantity > 0
GROUP BY p.category;
Comprueba el conteo de filas
El mejor hábito con los joins: saber cuántas filas esperas y comprobarlo. Unir líneas de pedido con
productos debería devolver exactamente una fila por línea de pedido, porque cada línea coincide con un producto.
Compara SELECT COUNT(*) FROM order_items con el conteo tras el join. Si el conteo del join es
mayor, el lado “uno” tenía duplicados. Si es menor, algunas líneas no tenían coincidencia.
Variaciones de sintaxis
USING (product_id)es un atajo deON a.product_id = b.product_idcuando los nombres de columna son idénticos. Funciona en Postgres, DuckDB, MySQL y BigQuery.- Los joins con coma al estilo antiguo,
FROM order_items i, products p WHERE p.product_id = i.product_id, dan el mismo resultado, pero olvidar la condición del WHERE crea un cross join con todas las combinaciones de filas. Escribe mejorJOIN ... ONexplícito. NATURAL JOINcoincide por todas las columnas con el mismo nombre, lo que aquí incluiríaunit_pricey descartaría en silencio la mayoría de las filas. Evítalo.
Nota de entrevista
Prepárate para explicar INNER vs LEFT JOIN con un ejemplo concreto, y para decir qué pasa con las filas sin coincidencia. “Un inner join las descarta” es la esencia.
Errores comunes
- Dejar una columna compartida sin calificar, o elegir la copia equivocada.
unit_pricea secas es ambiguo, yp.unit_pricees el precio de lista de hoy. Los ingresos necesitan el precio pagado:i.unit_price. - Olvidar la condición del join. Un join con coma sin su WHERE,
FROM order_items i, products p, empareja cada línea con cada producto. EscribeJOIN products AS p ON p.product_id = i.product_id. - Unir por la clave equivocada.
ON p.product_id = i.order_item_idse ejecuta sin error, porque ambas columnas son números, y adjunta categorías al azar. Haz coincidir la clave con la columna que se refiere a ella. - Saltarse la comprobación del conteo de filas. Si el conteo del join difiere de
SELECT COUNT(*) FROM order_items, se descartaron o duplicaron líneas incluso antes de agrupar.
En SQL Server
Los joins se escriben exactamente igual. Dos hábitos que verás en SSMS: las tablas llevan un prefijo de esquema
(Sales.SalesOrderHeader), que las tablas de Harbor & Pine no necesitan, y para echar un vistazo a las filas se usa
SELECT TOP 10 * FROM ... en lugar de LIMIT 10.
¡Linterna encendida!
Practicaste: inner jointable aliasgroup by
- Resuelto
- Sin pistas
- Al primer intento
