SQL · Capítulo 10 · Lección de conceptos · 7 min de lectura

Datos limpios significan un significado, una escritura

Los datos sucios son lo mismo del mundo real escrito de varias formas. Limpiar es convertir cada variante a una forma canónica, decidir qué fila representa a una persona y evitar que los NULL cambien tus conteos en silencio.

La idea

Los datos sucios rara vez son basura aleatoria. Son lo mismo del mundo real escrito de varias formas: "New York", "NEW YORK" y "New York " (con un espacio al final) son una sola ciudad para ti y tres valores distintos para la base de datos. SQL compara el texto exactamente, byte por byte, así que felizmente hará tres grupos, tres clientes o tres claves de join a partir de una sola.

Limpiar es decidir cuál es la forma canónica y convertir cada variante a ella antes de agrupar, unir o contar. La segunda mitad es decidir qué hacer con las filas que no deberían contar en absoluto (duplicados, valores imposibles) y con los valores que faltan.

Figura 1

Cuatro escrituras, una ciudad

customers, agrupado por city sin limpiar
citycustomers
New York175
NEW YORK3
New York 2
new york1
agrupado por LOWER(TRIM(city))
city_keycustomers
new york181

Paso 1 de 3 GROUP BY city trata el texto exactamente como está guardado. Las mayúsculas y un espacio al final forman cuatro grupos para una sola ciudad.

  1. GROUP BY city trata el texto exactamente como está guardado. Las mayúsculas y un espacio al final forman cuatro grupos para una sola ciudad.
  2. TRIM quita los espacios sobrantes y LOWER iguala las mayúsculas. Ambas se aplican a cada fila antes de agrupar.
  3. Agrupar por la expresión limpia pliega las cuatro filas en una: 175 + 3 + 2 + 1 = 181 clientes. En toda la tabla, 129 escrituras distintas se vuelven 61 ciudades reales.

Por qué existe

Un informe construido sobre datos sucios está mal de una forma que parece bien. Un gráfico de ciudades con cuatro New York igual dibuja barras. Un conteo de clientes que incluye a veinte personas dos veces igual imprime un número. Nada falla, así que nadie se entera hasta que alguien compara contra un total en el que confía.

Arreglarlo en la tabla de origen suele ser imposible: no eres dueño del CRM, el archivo del proveedor llega otra vez como texto la semana que viene, y no deberías sobrescribir datos crudos que quizá necesites después. Por eso los analistas limpian en la consulta: un CTE o una vista que convierte las filas crudas en una versión confiable, y todos los informes leen de ahí. Un solo lugar donde corregir, y un registro de lo que se hizo.

Cómo funciona por dentro

Construye una clave limpia

Una clave es el valor por el que agrupas o emparejas. Créala con funciones que quiten las diferencias irrelevantes: TRIM para espacios sobrantes, LOWER o UPPER para las mayúsculas, REPLACE para la puntuación. Aplica la misma expresión en todos los lugares donde se use el valor. Agrupa por la clave limpia y conserva las escrituras originales al lado si quieres mostrarlas.

Fíjate en lo que no estás haciendo: no estás decidiendo que "NYC" significa New York. Eso necesita una tabla de equivalencias, no una función, y es una decisión de negocio.

Elige qué duplicado gana

Las personas duplicadas son filas distintas que deberían ser una. Elimínalas con ROW_NUMBER(), no con DISTINCT: DISTINCT solo descarta filas idénticas en todas las columnas, y estas difieren en el id, la fecha de registro o las mayúsculas del email.

Figura 2

Dejar una fila por persona con ROW_NUMBER

customers
customer_idfirst_namelast_nameemailsignup_date
355DouglasDiazddiaz@brightlinedesign.com2023-07-10
2010DouglasDiazddiaz@brightlinedesign.com2023-10-11
1698BrandonJonesbjones76@gmail.com2025-10-12
2014BrandonJonesBJONES76@GMAIL.COM2025-12-31
ROW_NUMBER() OVER (PARTITION BY LOWER(TRIM(email)) ORDER BY signup_date)
customer_idemail_keyrn
355ddiaz@brightlinedesign.com1
2010ddiaz@brightlinedesign.com2
1698bjones76@gmail.com1
2014bjones76@gmail.com2

Paso 1 de 4 Dos personas existen dos veces cada una. Douglas tiene el mismo email en ambas filas; la segunda fila de Brandon es la misma dirección en mayúsculas.

  1. Dos personas existen dos veces cada una. Douglas tiene el mismo email en ambas filas; la segunda fila de Brandon es la misma dirección en mayúsculas.
  2. Particiona por el email limpio, para que BJONES76@GMAIL.COM y bjones76@gmail.com caigan en un mismo grupo. Ordena cada grupo por signup_date.
  3. Numera las filas dentro de cada grupo. El registro más antiguo es el 1, el duplicado posterior es el 2.
  4. Conserva rn = 1. Cada persona aparece exactamente una vez, y tú decidiste qué registro gana en lugar de que la base de datos elija al azar.

El ORDER BY dentro de la ventana es la regla de negocio: el registro más antiguo, la actualización más reciente, la fila más completa. Los clientes 2001 a 2018 de Harbor & Pine son duplicados así, algunos con el mismo email exacto y otros que solo difieren en mayúsculas o espacios. Recuerda pasar los pedidos al id que sobrevive si vas a contar por cliente.

Respeta el NULL

NULL significa "desconocido", y SQL arrastra lo desconocido. 5 + NULL es NULL. NULL = NULL no es verdadero. WHERE referral_source <> 'search' descarta las filas NULL, porque NULL <> 'search' es desconocido, no verdadero. Los agregados saltan los NULL, así que AVG de una columna con vacíos promedia solo las filas llenas.

Figura 3

NULL cambia lo que cuentas

customers
customer_idfirst_namereferral_source
2DouglasNULL
3HenryNULL
4Brianpinterest
5KyleNULL
6OliviaNULL
7Abigailsearch
8Miguelsearch
Contar siete clientes
count_starcount_referralcount_coalesced
737

Paso 1 de 3 Siete clientes. Cuatro no tienen referral_source. La columna es NULL, que significa "desconocido", no una cadena vacía.

  1. Siete clientes. Cuatro no tienen referral_source. La columna es NULL, que significa "desconocido", no una cadena vacía.
  2. COUNT(*) cuenta filas, así que dice 7. COUNT(referral_source) salta los NULL y dice 3. Las dos son correctas, para preguntas distintas.
  3. COALESCE(referral_source, 'unknown') le pone etiqueta a los vacíos, así que un GROUP BY muestra un grupo "unknown" visible en lugar de descartarlos en silencio.

COALESCE(a, b) devuelve el primer valor que no es NULL, así que puedes sustituir una etiqueta o un valor por defecto. Úsalo para mostrar ('unknown'), no para inventar números: reemplazar un costo que falta por 0 hace que el margen se vea mejor de lo que es.

NULLIF(a, b) hace lo contrario: devuelve NULL cuando a es igual a b. Es la protección estándar contra la división entre cero, porque x / NULLIF(y, 0) da NULL en lugar de un error cuando y es 0.

Convierte el texto a propósito

Los archivos pegados de proveedores y transportistas llegan como texto: $1,299.00, 2023-02-06, 06/02/2023. CAST hace fallar toda la consulta por un solo valor malo; TRY_CAST (y try_strptime para fechas) devuelve NULL, así que puedes contar e inspeccionar los fallos. Limpia primero el texto (quita el $ y la coma) y analiza cada formato de fecha en su propia rama.

Marca, no borres

Algunas filas parecen incorrectas pero pueden ser reales: líneas de pedido con cantidad 0 o -1, o un precio diez veces la mediana. Si las borras en silencio, los totales bajan y nadie sabe por qué. Agrega una columna de marca (is_outlier, is_valid_quantity), reporta sobre las filas limpias y guarda un conteo de lo que se excluyó. Una regla declarada, como "fuera de 1.5 veces el rango intercuartílico", es algo que un compañero puede discutir. Un borrado silencioso, no.

El orden importa al limpiar

Limpia en capas, cada una en su propio CTE: primero el texto (espacios, mayúsculas, símbolos), luego los tipos (conversión y análisis), luego los duplicados, luego las marcas. Una capa solo puede confiar en la anterior. Deduplicar por email antes de pasarlo a minúsculas no detecta BJONES76@GMAIL.COM, y convertir un precio antes de quitarle el $ convierte un valor bueno en un fallo. Cuando un número se vea raro, ejecuta las capas una por una y mira cuál lo cambió.

Errores comunes

"DISTINCT elimina los clientes duplicados"

Elimina las filas que coinciden en todas las columnas seleccionadas. Dos registros de una misma persona difieren al menos en el id, así que para DISTINCT no son duplicados. Si seleccionas solo las columnas que definen a una persona, las mayúsculas igual importan, y por eso primero limpias la clave.

"NULL es lo mismo que cero o una cadena vacía"

NULL es desconocido. Cero es un número, y '' es un texto de longitud cero. Se comportan distinto en conteos, promedios y comparaciones. Antes de reemplazar los NULL, pregunta qué significa el vacío: "no se registró la fuente de referencia" no es una fuente llamada vacío.

"Limpiar significa arreglar la tabla"

Limpiar en SQL significa escribir una consulta o vista que devuelva la versión corregida. La tabla cruda queda intacta, así que puedes volver a ejecutar la limpieza cuando cambien las reglas y demostrar qué cambiaste.

Ponte a prueba

Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.

customers tiene 2,018 filas y referral_source es NULL en 666 de ellas. ¿Qué devuelve SELECT COUNT(referral_source) FROM customers?

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