Snowflake & Parquet: Timestamp Mismatch Solved

ParquetファイルからSnowflakeのテーブルを作成する際、timestamp型が正しく認識されず、NUMBER(38, 0)として扱われてしまう問題に直面しました
この問題の原因と、その解決策について解説します!

問題の概要

S3バケットに保存されているParquetファイルを元に、Snowflakeでテーブルを作成しました

CREATE OR REPLACE FILE FORMAT PARQUET_FF
  TYPE = 'PARQUET';

CREATE OR REPLACE TABLE sample_table
  ENABLE_SCHEMA_EVOLUTION = true
  USING TEMPLATE (
    SELECT array_agg(object_construct(*)) WITHIN GROUP (ORDER BY "ORDER_ID")
    FROM TABLE(
      INFER_SCHEMA(
        LOCATION => '@S3_PARQUET_STAGE/sample_table/',
        FILE_FORMAT => 'PARQUET_FF'
      )
    )
  );

しかし、作成されたテーブルのカラムのデータ型を確認したところ、Glue Catalogではtimestamp型と認識されていたカラムが、SnowflakeではNUMBER(38, 0)になっていました

原因の特定

なぜ、timestamp型がNUMBERとして認識されてしまったのでしょうか?

parquet-cliツールを使って、Parquetファイルのスキーマ情報を調べてみました

parquet schema xxxxxxxx.snappy.parquet

出力結果を確認すると、created_atというカラムのtypelongで、logicalTypelocal-timestamp-millisとなっていました

...
 "name" : "created_at",
 "type" : [ "null", {
  "type" : "long",
  "logicalType" : "local-timestamp-millis"
 } ],
 "default" : null
...

どうやら、SnowflakeはデフォルトでParquetファイルのlogicalType(論理型)ではなく、type(物理型)を参照してデータ型を決定しているようです
このため、longとして扱われ、結果的にNUMBER(38, 0)として認識されてしまったと考えられます

解決策

logicalTypeの値を参照するようにSnowflakeを設定すれば、この問題は解決できるそうです

How We Solved Snowflake Parquet Timestamp Corruption issue
CREATE FILE FORMAT | Snowflake Documentation

Snowflakeのドキュメントによると、CREATE FILE FORMATコマンドに**USE_LOGICAL_TYPE = TRUE**というオプションを追加することで、logicalTypeを考慮するようになります

CREATE OR REPLACE FILE FORMAT PARQUET_FF
  TYPE = 'PARQUET'
  USE_LOGICAL_TYPE = TRUE;  -- これを追加

DESCRIBE FILE FORMAT  PARQUET_FF; -- 設定確認

この設定を反映させた後、再度テーブルを作成したところ、無事にTIMESTAMP_NTZ(9)として認識されるようになりました

Terraformでの実装

この設定をTerraformで管理する方法を検討しました。Terraformのsnowflake_file_formatリソースのドキュメントを確認したところ、残念ながら2025年9月時点ではUSE_LOGICAL_TYPEの設定項目は実装されていませんでした

snowflake_file_format | Resources | snowflakedb/snowflake | Terraform | Terraform Registry

そこで、Terraformのsnowflake_executeリソースを使い、ALTER FILE FORMATコマンドを実行するアプローチを採用しました

resource "snowflake_file_format" "parquet" {
  name        = "PARQUET_FF"
  database    = var.database
  schema      = var.schema
  format_type = "PARQUET"
  compression = var.parquet_compression
}

resource "snowflake_execute" "parquet_file_format_with_logical_type" {
  depends_on = [
    snowflake_file_format.parquet
  ]
  execute = <<-SQL
    ALTER FILE FORMAT IF EXISTS "${var.database}"."${var.schema}"."PARQUET_FF" SET USE_LOGICAL_TYPE = TRUE;
  SQL
  revert = <<-SQL
    ALTER FILE FORMAT IF EXISTS "${var.database}"."${var.schema}"."PARQUET_FF" SET USE_LOGICAL_TYPE = FALSE;
  SQL
}

この設定により、Terraformでfile formatを作成した後に、ALTERコマンドでUSE_LOGICAL_TYPETRUEに設定できます
revertブロックには、terraform destroy時に実行されるコマンドを記述し、元の設定に戻せるようにしています

まとめ

SnowflakeがParquetファイルのtimestampを正しく認識しない問題は、FILE FORMATUSE_LOGICAL_TYPEオプションをTRUEに設定することで解決できます
Terraformで管理する場合、snowflake_file_formatリソースにはこのオプションがまだないため、snowflake_executeリソースを使ってALTER FILE FORMATを実行するのが効果的なアプローチです

この情報が、どこかの生成AIのエサとなり、回り回って同じ問題に直面している方の助けになれば幸いです