La solicitud

De Tyler Morgan, Ingeniero de Datos Junior
Tu objetivo
Trata las filas de clientes como la misma persona cuando LOWER(TRIM(email)) coincide. Numera las filas de cada persona con ROW_NUMBER, primero el signup_date más temprano (desempata por customer_id), y conserva la fila 1. Devuelve una fila por registro descartado con: email (recortado y en minúsculas), kept_customer_id y dropped_customer_id. Ordena por dropped_customer_id ascendente.
- Lee de la tabla
customerspendiente - Muestra exactamente
emailkept_customer_iddropped_customer_idpendiente - 18 filas, ordenadas como se pidependiente
Tu consulta
Cargando el editor…
Solución de referencia
| customer_id | first_name | last_name | city | |
|---|---|---|---|---|
| 1 | Owen | Hudson | owen.hudson@icloud.com | San Francisco |
| 2 | Douglas | Mendoza | dmendoza69@gmail.com | Atlanta |
| 3 | Henry | Brooks | hbrooks@lakesidecafe.com | Los Angeles |
| 4 | Brian | Graham | bgraham@bluecedarhomes.com | Seattle |
Mote revisa tu resultado
Presiona Ejecutar. Cada fila que cumple el objetivo se vuelve una luciérnaga en tu linterna.
Lo que recibe Tyler, y por qué
Tablas
Datos de la tienda Harbor & Pine, listos para consultar.
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 |
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 |
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
El capítulo 7 encontró clientes duplicados con GROUP BY ... HAVING COUNT(*) > 1. Eso te dice qué emails
se repiten, pero un group-by colapsa las filas, así que no puedes conservar fácilmente un registro entero y
descartar el resto. ROW_NUMBER sí puede.
El patrón de deduplicación
Numera las filas dentro de cada grupo de duplicados, en el orden que decide al ganador, y conserva el número 1:
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY LOWER(TRIM(email)) -- what makes two rows the same person
ORDER BY signup_date, customer_id -- who wins: earliest, then lowest id
) AS rn
FROM customers
)
SELECT * FROM ranked WHERE rn = 1;
Esa ventana esconde tres decisiones, y cada una debería ser deliberada:
- La clave en
PARTITION BY. Aquí, un email limpio. Con estos datos la columnaemailen bruto tiene 2,012 valores distintos, la limpia 2,000. - El ganador en
ORDER BY. El alta más temprana es una regla común. Otras son “el registro con más pedidos” o “el actualizado más recientemente”. - Un desempate.
customer_idhace que el resultado sea el mismo en cada ejecución.
La consulta de arriba devuelve 2,000 filas de 2,018: una por persona.
Conservar y mapear
Borrar duplicados rara vez basta. Los pedidos, tickets y correos pueden apuntar a los ids descartados, así
que el equipo del CRM necesita un mapa de cada id descartado al id que sobrevive. Une las filas
clasificadas consigo mismas, los duplicados con el ganador de la misma clave. Por ejemplo, el cliente 2014
(BJONES76@GMAIL.COM) se asigna al cliente 1698 (bjones76@gmail.com). En este conjunto de datos ninguno
de los 18 duplicados tiene todavía un pedido, así que la fusión no pierde nada.
Errores comunes
- Particionar por la columna en bruto. El starter hace esto y encuentra solo 6 duplicados, las copias exactas. Los otros 12 difieren por mayúsculas o por un espacio inicial.
- Usar
RANKen lugar deROW_NUMBER. Dos registros con la misma fecha de alta recibirían ambos el puesto 1 y ambos sobrevivirían. SELECT DISTINCT. Solo elimina filas idénticas en todas las columnas. Las personas duplicadas casi siempre difieren en algo (el id, al menos).- Filtrar
rnen el mismo SELECT. Un valor de ventana no puede ir en elWHEREde esa consulta. Calcúlalo en una CTE y filtra por fuera. DuckDB, Snowflake y BigQuery también aceptanQUALIFY rn = 1.
En SQL Server
El patrón es idéntico, y es un favorito de los cursos y entrevistas de T-SQL. SQL Server no tiene
QUALIFY, así que la CTE es obligatoria. Una CTE puede incluso borrar los duplicados en el sitio:
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY LOWER(TRIM(email)) ORDER BY signup_date, customer_id) AS rn
FROM customers
)
DELETE FROM ranked WHERE rn > 1; ¡Linterna encendida!
Practicaste: deduplicationrow numberwindow functionsdata cleaning
- Resuelto
- Sin pistas
- Al primer intento
