外部表简介

外部表 是 Snowflake 的一项功能,您可以使用此功能查询存储在 外部暂存区 中的数据,就像数据位于 Snowflake 中的表内部一样。外部暂存区不是 Snowflake 的一部分,因此 Snowflake 不存储或管理暂存区。为强化您的安全态势,您可以为 出站专用连接 配置外部暂存区,以便使用专用连接访问外部表。

外部表允许您(在 Snowflake 中)存储某些文件级别元数据,包括文件名、版本标识符和相关属性。外部表可以访问以 COPY INTO <table> 命令支持的任何格式存储的数据,但 XML 除外。

外部表是只读的。您不能对外部表执行数据操作语言 (DML) 操作。但是,您可以使用外部表进行查询和联接操作。也可以创建针对外部表的视图。

查询外部表中的数据可能比查询本地存储在 Snowflake 中表内的数据慢。为提高查询性能,您可以使用基于外部表的 物化视图。要在处理 Parquet 文件时获得出色查询性能,请考虑改用 Apache Iceberg™ 表。

备注

如果 Snowflake 在查询操作期间扫描云存储中的文件时遇到错误,则会跳过该文件并继续扫描下一个文件。查询可以部分扫描文件并返回遇到错误之前扫描的行。

规划外部表的架构

以下部分将介绍在规划外部表时可用的选项。

读取时的架构

所有外部表都包括以下列:

VALUE:

表示外部文件中单个行的 VARIANT 类型列。

METADATA$FILENAME:

一个伪列,标识外部表中包含的每个暂存数据文件的名称,包括其在暂存区中的路径。

METADATA$FILE_ROW_NUMBER:

显示暂存数据文件中每个记录的行号的伪列。

要创建外部表,您只需略微了解源数据文件的文件格式和记录格式。不需要知道数据文件的架构。

备注

SELECT * 始终会返回 VALUE 列,其中所有常规或半结构化数据都投射到变体行。

虚拟列

如果您熟悉源数据文件的架构,则可以使用 VALUE 列和 METADATA$FILENAME 或 METADATA$FILE_ROW_NUMBER 伪列来创建用作表达式的额外虚拟列。扫描外部数据时,数据文件中任何指定字段或半结构化数据元素的数据类型必须与外部表中这些额外列的数据类型匹配。这一要求能够实现对外部数据进行强类型检查和架构验证。

常规文件大小调整建议

为了优化查询外部表时的并行扫描操作数,我们建议每种格式的文件或行组大小如下:

格式

推荐大小范围

备注

Parquet 文件

256 - 512 MB

Parquet 行组

16 - 256 MB

当 Parquet 文件包含多个行组时,Snowflake 可以在不同服务器中的每个行组上操作。为了提高查询性能,建议将 Parquet 文件的大小调整到建议的范围内;或者,如果需要较大的文件大小,可以在每个文件中包含多个行组。

所有其他受支持的文件格式

16 - 256 MB

为了在查询大型数据文件时优化性能,请创建和查询 外部表上的物化视图。

已分区的外部表

我们强烈建议对外部表进行分区,这需要使用逻辑路径整理好基础数据,其中包括日期、时间、国家/地区或路径中的类似维度。分区会使用分区列将外部表数据划分为多个部分。

外部表定义可以包括多个分区列,这些分区列会对外部数据强制使用多维结构。分区存储在外部表元数据中。

分区能够提升查询性能。由于外部数据被划分为单独的切片或部分,因此如果处理一小部分数据而不是扫描整个数据集,查询响应时间会更快。

根据您的个人应用场景,您可以执行以下任一操作:

  • 通过刷新为每个分区列定义表达式的外部表来自动添加新分区。

  • 手动添加新分区。

在创建外部表时,使用 CREATE EXTERNAL TABLE ... 定义分区列PARTITION BY 语法。创建外部表后,无法更改添加分区的方法。

以下各部分将更详细地解释用于添加分区的不同选项。有关示例,请参阅 CREATE EXTERNAL TABLE。

自动添加的分区

外部表创建器将新外部表中的分区列定义为用于解析存储在 METADATA$FILENAME 伪列中的路径或文件名信息的表达式。分区由与分区列表达式中的路径或文件名匹配的所有数据文件组成。

以下是根据表达式自动添加分区的 CREATE EXTERNAL TABLE 语法:

CREATE EXTERNAL TABLE
  <table_name>
     ( <part_col_name> <col_type> AS <part_expr> )
     [ , ... ]
  [ PARTITION BY ( <part_col_name> [, <part_col_name> ... ] ) ]
  ..

刷新外部表元数据时,Snowflake 会根据定义的分区列表达式计算和添加分区。默认情况下,创建对象时会自动刷新元数据。此外,对象所有者可以将元数据配置为在外部暂存区中有新的或更新后的数据文件可用时自动刷新。所有者也可以通过执行 ALTER EXTERNAL TABLE ... 来手动刷新元数据REFRESH 命令克隆包含混合表的数据库。

手动添加的分区

外部表创建者将新外部表的分区类型确定为 用户定义,并仅指定分区列的数据类型。如果您希望有选择地添加和移除分区,而不是为外部存储位置中与表达式匹配的所有新文件自动添加分区,请使用此选项。

通常选择此选项来同步外部表与其他元存储(例如 AWS Glue 或 Apache Hive)。

以下是用于手动添加分区的 CREATE EXTERNAL TABLE 语法:

CREATE EXTERNAL TABLE
  <table_name>
     ( <part_col_name> <col_type> AS <part_expr> )
     [ , ... ]
  [ PARTITION BY ( <part_col_name> [, <part_col_name> ... ] ) ]
  PARTITION_TYPE = USER_SPECIFIED
  ..

包括所需的 PARTITION_TYPE = USER_SPECIFIED 参数。

分区列的定义是解析内部(隐藏) METADATA$EXTERNAL_TABLE_PARTITION 列中的列元数据的表达式。

对象所有者通过运行 ALTER EXTERNAL TABLE ... ADD PARTITION 命令为表元数据手动添加分区:

ALTER EXTERNAL TABLE <name> ADD PARTITION ( <part_col_name> = '<string>' [ , <part_col_name> = '<string>' ] ) LOCATION '<path>'

不支持使用用户定义的分区自动刷新外部表。尝试手动刷新这种类型的外部表会产生用户错误。

Delta Lake 支持

备注

此功能仍受支持,但将在未来的版本中被弃用。

请考虑改用 Apache Iceberg™ 表。Iceberg 表使用 外部卷 连接云存储中的 Delta 表文件。

有关更多信息,请参阅 Iceberg 表 和 CREATE ICEBERG TABLE(对象存储中的 Delta 文件)。您还可以参阅 将 Delta 外部表迁移到 Apache Iceberg™.

Delta Lake (https://delta.io/) 是数据湖中的一种表格式,支持 ACID(原子性、一致性、隔离性、持久性)事务以及其他功能。Delta Lake 中的所有数据都以 Apache Parquet 格式存储。您可以创建引用使用 Delta Lake 增强的云存储位置的外部表。

若要创建引用 Delta Lake 的外部表,请设置 CREATE EXTERNAL TABLE 语句中的 TABLE_FORMAT = DELTA 参数。

设置此参数后,外部表将扫描 [ WITH ] LOCATION 位置中的 Delta Lake 事务日志文件。Delta 日志文件的名称为 _delta_log/00000000000000000000.json 或 _delta_log/00000000000000000010.checkpoint.parquet 等。刷新外部表的元数据时,Snowflake 将解析 Delta Lake 事务日志并确定哪些 Parquet 文件是当前文件。在后台,刷新会执行文件添加和移除操作,以保持外部表元数据同步。

有关更多信息(包括示例),请参阅 CREATE EXTERNAL TABLE。

备注

  • 引用 Delta Lake 文件的外部表不支持删除向量。

  • 此功能不支持自动刷新,因为云存储中数据定义语言 (DDL) 操作触发的事件通知顺序不受保证。要注册任何已添加或已移除的文件,请定期运行 ALTER EXTERNAL TABLE ... REFRESH 语句。

将 Delta 外部表迁移到 Apache Iceberg™

要将一个或多个 引用 Delta Lake 的外部表 迁移至 Apache Iceberg™ 表,请完成以下步骤:

  1. 使用 SHOW EXTERNAL TABLES 命令检索外部表的 location (外部暂存区和文件夹路径)。

    例如,以下命令返回外部表和名称筛选器的信息,例如 my_delta_ext_table:

    SHOW EXTERNAL TABLES LIKE 'my_delta_ext_table';
    
  2. 创建外部卷;将您在上一步中检索到的位置指定为 STORAGE_BASE_URL。

    要在同一存储位置下为多个 Delta 表创建单个外部卷,请将外部卷的活动位置 (STORAGE_BASE_URL) 用作公共根目录。

    例如,考虑从同一存储位置分支的三个 Delta 表,其位置如下:

    • s3://my-bucket/delta-ext-table-1/

    • s3://my-bucket/delta-ext-table-2/

    • s3://my-bucket/delta-ext-table-3/

    如以下示例所示,在创建外部卷时,将桶指定为 STORAGE_BASE_URL。稍后,当创建 Iceberg 表时,可以指定表文件的相对路径(例如 delta-ext-table-1/)作为 BASE_LOCATION:

    CREATE OR REPLACE EXTERNAL VOLUME delta_migration_ext_vol
    STORAGE_LOCATIONS = (
      (
        NAME = storage_location_1
        STORAGE_PROVIDER = 'S3'
        STORAGE_BASE_URL = 's3://my-bucket/'
        STORAGE_AWS_ROLE_ARN = 'arn:aws:iam::123456789123:role/my-storage-role' )
    );
    
  3. 为 Delta 表创建目录集成。

  4. 使用 CREATE ICEBERG TABLE(对象存储中的 Delta 文件) 命令创建 Iceberg 表。外部卷的 BASE_LOCATION 必须指向现有的外部表位置。

    以下示例基于位于 s3://my-bucket/delta-ext-table-1/ 的外部表文件创建 Iceberg 表,并引用先前创建的外部卷。为确定表的完整存储位置,Snowflake 会将 BASE_LOCATION 附加至外部卷的 STORAGE_BASE_URL:

    CREATE ICEBERG TABLE my_delta_table_1
      BASE_LOCATION = 'delta-ext-table-1'
      EXTERNAL_VOLUME = 'delta_migration_ext_vol'
      CATALOG = 'delta_catalog_integration';
    
  5. 删除外部表:

    DROP EXTERNAL TABLE my_delta_ext_table_1;
    

添加或删除列

要更改现有外部表以添加或移除列,请使用以下 ALTER TABLE 语法:

  • 添加列: ALTER TABLE ...ADD COLUMN。

  • 删除列: ALTER TABLE ...DROP COLUMN。

备注

无法删除默认的 VALUE 列及 METADATA$FILENAME 和 METADATA$FILE_ROW_NUMBER 伪列。

有关更多信息,请参阅 ALTER TABLE 中的示例。

保护外部表

可以使用掩码策略和行访问策略来保护外部表。有关详细信息,请参阅以下主题:

外部表上的物化视图

在许多情况下,外部表的 物化视图 可以提供比基础外部表的等效查询更快的性能。当频繁执行查询或查询复杂度较高时,物化视图可显著提升查询速度。

刷新任何已查询外部表中的文件级别元数据,以便物化视图反映引用的云存储位置中的当前文件集。

您可以使用云存储服务的事件通知服务 自动 刷新外部表的元数据,也可以使用 ALTER EXTERNAL TABLE ...REFRESH 语句手动刷新。

自动刷新外部表元数据

您可以使用云存储服务的事件通知服务自动刷新外部表的元数据。

刷新操作会将元数据与外部暂存区和路径中的一组近期关联文件同步,即:

  • 路径中的新文件将添加到表元数据中。

  • 对路径中文件的更改将在表元数据中更新。

  • 路径中不再存在的文件将从表元数据中移除。

有关更多信息,请参阅 自动刷新外部表。

外部表计费

Snowflake 会在您的费用中包含一项管理开销,用于处理外部表元数据自动刷新的事件通知。此开销随着为外部表指定的外部暂存区和路径在云存储中添加的文件数量而增加。此开销会在账单中显示为 Snowpipe 费用,因为自动外部表刷新的事件通知会使用 Snowpipe。您可以通过查询 PIPE_USAGE_HISTORY 函数或检查 Account Usage PIPE_USAGE_HISTORY 视图 来估算此费用。

此外,Snowflake 会对手动刷新外部表元数据(使用 ALTER EXTERNAL TABLE ... REFRESH)收取少量维护开销费用。与 Snowflake 中的所有类似活动一样,此开销根据标准 云服务计费模型 收取。手动刷新标准外部表只是云服务操作;而使用 Delta Lake 增强的外部表的手动刷新则依赖于用户管理的计算资源(即虚拟仓库)。

如果用户担任 ACCOUNTADMIN 角色或担任具有全局 MONITOR USAGE 权限的角色,则可以查询 AUTO_REFRESH_REGISTRATION_HISTORY 表功能,检索在指定对象的元数据中注册的数据文件的历史记录以及为这些操作计费的 credit。

设置和加载工作流概述

备注

外部表不支持存储版本控制(S3 版本控制、Google Cloud Storage 中的对象版本控制或 Azure Storage 的版本控制)。

Amazon S3 工作流

以下步骤简要概述了引用 Amazon S3 暂存区的外部表的设置和加载工作流。有关完整说明,请参阅 自动刷新 Amazon S3 的外部表:

  1. (使用 CREATE STAGE)创建一个已命名的暂存区对象,该对象引用暂存数据文件的外部位置(即 S3 桶)。

  2. (使用 CREATE EXTERNAL TABLE)创建引用已命名暂存区的外部表。

  3. 手动刷新外部表元数据(使用 ALTER EXTERNAL TABLE ...REFRESH),以将元数据与暂存区路径中的当前文件列表同步。此步骤还会验证外部表定义中的设置。

  4. 为 S3 桶配置事件通知。Snowflake 依赖于事件通知来持续刷新外部表元数据,以保持与暂存文件的一致性。

  5. 再次手动刷新外部表元数据(使用 ALTER EXTERNAL TABLE ...REFRESH),以将元数据与执行第 3 步之后发生的任何变更同步。此后,S3 事件通知会自动触发元数据刷新。

  6. 为任何其他角色配置 Snowflake 访问控制权限,以授予他们对外部表的查询访问权限。

Google Cloud Storage 工作流

以下步骤简要概述了引用 Google Cloud Storage (GCS) 暂存区的外部表的设置和加载工作流。

  1. 为 GCS 事件配置 Google Pub/Sub 订阅。

  2. 在 Snowflake 中创建通知集成通知集成是一个 Snowflake 对象,它在 Snowflake 和诸如 Pub/Sub 这样的第三方云消息队列服务之间提供一个接口。

  3. (使用 CREATE STAGE)创建一个已命名的暂存区对象,该对象引用暂存数据文件的外部位置(即 GCS 桶)。

  4. (使用 CREATE EXTERNAL TABLE)创建一个引用已命名暂存区和集成的外部表。

  5. 再次手动刷新外部表元数据(使用 ALTER EXTERNAL TABLE ... REFRESH),以将元数据与执行第 4 步之后发生的任何变更同步。此后,Pub/Sub 通知会自动触发元数据刷新。

  6. 为任何其他角色配置 Snowflake 访问控制权限,以授予他们对外部表的查询访问权限。

Microsoft Azure 工作流

以下步骤简要概述了引用 Azure 暂存区的外部表的设置和加载工作流。有关完整说明,请参阅 自动刷新 Azure Blob 存储的外部表:

  1. 为 Azure 存储事件配置事件网格订阅。

  2. 在 Snowflake 中创建通知集成通知集成是一个 Snowflake 对象,它在 Snowflake 和诸如 Microsoft 事件网格这样的第三方云消息队列服务之间提供一个接口。

  3. 创建一个已命名的暂存区对象(使用 CREATE STAGE),引用暂存数据文件的外部位置(即 Azure 容器)。

  4. (使用 CREATE EXTERNAL TABLE)创建一个引用已命名暂存区和集成的外部表。

  5. 再次手动刷新外部表元数据(使用 ALTER EXTERNAL TABLE ... REFRESH),以将元数据与执行第 4 步之后发生的任何变更同步。此后,事件网格通知会自动触发元数据刷新。

  6. 为任何其他角色配置 Snowflake 访问控制权限,以授予他们对外部表的查询访问权限。

查询外部表

像 查询 标准表一样查询外部表。

对于包含无效 UTF-8 数据的记录,Snowflake 省略其查询结果。遇到无效数据后,Snowflake 会继续扫描文件,而不会返回错误消息。

为避免因无效 UTF-8 数据而导致查询结果中缺少记录,请为您的 文件格式 指定 REPLACE_INVALID_CHARACTERS = TRUE。这样做会在您查询表时,将无效的 UTF-8 字符替换为 Unicode 替换字符 (�)。

对于 Parquet 文件,还可以为文件格式设置 BINARY_AS_TEXT = FALSE,以便 Snowflake 将具有未定义的逻辑数据类型的列解释为二进制数据而不是 UTF-8 文本。

筛选 Parquet 文件中的记录

要在 Parquet 文件中使用行组统计信息来裁剪数据,您可以在 WHERE 子句中包含分区列、常规列,或两者都包含。以下限制适用:

此外,采用 "value:<path>::<data type>" 形式(或等效 GET/ GET_PATH、: 函数)的查询会利用矢量化扫描器。采用 "value" 或简单的 "value:<path>" 形式的查询则会使用非矢量化扫描器处理。使用 CONVERT_TIMEZONE 函数将所有时区数据转换为标准时区,用于使用向量化扫描器的查询。

当文件按查询筛选器中包含的键排序时,且文件中有多个行组,您可能获得更好的修剪结果。

下表显示了类似的查询结构,用于说明本部分中的行为,其中 et 是外部表,c1、c2 和 c3 则为虚拟列:

已优化

未优化

SELECT c1, c2, c3 FROM et;

SELECT value:c1, c2, c3 FROM et;

SELECT c1, c2, c3  FROM et WHERE c1 = 'foo';

SELECT c1, c2, c3 FROM et WHERE value:c1::string = 'foo';

SELECT c1, c2, c3 FROM et WHERE value:c1 = 'foo';

持久化查询结果

与表类似,外部表的查询结果将 持续 24 小时。在这 24 小时内,以下操作会使外部表的查询结果缓存失效并将其清除:

  • 任何修改外部表定义的 DDL 操作。其中包括显式修改外部表定义(使用 ALTER EXTERNAL TABLE)或重新创建外部表(使用 CREATE OR REPLACE EXTERNAL TABLE)。

  • 云存储中使用外部表元数据中注册的文件集的更改。使用存储位置的事件通知服务执行自动刷新操作,或(使用 ALTER EXTERNAL TABLE ...REFRESH)手动刷新操作,使结果的缓存失效。

备注

在以下情况下,对云存储中引用文件的更改 不会 使查询结果缓存失效,从而不会由此导致查询结果过期:

  • 自动刷新操作已关闭(即 AUTO_REFRESH = FALSE)或配置不正确。

  • 未手动刷新外部表元数据。

示例:从外部表元数据中移除旧的暂存文件

以下步骤提供了一个示例,说明如何使用 ALTER EXTERNAL TABLE ... REMOVE FILES 语句从外部表的元数据中移除较旧的暂存文件。存储过程将根据文件在暂存区中的新近修改日期从元数据中移除文件。

  1. 使用 CREATE PROCEDURE 语句创建存储过程:

    CREATE or replace PROCEDURE remove_old_files(external_table_name varchar, num_days float)
      RETURNS varchar
      LANGUAGE javascript
      EXECUTE AS CALLER
      AS
      $$
      // 1. Get the relative path of the external table
      // 2. Find all files registered before the specified time period
      // 3. Remove the files
    
    
      var resultSet1 = snowflake.execute({ sqlText:
        `call exttable_bucket_relative_path('` + EXTERNAL_TABLE_NAME + `');`
      });
      resultSet1.next();
      var relPath = resultSet1.getColumnValue(1);
    
    
      var resultSet2 = snowflake.execute({ sqlText:
        `select file_name
         from table(information_schema.EXTERNAL_TABLE_FILES (
             TABLE_NAME => '` + EXTERNAL_TABLE_NAME +`'))
         where last_modified < dateadd(day, -` + NUM_DAYS + `, current_timestamp());`
      });
    
      var fileNames = [];
      while (resultSet2.next())
      {
        fileNames.push(resultSet2.getColumnValue(1).substring(relPath.length));
      }
    
      if (fileNames.length == 0)
      {
        return 'nothing to do';
      }
    
    
      var alterCommand = `ALTER EXTERNAL TABLE ` + EXTERNAL_TABLE_NAME + ` REMOVE FILES ('` + fileNames.join(`', '`) + `');`;
    
      var resultSet3 = snowflake.execute({ sqlText: alterCommand });
    
      var results = [];
      while (resultSet3.next())
      {
        results.push(resultSet3.getColumnValue(1) + ' -> ' + resultSet3.getColumnValue(2));
      }
    
      return results.length + ' files: \n' + results.join('\n');
    
      $$;
    
      CREATE or replace PROCEDURE exttable_bucket_relative_path(external_table_name varchar)
      RETURNS varchar
      LANGUAGE javascript
      EXECUTE AS CALLER
      AS
      $$
      var resultSet = snowflake.execute({ sqlText:
        `show external tables like '` + EXTERNAL_TABLE_NAME + `';`
      });
    
      resultSet.next();
      var location = resultSet.getColumnValue(10);
    
      var relPath = location.split('/').slice(3).join('/');
      return relPath.endsWith("/") ? relPath : relPath + "/";
    
      $$;
    
  2. 调用存储过程:

    -- Remove all files from the exttable external table metadata:
    call remove_old_files('exttable', 0);
    
    -- Remove files staged longer than 90 days ago from the exttable external table metadata:
    call remove_old_files('exttable', 90);
    

    或者,您可以使用 CREATE TASK 创建一个定期调用存储过程的任务,以从外部表元数据中移除较旧的文件。

Apache Hive 元存储集成

Snowflake 支持使用外部表将 Apache Hive (https://hive.apache.org/) 元存储与 Snowflake 集成。Hive 连接器检测元存储区事件并将这些事件传输到 Snowflake,以保持外部表与 Hive 元存储的同步。借助该功能,用户可以在通过 Snowflake 查询数据的同时,在 Hive 中管理其数据。

有关说明,请参阅 将 Apache Hive 元存储与 Snowflake 集成。

外部表 DDL

为了支持创建和管理外部表,Snowflake 提供了以下一组特殊 DDL 命令:

所需访问权限

创建和管理外部表需要至少具有以下角色权限的角色:

对象

权限

数据库

USAGE

架构

USAGE、CREATE STAGE(如果创建新暂存区)、CREATE EXTERNAL TABLE

暂存区(如果使用现有暂存区)

USAGE

Information Schema

Snowflake Information Schema 包括视图和表函数,您可以查询这些视图和表函数来检索有关外部表及其暂存数据文件的信息。

视图

EXTERNAL_TABLES 视图

显示指定(或当前)数据库中外部表的信息。

表函数

AUTO_REFRESH_REGISTRATION_HISTORY

检索在指定对象元数据中注册的数据文件的历史记录,以及为这些操作计费的 credit。

EXTERNAL_TABLE_FILES

检索指定外部表的元数据中包含的暂存数据文件相关信息。

EXTERNAL_TABLE_FILE_REGISTRATION_HISTORY

检索有关外部表的元数据历史记录的信息,包括刷新元数据时发现的任何错误。