Dans l'article sur les stages, on a vu que le stage répond à une question simple :

“Où sont les fichiers ?”

On a terminé l'article avec cette commande :

CREATE OR REPLACE STAGE stg_sales_internal
  FILE_FORMAT = (FORMAT_NAME = 'CSV_SALES_FORMAT');

COPY INTO sales
FROM @stg_sales_internal;

Le stage dit à Snowflake : “les fichiers sont là, dans stg_sales_internal”.
Il reste une autre question tout aussi importante :

“OK, mais comment je dois lire ces fichiers ?”

C'est exactement le rôle des file formats.

C'est quoi concrètement un file format dans Snowflake ?

Un file format, c'est simplement une fiche de lecture pour tes fichiers.

Tu y dis à Snowflake : “ce sont des CSV” ou “ce sont des JSON / Parquet”, et tu précises les règles de base comme le séparateur, en-têtes, encodage, gestion des NULL, compression, etc....

Tu peux tout écrire à la main dans chaque COPY… mais ce n'est clairement pas une bonne idée sur le long terme. Le mieux, c'est de créer un file format nommé et de le réutiliser partout.

Exemple :

COPY INTO sales
FROM @stg_sales_internal
FILE_FORMAT = (
  TYPE = CSV
  FIELD_DELIMITER = ','
  SKIP_HEADER = 1
);

Snowflake comprend : “je lis un CSV, séparé par des virgules, avec une ligne d'en-tête à ignorer”.

Mais comme je l'ai déjà mentionné, cette méthode n'est pas recommandée car dès que tu as plusieurs jobs qui lisent les mêmes fichiers, tu te retrouves vite à copier-coller les mêmes options partout. C'est à ce moment-là que les file formats nommés deviennent vraiment intéressants.

Exemple avec un file format CSV

Au lieu de réécrire les options dans chaque COPY, tu peux créer une sorte de “profil CSV” :

CREATE OR REPLACE FILE FORMAT CSV_SALES_FORMAT
  TYPE = CSV
  FIELD_DELIMITER = ','
  SKIP_HEADER = 1
  FIELD_OPTIONALLY_ENCLOSED_BY = '"'
  NULL_IF = ('NULL', 'null', '')
  EMPTY_FIELD_AS_NULL = TRUE
  COMPRESSION = AUTO;

Ensuite ton COPY devient beaucoup plus lisible :

COPY INTO sales
FROM @stg_sales_internal
FILE_FORMAT = (FORMAT_NAME = 'CSV_SALES_FORMAT');

Si demain le fournisseur change le séparateur ou la façon d'écrire les NULL, tu corriges juste le file format, pas dix scripts SQL différents.

Tu peux même aller plus loin et fixer le file format directement sur le stage :

CREATE OR REPLACE STAGE stg_sales_internal
  FILE_FORMAT = (FORMAT_NAME = 'CSV_SALES_FORMAT');

À partir de là, ton COPY se simplifie encore :

COPY INTO sales
FROM @stg_sales_internal;

Le stage sait où sont les fichiers et quel file format utiliser. Toi, tu te concentres sur la table cible.

Exemple avec un file format JSON ou Parquet

Dès que tu touches à des logs, des events ou des payloads d'API, tu tombes vite sur des formats semi-structurés (JSON, Parquet, XML etc....).

Là aussi, tu peux définir un file format dédié.

Par exemple, pour des events JSON :

CREATE OR REPLACE FILE FORMAT JSON_EVENTS_FORMAT
  TYPE = JSON
  STRIP_OUTER_ARRAY = TRUE
  IGNORE_UTF8_ERRORS = TRUE;

STRIP_OUTER_ARRAY = TRUE dit à Snowflake :
“si le fichier contient un tableau [ {...}, {...}, ... ], tu me fais une ligne par élément du tableau”.

Pour du Parquet, c'est encore plus simple :

CREATE OR REPLACE FILE FORMAT PARQUET_GENERIC_FORMAT
  TYPE = PARQUET;

Tu laisses Snowflake mapper les types, et tu n'affines que si tu as un besoin particulier.

Les options à connaitre

La doc officielle de snowflake liste une tonne d'options, mais en pratique tu joues toujours avec les mêmes.

Pour les CSV, on a besoin généralement de :

  • Séparateur (FIELD_DELIMITER) ;
  • Lignes d'en-tête à sauter (SKIP_HEADER) ;
  • Champs texte qui contiennent le séparateur (FIELD_OPTIONALLY_ENCLOSED_BY) ;
  • Gestion des NULL vs chaîne vide (NULL_IF, EMPTY_FIELD_AS_NULL) ;
  • Compression (COMPRESSION) pour les fichiers .gz et consorts.

Pour le JSON, on utilise souvent STRIP_OUTER_ARRAY et parfois des formats de dates / timestamps quand tes sources ont des formats un peu exotiques.

Le reste, tu le découvriras au cas par cas et selon le besoin, mais déjà avec ça tu couvres la majorité des besoins.

Quand est-ce que ça vaut le coup de créer un file format ?

Sauf quand tu fais un petit test rapide avec un seul fichier, tu peux considérer que la bonne réponse, c'est toujours : file format nommé ;)