tva
← Analyses

DuckDB pour l’analyse ponctuelle : transformer des milliers de CSV en tableau de bord

Amazon Seller Central génère des données à un rythme que ne peuvent pas suivre les outils de la plupart des analystes. Rapports de trafic, données de conversion, bilans de l’état des stocks, exports de performances commerciales, données détaillées des campagnes publicitaires – le tout livré sous forme de fichier CSV ou TSV, s’accumulant sur des mois et des années dans des répertoires contenant des milliers de fichiers. La réponse standard est d’importer tout dans une base de données. Mais en réalité, mettre en place, maintenir et interroger une instance PostgreSQL pour ce qui est fondamentalement une charge analytique principalement en lecture et à schéma variable introduit plus de charge opérationnelle que le problème lui-même ne le justifie.

Nous avons construit un pipeline de production de rapports pour un vendeur Amazon opérant sur cinq places de marché européennes. Les données brutes : six ans d’exports Seller Central, 4 200 fichiers, couvrant les rapports commerciaux, les performances publicitaires, les instantanés de stocks et les grands livres de frais FBA. L’approche initiale – un schéma PostgreSQL avec des scripts ETL – a nécessité trois semaines de stabilisation, puis une attention constante au fur et à mesure qu’Amazon révisait les noms de colonnes et les structures de rapports entre les versions d’API. Les requêtes étaient rapides. La maintenance ne l’était pas.

DuckDB change la donne. C’est une base de données analytique embarquée – elle s’exécute dans le même processus que votre code d’analyse, ne nécessite pas de serveur, pas de démon, pas de chaînes de connexion, et lit directement depuis les fichiers sur disque. L’interface de requête est du SQL standard. Les caractéristiques de performance sont celles d’un moteur columnaire optimisé pour l’agrégation à grande échelle. Pour une charge de travail comme celle-ci, DuckDB n’est pas seulement adapté – c’est le bon outil.

Ce qu’est réellement DuckDB

La distinction qui importe le plus est le stockage en colonnes par opposition au stockage en lignes. PostgreSQL organise les données en lignes : chaque champ d’un enregistrement donné est stocké de manière contiguë sur disque. Lorsque vous exécutez SELECT revenue, units_sold FROM orders WHERE marketplace = 'DE', un parcours séquentiel lit chaque champ de chaque ligne parcourue, rejette ceux que vous n’avez pas demandés, et retourne les deux que vous avez demandés. Les index, y compris les parcours d’index seuls, peuvent réduire ces lectures, mais ils aident rarement les agrégations larges portant sur la majeure partie d’une table. C’est efficace pour les charges transactionnelles – insertion, mise à jour et récupération d’enregistrements individuels. Cette organisation est largement inadaptée aux requêtes analytiques qui agrègent sur des millions de lignes tout en ne touchant qu’une poignée de colonnes.

DuckDB organise les données par colonne. Toutes les valeurs de revenue sont stockées ensemble ; toutes les valeurs de units_sold sont stockées ensemble. La même requête ne lit que les trois colonnes dont elle a besoin (les deux renvoyées et marketplace pour le filtre), sans lire les autres. Sur un ensemble de données avec quarante colonnes, ce n’est pas une optimisation mineure – cela peut réduire les E/S d’environ 90 % ou plus pour les requêtes qui ne touchent que quelques colonnes. Grâce aussi à l’exécution vectorisée – traitement des données par lots de 2 048 valeurs par défaut en utilisant les instructions CPU SIMD plutôt qu’une ligne à la fois – DuckDB atteint des performances de requête sur les charges analytiques qui peuvent dépasser ce qu’une instance PostgreSQL bien réglée offre sur du matériel identique, en particulier avec des formats en colonnes comme Parquet.

L’architecture embarquée est tout aussi importante. Il n’y a pas d’aller-retour réseau, pas de surcharge d’authentification, pas de pool de connexions à gérer. DuckDB ouvre un fichier ou un répertoire, construit un plan de requête et s’exécute. Le binaire pèse moins de 100 Mo. Il s’exécute sur un ordinateur portable, dans une fonction Lambda, ou dans un script Python lancé par une tâche cron. Pour les pipelines analytiques auto-hébergés, la charge d’exploitation est presque nulle.

SQL directement sur des fichiers

La fonctionnalité qui rend DuckDB pratique pour les données Seller Central en particulier est la lecture des fichiers sélectionnés par des motifs glob. Une seule instruction SQL interroge des milliers de fichiers CSV simultanément :

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 infère le schéma à partir du contenu des fichiers – il échantillonne les lignes, détecte les types de colonnes, gère les différents délimiteurs et, avec union_by_name=true, unifie les colonnes par nom entre les fichiers dont les ensembles de colonnes diffèrent (par défaut, il prend le schéma du premier fichier et associe les fichiers suivants par position). Il ne fait pas lui-même le rapprochement entre les colonnes renommées ; celles-ci nécessitent toujours un alias ou un COALESCE dans la requête. Pour les données Seller Central, où Amazon a révisé les noms de colonnes au moins quatre fois au cours des trois dernières années, c’est important. La requête s’exécute sur tous les fichiers qui existent dans le répertoire au moment de l’exécution, sans étape d’importation, sans migration de schéma, et sans cache périmé à invalider.

Pour les exports TSV de la console publicitaire, la même approche s’applique avec une indication de format explicite :

FROM read_csv('/data/advertising/*.tsv', delim='	', header=true)

Ce n’est pas un simple gain de confort. C’est un modèle mental différent pour travailler avec des données basées sur des fichiers : le système de fichiers devient la base de données. Le schéma est inféré, pas déclaré. Les requêtes combinent les données de plusieurs fichiers sans aucune étape de prétraitement. Pour l’analyse ponctuelle, où la question change plus vite qu’un pipeline ETL ne peut être mis à jour, cette architecture est le bon choix par défaut.

Caractéristiques de performance

Sur notre ensemble de données de référence – 4 200 fichiers, environ 2,8 Go non compressés – une agrégation complète du chiffre d’affaires regroupée par place de marché, ASIN et mois se termine en 4,1 secondes sur un ordinateur portable standard (Apple M2, 16 Go de RAM). La requête PostgreSQL équivalente, sur des données importées et indexées, se termine en 2,8 secondes. Mais cette comparaison exclut les quarante minutes d’importation initiale, les migrations de schéma requises chaque fois qu’Amazon change un nom de colonne, et le coût continu de maintenance ETL. Sur le coût opérationnel total, DuckDB l’emporte nettement pour cette charge de travail.

Si les performances des requêtes deviennent un goulot d’étranglement – typiquement pour des volumes de plusieurs téraoctets – DuckDB lit Parquet nativement, et la conversion des fichiers CSV existants en Parquet est une opération unique :

COPY (FROM read_csv_auto('/data/**/*.csv'))
TO '/data/consolidated.parquet'
(FORMAT PARQUET);

Les requêtes ultérieures sur le fichier Parquet s’exécutent 8 à 12 fois plus vite qu’avec les CSV originaux. Parquet stocke des statistiques de colonnes – valeurs minimales et maximales par groupe de lignes – que DuckDB utilise pour ignorer des sections entières de données sans les lire. Un filtre sur marketplace = 'DE' sur un fichier Parquet trié peut lire moins de dix pour cent des données sur disque. Cette application anticipée des filtres n’est pas disponible avec des fichiers CSV plats.

Construire un tableau de bord autonome avec Chart.js

Les requêtes DuckDB produisent des données. La question pratique est de savoir quoi en faire. Pour la production de rapports internes des activités Amazon de petite à moyenne taille, un fichier HTML autonome utilisant Chart.js est souvent plus utile qu’un tableau de bord hébergé – pas de serveur à faire tourner, pas d’identifiants à gérer, pas d’instance Superset ou Metabase à maintenir.

Le pipeline fonctionne en trois étapes. D’abord, DuckDB exporte les résultats de requêtes en 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);

Ensuite, un script Python lit ces fichiers JSON et intègre les données directement dans un modèle HTML référençant Chart.js via CDN. Enfin, le fichier HTML s’ouvre dans un navigateur sans nécessiter de serveur 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>

Le résultat est un fichier HTML unique (il charge Chart.js depuis un CDN ; les lecteurs ont donc besoin d’un accès à Internet, sauf si vous intégrez directement la bibliothèque au fichier) qui peut être partagé par e-mail, placé sur un bucket S3 privé, ou ouvert directement depuis un répertoire local. Pour les équipes opérationnelles qui ont besoin de rapports hebdomadaires sans la charge opérationnelle d’une infrastructure de tableau de bord en ligne, cette approche est pragmatique et maintenable. Le tableau de bord se régénère en exécutant un script shell : DuckDB interroge les CSV, Python construit le HTML, terminé. Pas d’infrastructure serveur, pas de frais d’hébergement récurrents et peu d’éléments à maintenir : les requêtes et le script du modèle.

Quand DuckDB bat PostgreSQL

DuckDB n’est pas un remplacement de PostgreSQL. La comparaison n’a de sens que lorsque la charge de travail est véritablement analytique – lectures de grands volumes de données, agrégations, regroupements – et que les données sont principalement en lecture plutôt qu’en écriture. PostgreSQL l’emporte nettement sur les écritures concurrentes, l’intégrité référentielle, le contrôle d’accès au niveau des lignes, et les exigences opérationnelles complètes d’une application de production. C’est le bon choix pour un système multi-utilisateurs où les enregistrements sont insérés, mis à jour et interrogés par le code applicatif.

DuckDB gagne quand :

  • Les données existent déjà sous forme de fichiers sur disque, sans étape d’importation prévue
  • Le schéma n’est pas entièrement connu à l’avance, ou change fréquemment
  • Les requêtes répondent à des besoins ponctuels et les modes d’accès ne peuvent pas être prédits à l’avance
  • La charge opérationnelle d’un serveur de base de données n’est pas justifiée par la longévité ou l’échelle du projet

Les données Amazon Seller Central satisfont précisément ces quatre critères. Le schéma change à chaque version de Seller Central. Les fichiers existent déjà localement après l’export. Les questions métier changent d’une semaine à l’autre selon ce qui est optimisé. Et l’alternative – un pipeline ETL PostgreSQL maintenu – représente des semaines de temps d’ingénierie pour un problème que DuckDB résout en un après-midi.

La règle pratique : si vous utilisez habituellement Excel ou un notebook Jupyter pour analyser ces données, optez plutôt pour DuckDB. Le SQL est plus expressif, les performances sont généralement bien meilleures pour tout ce qui dépasse quelques milliers de lignes, et l’architecture basée sur des fichiers signifie que vos données restent là où elles sont déjà.

Analyses connexes

Pour aller plus loin