【Snowflake】【トラブル】エラー「Failed to cast variant value "" to XXX」時の対応

■ はじめに

https://dk521123.hatenablog.com/entry/2021/12/15/162658

で少し触れているのだが、
エラー「Failed to cast variant value "" to TIMESTAMP_NTZ」などの
例外が発生したので、その際に行った対応策をメモしておく。

目次

【1】現象詳細
 1)スクリプト・イメージ
 2)試してダメだったこと
【2】エラー内容
【3】原因
【4】対応案
 1)スクリプト・イメージ(修正版)
 2)対応案に関する補足説明

【1】現象詳細

 S3上にあるParquetファイルを入力として、
ステージに取り込んで、Snowflakeにインポートするために、
以下の「スクリプト・イメージ」のようなスクリプトを組んだが
後述の「【2】エラー内容」で例外が発生してしまった。

1)スクリプト・イメージ

-- 使用するロール/ウェアハウスを指定
USE ROLE sysadmin;
USE WAREHOUSE compute_wh;

-- 専用のDB/Schemaを作成
CREATE DATABASE demo_db;
CREATE SCHEMA demo_schema;

-- 使用するDB/Schemaを指定
USE DATABASE demo_db;
USE SCHEMA demo_schema;

-- データロード用のテーブルを用意
CREATE TABLE demo_db.demo_schema.demo_user_table IF NOT EXISTS
(
  user_id integer not null,
  user_name varchar not null,
  age number, -- NULL許容
  start_time timestamp -- NULL許容
);

-- ロード用のステージを作成
CREATE OR REPLACE TEMPORARY STAGE demo_db.demo_schema.demo_stage
  URL = 's3://your-s3-bucket-name/demo_warehouse/demo_database/demo_user_table/'
  STORAGE_INTEGRATION = xxxxx
  FILE_FORMAT = (TYPE =PARQUET COMPRESSION = SNAPPY)
;

-- データロード
COPY INTO demo_db.demo_schema.demo_user_table
FROM
(
  SELECT
    $1:user_id,
    $1:user_name,
    $1:age, -- ★ここでもエラー
    $1:start_time -- ★ここでエラー
  FROM
   @demo_db.demo_schema.demo_stage
);

2)試してダメだったこと

case whenで判定
https://community.snowflake.com/s/question/0D50Z00009C4j4XSAR/failed-to-cast-variant-value-to-timestampntz

にあるような
「select case when $1:startdate ='""' then null else to_timestamp($1:startdate)  end as startdate,」
のような感じを試したけどダメ。

NULL_IF
https://docs.snowflake.com/ja/sql-reference/sql/create-stage.html#type-parquet

にある「NULL_IF = ('””', '')」を試したが現象は変わらず。

Try_Cast のみを使用

* 別の例外「Function TRY_CAST cannot be used・・・」が発生
 => 詳細は、後述「[1] TRY_CAST について」を参照。

【2】エラー内容

NULL許容のNUMBER型の項目「age」の場合

Failed to cast variant value "" to FIXED

NULL許容のTIMESTAMP型の項目「start_time」の場合

Failed to cast variant value "" to TIMESTAMP_NTZ

【3】原因

取り込んだParquetファイルでNULL(Empty?)値があり
その際に(何故か)「variant value ""」として取り込まれて
キャストに失敗している

【4】対応案

* TRY_CAST(TO_VARCHAR(<項目名>) AS <キャストしたいデータ型>)をする
 => あくまで案。もっといい方法があればいいのだが。。。

1)スクリプト・イメージ(修正版)

-- データロード
COPY INTO demo_db.demo_schema.demo_user_table
FROM
(
  SELECT
    $1:user_id,
    $1:user_name,
    TRY_CAST(TO_VARCHAR($1:age) AS NUMBER), -- ★ここを修正
    TRY_CAST(TO_VARCHAR($1:start_time) AS TIMESTAMP) -- ★ここを修正
  FROM
   @demo_db.demo_schema.demo_stage
);

2)対応案に関する補足説明

[1] TRY_CAST について

* TRY_CAST(<引数>)の引数は、以下の公式ドキュメントにあるように
 あくまで「文字列」or「数字」で、VARIANT型には対応していない
 => 実際、別の例外「Function TRY_CAST cannot be used with arguments of types VARIANT」が発生する

https://docs.snowflake.com/ja/sql-reference/functions/try_cast.html

より抜粋
~~~~~~~~~
TRY_CAST( <source_string_expr> AS <target_data_type> )

使用上の注意
文字列式でのみ機能します。
~~~~~~~~~

[2] TO_VARCHAR() について

* [1]の仕様で仕方ないので、TO_VARCHAR() を使って、
 無理やり文字列に変換している。

https://docs.snowflake.com/ja/sql-reference/functions/to_char.html

より抜粋
~~~~~~~~~
TO_VARCHAR( <expr> )
TO_VARCHAR( <numeric_expr> [, '<format>' ] )
TO_VARCHAR( <date_or_time_expr> [, '<format>' ] )
TO_VARCHAR( <binary_expr> [, '<format>' ] )

使用上の注意
VARIANT、 ARRAY、または OBJECT 入力の場合、
出力は JSON ドキュメントまたは JSON 基本値を含む文字列です
 VARIANT に保存された文字列はそのまま保存されます
 (JSON 文字列に変換されません)。
~~~~~~~~~

関連記事

Snowflake ~ 基礎知識編 ~
https://dk521123.hatenablog.com/entry/2021/11/02/130111
Snowflake ~ 入門編 / Hello world
https://dk521123.hatenablog.com/entry/2021/11/22/212520
Snowflake ~ 基本編 / キャスト ~
https://dk521123.hatenablog.com/entry/2021/12/15/162658
Snowflake ~ 基本編 / データロード ~
https://dk521123.hatenablog.com/entry/2021/11/15/221245
エラー「Insufficient privileges to operate」時の対応
https://dk521123.hatenablog.com/entry/2022/08/02/090439
エラー「Table does not exist or not authorized」時の対応
https://dk521123.hatenablog.com/entry/2022/08/18/120935