データファイルの準備

このトピックでは、データファイルをロード用に準備するためのベストプラクティス、一般的なガイドライン、および重要な考慮事項について説明します。

このトピックの内容:

ファイルサイズのベストプラクティスと制限事項

最適なロードパフォーマンスとサイズ制限を回避するために、次のデータファイルのサイズ設定ガイドラインを考慮します。これらの推奨事項は、Snowpipeを使用した連続ロードだけでなく、一括データロードにも該当します。

一般的なファイルサイズの推奨事項

並行して実行されるロード操作の数は、ロードされるデータファイルの数を超えることはできません。ロードの並列操作の数を最適化するには、 圧縮された データファイルのサイズをおよそ100から250 MB(またはそれ以上)にすることをお勧めします。

注釈

非常に大きなファイル(例: 100 GB 以上)をロードすることはお勧めしません。

大きなファイルをロードする必要がある場合は、 ON_ERROR コピーオプションの値を慎重に検討してください。少数のエラーでファイルを中止またはスキップすると、遅延が発生し、クレジットが無駄になる可能性があります。さらに、データのロード操作が最大許容時間の24時間を超えて継続した場合は、ファイルの一部がコミットされずに中止される可能性があります。

小さいファイルを集約して、各ファイルの処理オーバーヘッドを最小限に抑えます。アクティブなウェアハウス内のコンピューティングリソース間でロードを分散するために、大きなファイルを多数の小さなファイルに分割します。並行して処理されるデータファイルの数は、ウェアハウス内のコンピューティングリソースの量によって決まります。記録がチャンクにまたがることを避けるために、大きなファイルは行で分割することをお勧めします。

ソースデータベースでデータファイルを小さなチャンクでエクスポートできない場合、サードパーティのユーティリティを使用して大きな CSV ファイルを分割できます。

Linuxまたは macOS

split ユーティリティを使用すると、 CSV ファイルを複数の小さなファイルに分割できます。

構文:

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

詳細については、ターミナルウィンドウに man split と入力します。

例:

split -l 100000 pagecounts-20151201.csv pages
Copy

この例では、 pagecounts-20151201.csv という名前のファイルを行の長さで分割します。8 GB の大きな単一ファイルに、10百万行が含まれているとします。100,000で分割すると、100個の小さいファイルのそれぞれは80 MB (10百万/100,000 = 100)です。分割ファイルの名前は pagessuffix です。

Windows

Windowsには、ネイティブファイル分割ユーティリティは含まれていません。ただしWindowsは、大きなデータファイルを分割できる多くのサードパーティツールとスクリプトをサポートしています。

ロード前に16 MB より大きいオブジェクトのサイズを縮小する

注釈

この機能を使用するには、アカウントで2024_08動作変更バンドルを有効にする必要があります。

アカウントでこのバンドルを有効にする には、以下のステートメントを実行します。

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

ステージ上のファイルから16 MB を超えるオブジェクトを、以下のタイプの列のいずれかにロードしようとするとエラーが発生します:

列に格納されるオブジェクトの最大サイズが16 MB であるため、以下のエラーが発生します:

Max LOB size (16777216) exceeded

過去には、このエラーは、 ステージ上のファイルをクエリ しようとした場合にも発生し、ファイルには16 MB より大きなオブジェクトが含まれていました。

列に16 MB 以上のオブジェクトを格納することはまだできませんが、ステージ上のファイルに128 MB までのオブジェクトをクエリできるようになりました。そして、列にオブジェクトを格納する前に、オブジェクトのサイズを小さくすることができます。16 MB より大きく128 MB より小さいオブジェクトを含むファイルをクエリしても、エラーが発生しなくなりました。

たとえば、大きなオブジェクトを複数の列や行に分割したり、ネストした JSON を表形式に変換したり、複雑な形状を単純化したりできます。

例: 大きな JSON ファイルを分割してロードする

一般に、 JSON データセットは、複数のドキュメントの単純な連結です。一部のソフトウェアからの JSON 出力は、複数の記録を含む単一の巨大な配列で構成されています。両方ともサポートされていますが、ドキュメントを改行またはコンマで区切る必要はありません。

データが 16 MB を超える場合は、 COPY INTO <テーブル> コマンドの STRIP_OUTER_ARRAY ファイル形式オプションを有効にして、外部配列構造を削除し、記録を別のテーブル行にロードします。

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

例: Parquetファイルからの JSON オブジェクトのロードと分割

ステージからParquetファイルをロードしているとき、Parquetファイルに16 MB 以上のサイズの JSON オブジェクトが含まれていたとします:

{
  "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

次の例では、ファイルのデータを格納するテーブルを作成し、そのテーブルにデータをロードしています。イベント配列のサイズが16 MB を超えることがあるため、この例では、イベント配列を別々の行(各配列要素に1つずつ)に展開しています。

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

以下は、出来上がったテーブルの内容の例です。

+----+-------------------+------------+------------+------------------------------+
| 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"           |
|    |                   |            |            | }                            |
+----+-------------------+------------+------------+------------------------------+

FLATTEN の結果を既存のテーブルに挿入する

FLATTEN 関数の結果を既存のテーブルに挿入するには、 INSERT ステートメントを使用します。例:

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

例: XML のロードと分割

ステージから XML ファイルをロードしているとき、 XML オブジェクトが含まれていて、それが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

次の例では、ファイルのデータを格納するテーブルを作成し、そのテーブルにデータをロードしています。XML のサイズが16 MB を超えることがあるため、この例では各 node を別々の行に展開しています。

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

以下は、出来上がったテーブルの内容の例です。

+--------+---------+-------------------------+---------------------------------------------+
| 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 | ""                                          |
+--------+---------+-------------------------+---------------------------------------------+

例: 大きな地理空間オブジェクトを保存する前にロードして簡素化する

ステージからParquetファイルをロードしているとき、そのParquetファイルに16 MB を超える地理空間オブジェクトが含まれていたとします。オブジェクトを保存する前に、ステージからファイルをロードし、(ST_SIMPLIFY を使用して)地理空間オブジェクトを簡略化することができます:

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

例: COPY INTO <テーブル>の使用

COPY INTO <テーブル> を使用してステージ上のファイルからデータをロードする必要がある場合、 FLATTEN を使用して大きなオブジェクトを分割することはできません。代わりに、 SELECT を使用してください。例:

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

連続データロード(Snowpipeなど)とファイルサイズ

Snowpipeは、通常、ファイル通知が送信されてから1分以内に新しいデータをロードするように設計されています。ただし、非常に大きなファイルの場合や、新しいデータの圧縮解除、復号化、変換に通常以上のコンピューティングリソースが必要な場合は、ロードにかなりの時間がかかることがあります。

リソースの消費に加えて、内部ロードキュー内のファイルを管理するためのオーバーヘッドも、Snowpipeに請求される使用コストに含まれます。このオーバーヘッドは、ロードのためにキューに入れられたファイルの数に連動して増加します。Snowpipeは外部テーブルの自動更新のイベント通知に使用されるため、このオーバーヘッド料金は請求明細書にSnowpipe料金として表示されます。

Snowpipeで最も効率的で費用対効果の高いロードエクスペリエンスを得るために、 ファイルサイズ設定のベストプラクティスと制限事項 (このトピック内)のファイルサイズ設定の推奨事項に従うことをお勧めします。およそ100から250 MB 以上のデータファイルをロードすると、ロードされるデータの合計量に比べて、オーバーヘッドコストが重要ではなくなるまでオーバーヘッド料金が削減されます。

ソースアプリケーションに MBs のデータを蓄積するのに1分以上かかる場合は、新しい(潜在的に小さい)データファイルを1分に1回作成することを検討します。通常、このアプローチは、コスト(つまり、Snowpipeキュー管理に費やされるリソースと実際のロード)とパフォーマンス(つまり、ロードレイテンシー)のバランスにつながります。

小さなデータファイルを作成し、1分に1回よりも頻繁にクラウドストレージにステージングすることには、次の欠点があります。

  • データのステージングとロードの間のレイテンシーの縮小は保証できません。

  • 内部ロードキュー内のファイルを管理するためのオーバーヘッドは、Snowpipeに請求される使用コストに含まれています。このオーバーヘッドは、ロードのためにキューに入れられたファイルの数に連動して増加します。

さまざまなツールでデータファイルを集約およびバッチ処理できます。便利なオプションの1つは、Amazon Data Firehoseです。Firehoseでは、 バッファーサイズ と呼ばれる目的のファイルサイズと、 バッファーインターバル と呼ばれる新しいファイルが(この場合はクラウドストレージに)送信されるまでの待機間隔の両方を定義できます。詳細については、 Amazon Data Firehoseドキュメント をご参照ください。通常、ソースアプリケーションが1分以内に十分なデータを蓄積して、最適な並列処理のために推奨される最大値よりも大きいファイルを取り込む場合は、小さいファイルをトリガーするようにバッファーサイズを縮小することができます。バッファー間隔の設定を60秒(最小値)に維持すると、ファイルの作成が過剰になったり、待ち時間が長くなったりすることを回避できます。

区切りテキストファイルの準備

ロードする区切りテキスト(CSV)ファイルを準備するときは、次のガイドラインを考慮してください。

  • UTF-8がデフォルトの文字セットですが、追加のエンコードがサポートされています。 ENCODING ファイル形式オプションを使用して、データファイルの文字セットを指定します。詳細については、 CREATE FILE FORMAT をご参照ください。

  • 区切り文字を含むフィールドは、引用符(一重または二重)で囲む必要があります。データに一重引用符または二重引用符が含まれる場合、それらの引用符はエスケープする必要があります。

  • キャリッジリターンは、通常、Windowsシステム上で行末を示す改行文字(\r \n)と組み合わせて導入されます。キャリッジリターンを含むフィールドも引用符で囲む必要があります(一重または二重)。

  • 各行の列数は一貫している必要があります。

半構造化データファイルと列指向化

半構造化データが VARIANT 列に挿入されると、Snowflakeは特定のルールを使用して、可能な限り多くのデータを列指向形式で抽出します。残りのデータは、解析された半構造化構造の単一の列として格納されます。

デフォルトでは、Snowflakeはテーブルごと、パーティションごとに最大200個の要素を抽出します。この制限を増やすには、 Snowflakeサポート にお問い合わせください。

抽出されない要素

現在、次の特性を持つ要素は列に抽出 されません

  • 単一の「null」値を含む要素は列に抽出されません。これは、値が欠落している要素ではなく、列指向形式で表される「null」値をともなう要素に適用されます。

    このルールは、情報が失われないことを保証します(つまり、 VARIANT「null」値と SQL NULL 値の差は失われません)。

  • 複数のデータ型を含む要素。例:

    1行の foo 要素には数字が含まれています。

    {"foo":1}
    
    Copy

    別の行の同じ要素には文字列が含まれます。

    {"foo":"1"}
    
    Copy

抽出がクエリに与える影響

半構造化要素に対してクエリを実行すると、Snowflakeの実行エンジンは、要素が抽出されたかどうかによって異なる動作をします。

  • 要素が列に抽出された場合、エンジンは抽出された列のみをスキャンします。

  • 要素が列に抽出 されなかった 場合、実行エンジンは JSON 構造全体をスキャンし、各行で構造を走査して値を出力する必要があります。これはパフォーマンスに影響します。

抽出されなかった要素へのパフォーマンスの影響を避けるには、次を行います。

  • それらをロードする 前に、「null」値を含む半構造化データ要素をリレーショナル列に抽出します。

    または、ファイルの「null」値が欠損値を示しており、他の特別な意味がない場合、半構造化データファイルをロードするときに ファイル形式オプション STRIP_NULL_VALUES を TRUE に設定することをお勧めします。このオプションは、「null」値を含む OBJECT 要素または ARRAY 要素を削除します。

  • それぞれの一意の要素が、その形式に固有である単一のデータ型の値を格納していることを確認してください(例えば、 JSON の文字列または数)。

数値データのガイドライン

  • コンマなどの埋め込み文字(例: 123,456)は避けます。

  • 数値に小数部が含まれる場合、整数部と小数点で区切る必要があります(例: 123456.789)。

  • Oracleのみ。Oracleの NUMBER または NUMERIC 型は、任意のスケールを許可します。つまり、データ型が精度またはスケールで定義されていなくても、小数成分を持つ値を受け入れます。一方、Snowflakeでは、小数部分を持つ値用に設計された列は、小数部分を保持するスケールで定義する必要があります。

日付とタイムスタンプのデータガイドライン

  • 日付、時刻、タイムスタンプデータのサポートされている形式については、 日付と時刻の入出力形式 をご参照ください。

  • Oracleのみ。Oracle DATE データ型には、日付 または タイムスタンプ情報を含めることができます。Oracleデータベースに時間関連情報も保存する DATE 列が含まれている場合、これらの列を DATEではなくSnowflakeの TIMESTAMP データ型にマップします。

注釈

Snowflakeは、ロード時に一時的なデータ値をチェックします。無効な日付、時刻、およびタイムスタンプ値(例: 0000-00-00)ではエラーが発生します。