Préparation de vos fichiers de données

Ce chapitre fournit des bonnes pratiques, des lignes directrices générales et des considérations importantes concernant la préparation de vos fichiers de données en vue d’un chargement.

Dans ce chapitre :

Meilleures pratiques et limitations en matière de dimensionnement de fichiers

Pour optimiser les performances de chargement et éviter les limitations de taille, tenez compte des instructions de dimensionnement des fichiers de données suivantes. Notez que ces recommandations s’appliquent aux chargements de données en masse ainsi qu’au chargement continu avec Snowpipe.

Recommandations générales en matière de dimensionnement de fichiers

Le nombre d’opérations de chargement exécutées en parallèle ne peut pas dépasser le nombre de fichiers de données à charger. Pour optimiser le nombre d’opérations parallèles pour un chargement, nous vous recommandons de générer des fichiers de données d’une taille d’environ 100-250 MB (ou plus) de façon compressée.

Note

Le chargement de fichiers très volumineux (par exemple 100 GB ou plus) n’est pas recommandé.

Si vous devez charger un fichier volumineux, portez une attention particulière à la valeur de l’option de copie ON_ERROR. Abandonner ou ignorer un fichier en raison d’un petit nombre d’erreurs pourrait entraîner des retards et des crédits gaspillés. En outre, si une opération de chargement de données se poursuit au-delà de la durée maximale autorisée de 24 heures, elle peut être interrompue sans qu’aucune partie du fichier ne soit validée.

Agrégez des fichiers plus petits afin de minimiser la surcharge de traitement pour chaque fichier. La division de gros fichiers en un plus grand nombre de fichiers plus petits répartit la charge entre les ressources de calcul d’un entrepôt actif. Le nombre de fichiers de données traités en parallèle est déterminé par le nombre de ressources de calcul d’un entrepôt. Nous vous recommandons de diviser les fichiers volumineux par ligne pour éviter que les enregistrements s’étendent sur plusieurs morceaux.

Si votre base de données source ne vous permet pas d’exporter les fichiers de données en petites parties, vous pouvez utiliser un utilitaire tiers pour fractionner les gros fichiers CSV.

Linux ou macOS

L’utilitaire split vous permet de diviser un fichier CSV en plusieurs petits fichiers.

Syntaxe :

split [-a suffix_length] [-b byte_count[k|m]] [-l line_count] [-p pattern] [file [name]]
Copy

Pour plus d’informations, tapez man split dans une fenêtre de terminal.

Exemple :

split -l 100000 pagecounts-20151201.csv pages
Copy

Cet exemple divise un fichier nommé pagecounts-20151201.csv par longueur de ligne. Supposons que le gros fichier unique soit de 8 GB et contienne 10 millions de lignes. Divisé par 100 000, chacun des 100 plus petits fichiers est de 80 MB (10 millions/100 000 = 100). Les fichiers fractionnés sont nommés pagessuffix.

Windows

Windows n’inclut pas d’utilitaire natif de fractionnement de fichiers ; cependant, Windows prend en charge de nombreux outils et scripts tiers qui peuvent fractionner de gros fichiers de données.

Réduction de la taille des objets supérieurs à 16 MB avant le chargement

Note

Pour utiliser cette fonction, vous devez activer le bundle de changements de comportement 2024_08 dans votre compte.

Pour activer ce bundle dans votre compte, exécutez l’instruction suivante :

SELECT SYSTEM$ENABLE_BEHAVIOR_CHANGE_BUNDLE('2024_08');
Copy

Une erreur se produit si vous essayez de charger un objet supérieur à 16 MB à partir d’un fichier sur une zone de préparation dans l’un des types de colonnes suivants :

L’erreur suivante se produit car la taille maximale d’un objet stocké dans une colonne est de 16 MB :

Max LOB size (16777216) exceeded

Auparavant, cette erreur se produisait également si vous tentiez d”interroger un fichier sur une zone de préparation, et que le fichier contenait des objets supérieurs à 16 MB.

Bien que vous ne puissiez toujours pas stocker des objets supérieurs à 16 MB dans une colonne, vous pouvez désormais interroger des objets jusqu’à 128 MB dans des fichiers sur une zone de préparation. Vous pouvez ensuite réduire la taille des objets avant de les stocker dans des colonnes. Plus aucune erreur ne se produit lorsque vous interrogez un fichier contenant des objets supérieurs à 16 MB mais inférieurs à 128 MB.

Par exemple, vous pouvez diviser de grands objets sur plusieurs colonnes ou lignes, transformer des JSON imbriqués dans un format tabulaire ou simplifier des géométries complexes.

Exemple : chargement d’un fichier JSON volumineux dans des lignes distinctes

En général, les ensembles de données JSON sont une simple concaténation de plusieurs documents. La sortie JSON de certains logiciels est composée d’un seul grand tableau contenant plusieurs enregistrements. Il n’est pas nécessaire de séparer les documents par des sauts de ligne ou des virgules, bien que les deux soient pris en charge.

Si les données dépassent 16 MB, activez l’option de format de fichier STRIP_OUTER_ARRAY pour la commande COPY INTO <table> afin de supprimer la structure de tableau externe et de charger les enregistrements dans des lignes de table séparées :

COPY INTO <table>
  FROM @~/<file>.json
  FILE_FORMAT = (TYPE = 'JSON' STRIP_OUTER_ARRAY = true);
Copy

Exemple : chargement et division d’objets JSON à partir d’un fichier Parquet

Supposons que vous chargez un fichier Parquet depuis une zone de préparation et que le fichier Parquet contient un objet JSON supérieur à 16 MB :

{
  "ID": 1,
  "CustomerDetails": {
    "RegistrationDate": 158415500,
    "FirstName": "John",
    "LastName": "Doe",
    "Events": [
      {
        "Type": "LOGIN",
        "Time": 1584158401,
        "EventID": "NZ0000000001"
      },
      /* ... */
      /* this array contains thousands of elements */
      /* with total size exceeding 16 MB */
      /* ... */
      {
        "Type": "LOGOUT",
        "Time": 1584158402,
        "EventID": "NZ0000000002"
      }
    ]
  }
}
Copy

L’exemple suivant crée une table pour stocker les données du fichier et charge les données dans la table. Étant donné que la taille du tableau d’événements peut dépasser 16 MB, l’exemple étend le tableau d’événements dans des lignes distinctes (une pour chaque élément du tableau).

CREATE OR REPLACE TABLE mytable AS
  SELECT
    t1.$1:ID AS id,
    t1.$1:CustomerDetails:RegistrationDate::VARCHAR AS RegistrationDate,
    t1.$1:CustomerDetails:FirstName::VARCHAR AS First_Name,
    t1.$1:CustomerDetails:LastName::VARCHAR AS as Last_Name,
    t2.value AS Event
  FROM @json t1,
    TABLE(FLATTEN(INPUT => $1:CustomerDetails:Events)) t2;
Copy

Voici un exemple du contenu du tableau résultant.

+----+-------------------+------------+------------+------------------------------+
| ID | REGISTRATION_DATE | FIRST_NAME | LAST_NAME  | EVENT                        |
|----+-------------------+------------+------------+------------------------------|
| 1  | 158415500         | John       | Doe        | {                            |
|    |                   |            |            |   "EventID": "NZ0000000001", |
|    |                   |            |            |   "Time": 1584158401,        |
|    |                   |            |            |   "Type": "LOGIN"            |
|    |                   |            |            | }                            |
|                     ... thousands of rows ...                                   |
| 1  | 158415500         | John       | Doe        | {                            |
|    |                   |            |            |   "EventID": "NZ0000000002", |
|    |                   |            |            |   "Time": 1584158402,        |
|    |                   |            |            |   "Type": "LOGOUT"           |
|    |                   |            |            | }                            |
+----+-------------------+------------+------------+------------------------------+

Insertion de résultats FLATTEN dans une table existante

Pour insérer les résultats de la fonction FLATTEN dans une table existante, utilisez une instruction INSERT. Par exemple :

CREATE OR REPLACE TABLE mytable (
  id VARCHAR,
  registration_date VARCHAR(16777216),
  first_name VARCHAR(16777216),
  last_name VARCHAR(16777216),
  event VARCHAR(16777216));

INSERT INTO mytable
  SELECT
    t1.$1:ID,
    t1.$1:CustomerDetails:RegistrationDate::VARCHAR,
    t1.$1:CustomerDetails:FirstName::VARCHAR,
    t1.$1:CustomerDetails:LastName::VARCHAR,
    t2.value
  FROM @json t1,
    TABLE(FLATTEN(INPUT => $1:CustomerDetails:Events)) t2;
Copy

Exemple : chargement et division d’un fichier XML

Supposons que vous chargez un fichier XML depuis une zone de préparation et qu’il contient un objet XML supérieur à 16 MB :

<?xml version='1.0' encoding='UTF-8'?>
<osm version="0.6" generator="osmium/1.14.0">
  <node id="197798" version="17" timestamp="2021-09-06T17:01:27Z" />
  <node id="197824" version="7" timestamp="2021-08-04T23:17:18Z" >
    <tag k="highway" v="traffic_signals"/>
  </node>
  <!--  thousands of node elements with total size exceeding 16 MB -->
  <node id="197826" version="4" timestamp="2021-08-04T16:43:28Z" />
</osm>
Copy

L’exemple suivant crée une table pour stocker les données du fichier et charge les données dans la table. Étant donné que la taille du fichier XML peut dépasser 16 MB, l’exemple étend chaque node dans des lignes distinctes.

CREATE OR REPLACE TABLE mytable AS
  SELECT
    value:"@id" AS id,
    value:"@version" AS version,
    value:"@timestamp"::datetime AS TIMESTAMP,
    value:"$" AS tags
  FROM @mystage,
    LATERAL FLATTEN(INPUT => $1:"$")
  WHERE value:"@" = 'node';
Copy

Voici un exemple du contenu du tableau résultant.

+--------+---------+-------------------------+---------------------------------------------+
| ID     | VERSION | TIMESTAMP               | TAGS                                        |
|--------+---------+-------------------------+---------------------------------------------|
| 197798 | 17      | 2021-09-06 17:01:27.000 | ""                                          |
| 197824 | 7       | 2021-08-04 23:17:18.000 | <tag k="highway" v="traffic_signals"></tag> |
|                   ... thousands of rows ...                                              |
| 197826 | 4       | 2021-08-04 16:43:28.000 | ""                                          |
+--------+---------+-------------------------+---------------------------------------------+

Exemple : chargement et simplification de grands objets géospatiaux avant leur stockage

Supposons que vous chargez un fichier Parquet depuis une zone de préparation et que le fichier Parquet contient un objet géospatial supérieur à 16 MB. Vous pouvez charger le fichier depuis la zone de préparation et simplifier l’objet géospatial (en utilisant ST_SIMPLIFY) avant de stocker l’objet :

CREATE OR REPLACE TABLE mytable AS
  SELECT
    ST_SIMPLIFY($1:geo, 10) AS geo
  FROM @mystage;
Copy

Exemple : utilisation de COPY INTO < table>

Si vous devez utiliser COPY INTO <table> pour charger des données à partir d’un fichier sur une zone de préparation, vous ne pouvez pas utiliser FLATTEN pour diviser les gros objets. Utilisez plutôt SELECT. Par exemple :

CREATE OR REPLACE TABLE mytable (
  id VARCHAR,
  registration_date VARCHAR,
  first_name VARCHAR,
  last_name VARCHAR);

COPY INTO mytable (
  id,
  registration_date,
  first_name,
  last_name
) FROM (
    SELECT
      $1:ID,
      $1:CustomerDetails::OBJECT:RegistrationDate::VARCHAR,
      $1:CustomerDetails::OBJECT:FirstName::VARCHAR,
      $1:CustomerDetails::OBJECT:LastName::VARCHAR
    FROM @mystage
);
Copy

Chargements de données en continu (c.-à-d., Snowpipe) et dimensionnement de fichiers

Snowpipe est conçu pour charger de nouvelles données typiquement dans la minute qui suit l’envoi d’une notification de fichier ; cependant, le chargement peut prendre beaucoup plus de temps pour de très gros fichiers ou dans les cas où une quantité inhabituelle de ressources de calcul est nécessaire pour décompresser, déchiffrer et transformer les nouvelles données.

En plus de consommer des ressources, les frais généraux de gestion des fichiers dans la file d’attente de chargement interne sont inclus dans les coûts d’utilisation facturés pour Snowpipe. Ces frais généraux augmentent en fonction du nombre de fichiers mis en file d’attente pour le chargement. Ces frais généraux apparaissent comme des frais de Snowpipe dans votre relevé de facturation parce que Snowpipe est utilisé pour les notifications d’événements pour les rafraîchissements automatiques des tables externes.

Pour optimiser l’expérience de chargement avec Snowpipe, nous vous recommandons de suivre les recommandations de dimensionnement des fichiers décrites dans Meilleures pratiques et limitations en matière de taille de fichier (dans cette rubrique). Le chargement de fichiers de données d’environ 100-250 MB ou plus réduit les frais généraux par rapport à la quantité totale de données chargées au point où les frais généraux sont négligeables.

S’il faut plus d’une minute pour accumuler des MBs de données dans votre application source, pensez à créer un nouveau fichier de données (potentiellement plus petit) une fois par minute. Cette approche conduit généralement à un bon équilibre entre le coût (c’est-à-dire les ressources consacrées à la gestion des files d’attente et la charge réelle) et la performance (c’est-à-dire la latence de la charge).

La création de fichiers de données plus petits et leur mise en zone de préparation dans un stockage Cloud plus fréquente qu’une fois par minute présentent les inconvénients suivants :

  • Une réduction de la latence entre la mise en zone de préparation et le chargement des données ne peut être garantie.

  • Les frais généraux de gestion des fichiers dans la file d’attente de chargement interne sont inclus dans les coûts d’utilisation facturés pour Snowpipe. Ces frais généraux augmentent en fonction du nombre de fichiers mis en file d’attente pour le chargement.

Différents outils permettent d’agréger et de regrouper des fichiers de données par lots. Option pratique : Amazon Data Firehose. Firehose permet de définir à la fois la taille de fichier souhaitée, appelée taille tampon, et l’intervalle d’attente après lequel un nouveau fichier est envoyé (vers le stockage Cloud, dans ce cas), appelé intervalle tampon. Pour plus d’informations, voir la documentation sur Amazon Data Firehose. Si votre application source accumule généralement suffisamment de données en une minute pour remplir des fichiers plus importants que la taille maximale recommandée pour un traitement parallèle optimal, vous pouvez réduire la taille de tampon pour déclencher la livraison de fichiers plus petits. Maintenir le réglage de l’intervalle tampon à 60 secondes (la valeur minimale) permet d’éviter de créer trop de fichiers ou d’augmenter la latence.

Préparation de fichiers texte délimités

Tenez compte des directives suivantes lorsque vous préparez vos fichiers texte délimités (CSV) au chargement :

  • UTF-8 est le jeu de caractères par défaut. Cependant, des encodages supplémentaires sont pris en charge. Utilisez l’option de format de fichier ENCODING pour spécifier le jeu de caractères des fichiers de données. Pour plus d’informations, voir CREATE FILE FORMAT.

  • Les champs qui contiennent des caractères de délimitation doivent être entourés de guillemets (simples ou doubles). Si les données contiennent des guillemets simples ou doubles, alors ces guillemets doivent faire l’objet d’un échappement.

  • Les retours chariot sont généralement introduits sur les systèmes Windows en conjonction avec un caractère de saut de ligne pour marquer la fin d’une ligne (\r \n). Les champs qui contiennent des retours chariot doivent également être entourés de guillemets (simples ou doubles).

  • Le nombre de colonnes dans chaque ligne doit être cohérent.

Fichiers de données semi-structurées et mise en colonnes

Lorsque des données semi-structurées sont insérées dans une colonne VARIANT, Snowflake applique certaines règles pour extraire autant de données que possible sous forme de colonnes. Le reste des données est stocké sous la forme d’une seule colonne dans une structure semi-structurée analysée.

Par défaut, Snowflake extrait un maximum de 200 éléments par partition, par table. Pour augmenter cette limite, contactez le support Snowflake.

Éléments non extraits

Les éléments présentant les caractéristiques suivantes ne sont pas extraits dans une colonne :

  • Les éléments qui contiennent ne serait-ce qu’une seule valeur « null » ne sont pas extraits dans une colonne. Cela s’applique aux éléments avec des valeurs « null » et non aux éléments avec des valeurs manquantes, qui sont représentés sous forme de colonne.

    Cette règle garantit qu’aucune information n’est perdue (c’est-à-dire que la différence entre les valeurs VARIANT « null » et les valeurs SQL NULL n’est pas perdue).

  • Éléments contenant plusieurs types de données. Par exemple :

    L’élément foo dans une ligne contient un nombre :

    {"foo":1}
    
    Copy

    Le même élément dans une autre ligne contient une chaîne :

    {"foo":"1"}
    
    Copy

Impact de l’extraction sur les requêtes

Lorsque vous interrogez un élément semi-structuré, le moteur d’exécution de Snowflake se comporte différemment selon qu’un élément a été extrait ou non.

  • Si l’élément a été extrait dans une colonne, le moteur analyse uniquement la colonne extraite.

  • Si l’élément n’a pas été extrait dans une colonne, le moteur doit analyser toute la structure JSON, puis pour chaque ligne, parcourir la structure pour émettre les valeurs. Cela a un impact sur les performances.

Pour éviter l’impact sur les performances des éléments non extraits, procédez comme suit :

  • Extrayez les éléments de données semi-structurées contenant des valeurs « null » dans des colonnes relationnelles avant leur chargement.

    Sinon, si les valeurs « null » de vos fichiers indiquent des valeurs manquantes et n’ont pas d’autre signification particulière, nous vous recommandons de définir l”option de format de fichier STRIP_NULL_VALUES sur TRUE lors du chargement des fichiers de données semi-structurées. Cette option supprime les éléments OBJECT ou ARRAY contenant des valeurs « nulles ».

  • Assurez-vous que chaque élément unique stocke des valeurs d’un seul type de données natif du format (par exemple, une chaîne ou un nombre pour JSON).

Lignes directrices sur les données numériques

  • Évitez les caractères imbriqués, comme les virgules (p. ex, 123,456).

  • Si un nombre comprend une composante fractionnaire, il doit être séparé de la partie entière du nombre par un point décimal (p. ex, 123456.789).

  • Oracle uniquement. Les types NUMBER ou NUMERIC d’Oracle permettent une échelle arbitraire, ce qui signifie qu’ils acceptent des valeurs avec des composantes décimales même si le type de données n’a pas été défini avec une précision ou une échelle. Dans Snowflake, en revanche, les colonnes conçues pour les valeurs à composantes décimales doivent être définies avec une échelle afin de préserver la portion décimale.

Lignes directrices sur les données d’horodatage et de date

  • Pour plus d’informations sur les formats pris en charge pour les données de date, d’heure et d’horodatage, voir Formats d’entrée et de sortie de la date et de l’heure.

  • Oracle uniquement. Le type de données DATE d’Oracle peut contenir des informations de date ou d’horodatage. Si votre base de données Oracle comprend des colonnes DATE qui stockent également des informations temporelles, associez ces colonnes à un type de données TIMESTAMP dans Snowflake plutôt que DATE.

Note

Snowflake vérifie les valeurs temporelles des données au moment du chargement. Des valeurs de date, d’heure et d’horodatage non valides (par exemple, 0000-00-00) produisent une erreur.