DuckDB para analítica ad hoc: convertir miles de CSV en un panel
Amazon Seller Central genera datos a un ritmo que supera las herramientas de la mayoría de los analistas. Informes de tráfico, datos de conversión, resúmenes del estado del inventario, exportaciones de rendimiento del negocio y desgloses de campañas publicitarias: cada uno llega como archivo CSV o TSV y, durante meses y años, se acumulan en directorios con miles de archivos. La respuesta habitual es importarlo todo a una base de datos. Pero, en la práctica, configurar, mantener y consultar una instancia de PostgreSQL para una carga de analítica centrada en lecturas y con esquemas variables introduce más carga operativa de la que justifica el problema.
Creamos un pipeline de generación de informes para un vendedor de Amazon que operaba en cinco marketplaces europeos. Los datos de origen eran seis años de exportaciones de Seller Central: 4 200 archivos con informes del negocio, rendimiento publicitario, instantáneas del inventario y libros de tarifas de FBA. El enfoque inicial, un esquema PostgreSQL con scripts ETL, tardó tres semanas en estabilizarse y exigía atención constante a medida que Amazon modificaba los nombres de columnas y las estructuras de informes entre versiones de la API. Las consultas eran rápidas. El mantenimiento, no.
DuckDB cambia ese balance. Es una base de datos analítica integrada: se ejecuta en el mismo proceso que el código de análisis, no requiere servidor, servicio en segundo plano ni cadenas de conexión, y lee directamente los archivos del disco. Su interfaz de consulta es SQL estándar. Su rendimiento corresponde al de un motor columnar optimizado para agregaciones a gran escala. Para esta carga de trabajo, DuckDB no solo es suficiente: es la herramienta adecuada.
Qué es realmente DuckDB
La diferencia más relevante es el almacenamiento por columnas frente al almacenamiento por filas. PostgreSQL organiza los datos en filas: todos los campos de un registro se almacenan de forma contigua en disco. Al ejecutar SELECT revenue, units_sold FROM orders WHERE marketplace = 'DE', un escaneo secuencial lee todos los campos de cada fila que visita, descarta los no solicitados y devuelve los dos pedidos. Los índices, incluidos los escaneos que solo acceden al índice, pueden reducir estas lecturas, pero rara vez ayudan en agregaciones amplias que abarcan la mayor parte de una tabla. Esto es eficiente para cargas transaccionales: insertar, actualizar y recuperar registros individuales. Es poco adecuado para consultas analíticas que agregan millones de filas y solo utilizan unas pocas columnas.
DuckDB organiza los datos por columnas. Todos los valores de revenue se almacenan juntos, al igual que los de units_sold. La misma consulta lee únicamente las tres columnas que necesita (las dos que devuelve y marketplace para el filtro) y omite el resto. En un conjunto de datos de cuarenta columnas, no es una optimización menor: puede reducir las operaciones de entrada y salida aproximadamente un 90 por ciento o más en consultas que solo acceden a unas pocas columnas. Combinado con la ejecución vectorizada —procesar lotes de 2 048 valores de forma predeterminada mediante instrucciones SIMD de la CPU, en lugar de una fila a la vez—, DuckDB alcanza un rendimiento analítico que puede superar al de una instancia PostgreSQL bien ajustada con el mismo hardware, especialmente con formatos columnares como Parquet.
La arquitectura integrada importa tanto como lo anterior. No hay viajes de ida y vuelta por la red, sobrecarga de autenticación ni un pool de conexiones que gestionar. DuckDB abre un archivo o directorio, construye un plan de consulta y lo ejecuta. El binario ocupa menos de 100 MB. Funciona en una computadora portátil, dentro de una función Lambda o en un script Python ejecutado por una tarea cron. En pipelines analíticos autoalojados, las necesidades operativas son casi nulas.
SQL directamente sobre archivos
La característica que hace práctico DuckDB para los datos de Seller Central es la lectura de archivos mediante patrones glob. Una sola sentencia SQL consulta miles de archivos CSV simultáneamente:
SELECT
date_trunc('month', "Date") AS month,
SUM("Units Ordered") AS units,
SUM("Ordered Product Sales") AS revenue
FROM read_csv_auto('/data/seller-central/business-reports/*.csv')
GROUP BY 1
ORDER BY 1;
read_csv_auto infiere el esquema a partir del contenido de los archivos: toma muestras de filas, detecta tipos de columnas, maneja distintos delimitadores y, con union_by_name=true, unifica las columnas por nombre entre archivos con conjuntos de columnas diferentes (de forma predeterminada toma el esquema del primer archivo y hace coincidir los archivos posteriores por posición). No concilia por sí solo las columnas renombradas; estas siguen necesitando un alias o un COALESCE en la consulta. Esto importa con los datos de Seller Central, donde Amazon ha revisado los nombres de columnas al menos cuatro veces en los últimos tres años. La consulta se ejecuta sobre los archivos que existan en el directorio en ese momento, sin importación, migración de esquema ni caché desactualizada que invalidar.
Para las exportaciones TSV de la consola de publicidad, se aplica el mismo enfoque indicando explícitamente el formato:
FROM read_csv('/data/advertising/*.tsv', delim=' ', header=true)
No es una simple comodidad. Es una forma distinta de trabajar con datos en archivos: el sistema de archivos se convierte en la base de datos. El esquema se infiere, no se declara. Las consultas combinan archivos sin ningún paso de preprocesamiento. Para el análisis ad hoc, donde las preguntas cambian más rápido de lo que se puede actualizar un pipeline ETL, esta arquitectura es la opción predeterminada adecuada.
Características de rendimiento
En nuestro conjunto de datos de referencia —4 200 archivos, aproximadamente 2,8 GB sin comprimir—, una agregación completa de ingresos agrupada por marketplace, ASIN y mes se completa en 4,1 segundos en una computadora portátil estándar (Apple M2, 16 GB de RAM). La consulta PostgreSQL equivalente, sobre datos importados e indexados, se completa en 2,8 segundos. Pero esa comparación excluye los cuarenta minutos de importación inicial, las migraciones de esquema necesarias cada vez que Amazon cambia el nombre de una columna y el costo del mantenimiento continuo del ETL. En costo operativo total, DuckDB gana claramente para esta carga.
Si el rendimiento de las consultas se convierte en un cuello de botella, normalmente a escala de varios terabytes, DuckDB lee Parquet de forma nativa y convertir los CSV existentes a Parquet es una operación que se realiza una sola vez:
COPY (FROM read_csv_auto('/data/**/*.csv'))
TO '/data/consolidated.parquet'
(FORMAT PARQUET);
Las consultas posteriores sobre el archivo Parquet son entre 8 y 12 veces más rápidas que sobre los CSV originales. Parquet almacena estadísticas por columna —valores mínimos y máximos de cada grupo de filas— que DuckDB utiliza para omitir secciones completas sin leerlas. Un filtro marketplace = 'DE' sobre un archivo Parquet ordenado puede leer menos del diez por ciento de los datos en disco. Esta aplicación anticipada de filtros no está disponible sobre archivos CSV planos.
Crear un panel independiente con Chart.js
Las consultas de DuckDB producen datos. La pregunta práctica es qué hacer con ellos. Para los informes internos de operaciones pequeñas o medianas en Amazon, un archivo HTML independiente con Chart.js suele ser más útil que un panel alojado: no hay servidor que ejecutar, credenciales que gestionar ni una instancia de Superset o Metabase que mantener.
El pipeline funciona en tres pasos. Primero, DuckDB exporta los resultados de las consultas a JSON:
COPY (
SELECT
strftime(date_trunc('week', "Date"), '%Y-%m-%d') AS week,
SUM("Units Ordered") AS units,
SUM("Ordered Product Sales") AS revenue
FROM read_csv_auto('/data/**/*.csv')
GROUP BY 1 ORDER BY 1
) TO 'weekly.json' (FORMAT JSON, ARRAY true);
Segundo, un script Python lee esos archivos JSON e incorpora los datos directamente en una plantilla HTML que carga Chart.js mediante una CDN. Tercero, el archivo HTML se abre en un navegador sin necesidad de servidor web.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Seller Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<canvas id="revenue"></canvas>
<script>
const rows = /* JSON_DATA_HERE */;
new Chart(document.getElementById('revenue'), {
type: 'line',
data: {
labels: rows.map(r => r.week),
datasets: [{ label: 'Revenue', data: rows.map(r => r.revenue), tension: 0.3 }]
}
});
</script>
</body>
</html>
El resultado es un único archivo HTML (carga Chart.js desde una CDN, por lo que quienes lo abran necesitan acceso a internet, salvo que se integre la biblioteca directamente en el archivo) que se puede compartir por correo electrónico, colocar en un bucket privado de S3 o abrir directamente desde un directorio local. Para equipos operativos que necesitan informes semanales sin la carga de una infraestructura de paneles en tiempo real, este enfoque es práctico y fácil de mantener. El panel se regenera ejecutando un script de shell: DuckDB consulta los CSV y Python construye el HTML. Sin infraestructura de servidores ni costos recurrentes de alojamiento, y con pocos elementos que mantener: las consultas y el script de la plantilla.
Cuándo DuckDB supera a PostgreSQL
DuckDB no sustituye a PostgreSQL. La comparación solo tiene sentido cuando la carga es realmente analítica —lecturas extensas, agregaciones y agrupaciones— y los datos se leen principalmente, en lugar de escribirse. PostgreSQL gana claramente en escrituras concurrentes, integridad referencial, control de acceso por fila y requisitos operativos completos de una aplicación de producción. Es la opción correcta para un sistema multiusuario donde el código de la aplicación inserta, actualiza y consulta registros.
DuckDB gana cuando:
- Los datos ya existen como archivos en disco y no se prevé importarlos.
- El esquema no se conoce completamente de antemano o cambia con frecuencia.
- Las consultas son ad hoc y no se pueden predecir los patrones de acceso.
- La duración o escala del proyecto no justifica la carga operativa de un servidor de base de datos.
Los datos de Amazon Seller Central cumplen exactamente los cuatro criterios. El esquema cambia con cada versión de Seller Central. Los archivos ya están disponibles localmente tras la exportación. Las preguntas del negocio cambian cada semana según lo que se esté optimizando. Y la alternativa, un pipeline ETL de PostgreSQL que hay que mantener, representa semanas de ingeniería para un problema que DuckDB resuelve en una tarde.
La regla práctica: si normalmente usaría Excel o un cuaderno de Jupyter para analizar estos datos, use DuckDB. SQL es más expresivo, el rendimiento suele ser muy superior con más de unos pocos miles de filas y la arquitectura basada en archivos permite que los datos permanezcan donde ya están.
Artículos relacionados
- tva-fetch | Cómo la plena propiedad de los datos transforma las operaciones de venta en Amazon
- Crear infraestructura de datos preparada para producción para vendedores de Amazon: presentamos tva-fetch
- Proxy inverso Traefik: guía completa de autoalojamiento para automatizar HTTPS y SSL
- Autoalojamiento de Windmill en Ubuntu: tutorial completo de configuración con Docker y resolución de problemas de PostgreSQL