创建表

在 Dataform 中,表是构成工作流的对象类型之一。您可以创建表,以引用为工作流声明的数据源中的数据,或工作流中其他表中的数据。Dataform 会实时将表定义编译为 SQL。触发执行后,Dataform 会运行 SQL 代码,并在 BigQuery 中创建您定义的表。

您可以在 type: "table" SQLX 文件中创建以下表类型:

  • table:常规表。
  • incremental:增量表。
  • view:表视图。如需了解详情,请参阅视图简介。

您还可以定义表分区和聚簇。

如需记录表在工作流中的用途或与其他表的关系,您可以向表或其所选列添加文档。

如需根据特定条件测试表中的数据,您可以创建名为“断言”的数据质量测试查询。Dataform 每次更新工作流时都会运行断言,并在任何断言失败时提醒您。

如需替换所选表的默认架构、数据库和名称,您可以替换表设置。

如需停用表创建功能,或在创建表之前或之后运行 SQL 语句,您可以配置其他操作。

如需在运行表后在 BigQuery 中整理表,您可以添加 BigQuery 标签。如需了解详情,请参阅标签简介。

如需在表列级别限制数据访问权限,您可以添加 BigQuery 政策标记。如需了解详情,请参阅列级访问权限控制简介。

如需保留外部系统在表运行期间应用的 BigQuery 数据政策和数据治理标记(预览版),您可以保留访问权限控制元数据。

除了在 type: "table" SQLX 文件中定义表之外,您还可以通过在 type: "operations" SQLX 文件中定义自定义 SQL 查询来创建空表。您可能希望创建一个空表,以便其他服务可以使用数据填充该表。

准备工作

  1. 在 Google Cloud 控制台中,前往 Dataform 页面。

    前往 Dataform

  2. 在代码库中创建并初始化开发工作区。

  3. 可选:声明数据源。

所需的角色

如需获得完成本文档中的任务所需的权限,请让您的管理员为您授予以下 IAM 角色:

  • 针对工作区的 Dataform Editor (roles/dataform.editor)
  • 将元数据同步到 Knowledge Catalog:针对项目或 @bigquery 条目组的 Dataplex Catalog Editor (roles/dataplex.catalogEditor) 角色

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

您也可以通过自定义角色或其他预定义角色来获取所需的权限。

如需保留访问权限控制元数据,执行 Dataform 工作流的服务账号必须具有特定的 IAM 权限,具体取决于任务:

  • 提取元数据:如需读取 INFORMATION_SCHEMA.COLUMNS 视图,服务账号必须对数据集或表架构具有读取权限。以下 IAM 角色包含这些权限:

  • 应用数据治理标记:如需在表和视图上附加和管理数据治理标记,服务账号需要 bigquery.tables.createTagBinding 和 bigquery.tables.deleteTagBinding 权限。以下预定义的 IAM 角色包含这些权限:

  • 应用数据政策:如需创建和更新数据政策(包括无标记政策),服务账号需要 bigquery.dataPolicies.create 和 bigquery.dataPolicies.update 权限。以下预定义的 IAM 角色包含这些权限:

创建表

本部分介绍了如何在 Dataform 中使用 Dataform 核心创建表。

关于表定义

如需定义表,您需要在 type: "table" SQLX 文件中定义表类型并编写 SELECT 语句。然后,Dataform 会将您的 Dataform 核心代码编译为 SQL,运行 SQL 代码,并在 BigQuery 中创建您定义的表。

在 Dataform 核心 SELECT 语句中,您可以定义表结构并引用工作流的其他对象。

除了在 type: "table" SQLX 文件中定义表之外,您还可以通过在 type: "operations" SQLX 文件中定义自定义 SQL 查询来创建空表。如需了解详情,请参阅创建空表。

使用 ref 引用依赖项

如需在 SELECT 语句中引用工作流操作并自动将其添加为依赖项,请使用 ref 函数。Dataform 会在依赖项所依赖的表之前运行依赖项,以验证流水线顺序是否正确。

ref 函数是一个内置的 Dataform 核心函数,对 Dataform 中的依赖项管理至关重要。借助 ref 函数,您可以引用 Dataform 工作流中定义的以下对象并自动依赖这些对象,而无需对架构和表名称进行硬编码:

Dataform 使用 ref 函数构建要创建或更新的所有表的依赖项树。

编译后,Dataform 会向 SQL 语句添加样板语句,例如 CREATE、REPLACE、INSERT 或 MERGE。

以下代码示例展示了使用 ref 函数的表定义:

config { type: "table" }

SELECT
  order_date AS date,
  order_id AS order_id,
  order_status AS order_status,
  SUM(item_count) AS item_count,
  SUM(amount) AS revenue

FROM ${ref("store_clean")}

GROUP BY 1, 2

在 ref 函数中,您需要提供要依赖的表或数据源声明的名称。这通常是定义该表或数据源声明的 SQLX 文件的文件名。

如果表名称被替换,请在 ref 函数中使用替换后的名称。例如,将具有 config { name: "overridden_name" } 的表引用为 ref("overridden_name")。如需了解详情,请参阅替换表设置和引用具有替换表名称的表。

如果不同架构中有多个同名表,您可以通过向 ref 函数提供两个实参(架构名称和表名称)来引用特定表。

以下代码示例展示了带有两个实参的 ref 函数,用于指定特定架构中的表:

config { type: "table" }
SELECT * FROM ${ref("schema", "store_clean")}

您还可以手动将表依赖项添加到 config 块中,以用于 SELECT 语句中的 ref 函数未引用的表、断言、数据源声明或自定义 SQL 操作。Dataform 会在依赖表之前运行这些依赖项。

以下代码示例展示了 config 块中的表依赖项:

config { dependencies: [ "unreferenced_table" ] }
SELECT * FROM ...

如需详细了解工作流中的依赖项管理,请参阅设置依赖项。

使用 resolve 引用其他表

借助 resolve 函数,您可以在 SELECT 语句中引用表或数据源声明(如 ref 函数),但不会将引用添加为依赖项。这意味着,使用 resolve 函数引用的对象不会影响使用 resolve 函数的表的执行。

如需详细了解内置 Dataform 核心函数,请参阅 Dataform 核心参考文档。

创建用于表定义的 SQLX 文件

将表定义 SQLX 文件存储在 definitions/ 目录中。如需在 definitions/ 目录中创建新的 SQLX 文件,请按照以下步骤操作:

  1. 在 Google Cloud 控制台中,前往 Dataform 页面。

    前往 Dataform

  2. 如需打开代码库,请点击代码库名称。

  3. 如需打开开发工作区,请点击该工作区名称。

  4. 在文件窗格中,点击 definitions/ 旁边的更多。

  5. 点击创建文件。

  6. 在添加文件路径字段中,输入文件名,后跟 definitions/ 后面的 .sqlx。例如 definitions/my-table.sqlx。

    文件名只能包含数字、字母、连字符和下划线。

  7. 点击创建文件。

定义表类型

如需创建新的表类型定义,请按以下步骤操作:

  1. 在开发工作区的文件窗格中,展开 definitions/ 目录。
  2. 选择要修改的表定义 SQLX 文件。
  3. 在文件中输入以下代码段:

    config { type: "TABLE_TYPE" }
    

    将 TABLE_TYPE 替换为以下某个表类型:

    • table
    • incremental
    • view
  4. 可选:如需定义具体化视图,请在 type: "view" 下以以下格式输入 materialized 属性:

    config {
      type: "view",
      materialized: true
    }
    

    如需了解详情,请参阅 ITableConfig。

  5. 可选:点击格式化。

定义表结构和依赖项

如需编写表定义 SELECT 语句并定义表结构和依赖项,请按以下步骤操作:

  1. 在开发工作区的文件窗格中,展开 definitions/ 目录。
  2. 选择要修改的表定义 SQLX 文件。
  3. 在 config 代码块下方,编写一个 SELECT 语句。
  4. 可选:点击格式化。

以下代码示例展示了包含 SELECT 语句和 ref 函数的表定义:

config { type: "table" }
SELECT
  customers.id AS id,
  customers.first_name AS first_name,
  customers.last_name AS last_name,
  customers.email AS email,
  customers.country AS country,
  COUNT(orders.id) AS order_count,
  SUM(orders.amount) AS total_spent
FROM
  dataform-samples.dataform_sample.crm_customers AS customers
  LEFT JOIN ${ref('order_stats')} orders
    ON customers.id = orders.customer_id

WHERE
  customers.id IS NOT NULL
  AND customers.first_name <> 'Internal account'
  AND country IN ('UK', 'US', 'FR', 'ES', 'NG', 'JP')

GROUP BY 1, 2, 3, 4, 5

缓解列架构限制

在 type: "table" 或 type: "incremental" SQLX 文件中定义表时,Dataform 会将 SELECT 查询编译为 BigQuery 中的 CREATE OR REPLACE TABLE ... AS SELECT 语句,而无需显式列架构定义列表。因此,BigQuery 默认会以 NULLABLE 模式创建列。

Dataform 表定义不支持在 config 块或 columns 属性中将列的模式设置为 REQUIRED (NOT NULL)。在 BigQuery 中,如果不使用显式架构定义重新创建表,您也无法将现有列的模式从 NULLABLE 更改为 REQUIRED。如需详细了解如何在 BigQuery 中修改列模式,请参阅更改列的模式。

如需验证非 null 值或创建包含 REQUIRED 列的表,请使用以下方法之一:

使用断言验证非 null 值
在表的 config 代码块中添加内置 nonNull 或 rowConditions 断言,例如 assertions: { nonNull: ["column_name"] } 或 assertions: { rowConditions: ["column_name IS NOT NULL"] }。您还可以在单独的 type: "assertion" SQLX 文件中定义手动断言。断言会在工作流执行期间检查行是否包含 NULL 值,而不会将底层 BigQuery 列模式从 NULLABLE 更改为 REQUIRED。如需详细了解断言,请参阅使用断言测试数据。
使用自定义 SQL 操作或在 BigQuery 中定义 NOT NULL 列
如需创建 BigQuery 架构将列设置为 REQUIRED 模式的表,请在自定义 SQL 操作文件中使用 config { type: "operations", hasOutput: true } 编写具有显式 NOT NULL 列限制条件的 CREATE OR REPLACE TABLE DDL 语句,或直接在 BigQuery 中创建和管理表架构。如需详细了解如何在 DDL 语句中定义 REQUIRED 列,请参阅创建包含 REQUIRED 列的表。

添加手动表依赖项

如需添加未在 SELECT 语句中引用但需要在当前表之前运行的表依赖项,请按照以下步骤操作:

  1. 在开发工作区的文件窗格中,展开 definitions/ 目录。
  2. 选择要修改的表定义 SQLX 文件。
  3. 在表的 config 代码块中,输入以下代码段:

    dependencies: [ "DEPENDENCY_TABLE", ]
    

    将 DEPENDENCY_TABLE 替换为要添加为依赖项的表的文件名。您可以输入多个文件名。

  4. 可选:点击格式化。

以下代码示例展示了作为手动表依赖项添加到表定义文件的 config 代码块中的两个表:

config { dependencies: [ "some_table", "some_other_table" ] }

覆盖表设置

您可以替换所选表的默认架构、数据库和名称。

默认情况下,表遵循您在 workflow_settings.yaml 中设置的架构和数据库配置。表的名称与表定义 SQLX 文件的名称相同。

如需替换所选表的架构和名称,请按以下步骤操作:

  1. 前往您的开发工作区。

  2. 在文件窗格中,展开 definitions/。

  3. 打开 SQLX 表定义文件。

  4. 在 config 块中,输入以下代码段:

     {
       schema: "OVERRIDDEN_SCHEMA",
       database: "OVERRIDDEN_DATABASE",
       name: "OVERRIDDEN_NAME"
     }
    

    替换以下内容:

    • OVERRIDDEN_SCHEMA:要在其中创建表的 BigQuery 数据集。

    • OVERRIDDEN_DATABASE:要在其中创建表的 BigQuery 项目的 ID。

    • OVERRIDDEN_NAME:表的名称,与 SQLX 表定义文件名不同。

  5. 可选:点击格式化。

如需了解详情,请参阅引用具有替换表名称的表。

创建 Apache Iceberg 托管表

使用 Dataform 创建 Iceberg 表格式的 BigQuery 表。这些表称为 Iceberg 托管表。如需详细了解所需角色和其他设置任务,请参阅 Iceberg 托管表工作流的准备工作步骤。

创建 Iceberg 表定义

如需创建表,请在表定义文件的 bigquery 块中定义 Iceberg 配置。

如需定义表,请按以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 打开表定义 SQLX 文件。
  4. 在 config 块中,添加一个包含 iceberg 块的 bigquery 块,格式如下:

    config {
      type: "table",
      name: "my_table",
      uniqueKey: ["uniquekey"],
      bigquery: {
        iceberg: {
          bucket_name: "BUCKET_NAME"
        }
      }
    }
    

    将 BUCKET_NAME 替换为存储表数据的 Cloud Storage 存储桶的名称。

    其他属性是可选的,如果未指定,则具有默认值。如需了解详情,请参阅 iceberg 属性。

    您可以为每个表提供值,也可以在 workflow_settings.yaml 中设置工作流级默认值。您可以通过在 iceberg 代码块中明确设置属性来替换工作流级默认值。

创建表分区和聚簇

本部分介绍如何使用 Dataform 核心创建表分区和集群。BigQuery 支持分区表和表聚簇。如需了解详情,请参阅分区表简介和创建和使用聚簇表。

创建表分区

如需创建表分区,请按以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 打开表定义 SQLX 文件。
  4. 在 config 块中,在表类型声明下方添加 bigquery 块,格式如下:

    config {
      type: "table",
      bigquery: {
      }
    }
    
  5. 在 bigquery 块中,输入以下代码段:

        partitionBy: "PARTITION_EXPRESSION"
    

    将 PARTITION_EXPRESSION 替换为用于对表进行分区的表达式。

  6. 可选:点击格式化。

以下代码示例展示了如何在表定义 SQLX 文件中按小时对表进行分区:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATETIME_TRUNC(<timestamp_column>, HOUR)"
  }
}

以下代码示例展示了如何在表定义 SQLX 文件中按整数值对表进行分区:

config {
  type: "table",
  bigquery: {
    partitionBy: "RANGE_BUCKET(<integer_column>, GENERATE_ARRAY(0, 1000000, 1000))"
  }
}

设置分区过滤条件

如需设置分区过滤条件,请按以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 打开分区表定义 SQLX 文件。
  4. 在 bigquery 块中,输入以下代码段:

    requirePartitionFilter : true
    
  5. 可选:点击格式化。

以下代码示例展示了在分区表 SQLX 文件的 bigquery 块中设置的分区过滤条件:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATE(ts)",
    requirePartitionFilter : true
  }
}
SELECT CURRENT_TIMESTAMP() AS ts

如需详细了解 BigQuery 中的分区过滤条件,请参阅在分区表上设置“需要分区过滤条件”属性。

为分区设置保留期限

如需控制分区表中所有分区的保留,请按照以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 打开分区表定义 SQLX 文件。
  4. 在 bigquery 块中,输入以下代码段:

    partitionExpirationDays: NUMBER_OF_DAYS
    

    将 NUMBER_OF_DAYS 替换为您要保留分区的天数。

  5. 可选:点击格式化。

以下代码示例展示了在分区表 SQLX 文件的 bigquery 块中将分区保留期限设置为 14 天:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATE(ts)",
    partitionExpirationDays: 14,
  }
}
SELECT CURRENT_TIMESTAMP() AS ts

创建表集群

如需创建表集群,请按照以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 打开表定义 SQLX 文件。
  4. 在 bigquery 块中,输入以下代码段:

        clusterBy: ["CLUSTER_COLUMN"]
    

    将 CLUSTER_COLUMN 替换为要用于对表进行聚簇的列的名称。如需了解详情,请参阅 clustering_column_list。

  5. 可选:点击格式化。

以下代码示例展示了按 name 和 revenue 列聚簇的分区表:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATE(ts)",
    clusterBy: ["name", "revenue"]
  }
}
SELECT CURRENT_TIMESTAMP() as ts, name, revenue

配置增量表

本部分介绍如何使用 Dataform 核心配置增量表。

增量表简介

Dataform 会根据表类型以不同的方式更新表。在每次执行表或视图期间,Dataform 都会从头开始重新构建整个表或视图。

定义增量表时,Dataform 仅在首次从头开始构建增量表。在后续执行期间,Dataform 仅根据您配置的条件将新行插入或合并到增量表中。

Dataform 仅将新行插入到增量表中已存在的列中。如果您对增量表定义查询进行更改(例如,添加新列),则必须决定是否应从头开始重新构建表。如需重新构建表,请在下次触发运行表时选择运行并完全刷新选项。如需了解其他选项,请参阅在不进行完全刷新的情况下更改增量表架构。

以下是增量表的一些常见使用场景:

性能优化
对于某些类型的数据(例如网站日志或分析数据),您可能只想处理新记录,而不是重新处理整个表。
减少延迟
您可以使用增量表快速但频繁地运行工作流,从而减少输出表的下游延迟时间。
每日快照
您可以配置增量表,以创建表数据的每日快照,例如,对存储在生产数据库中的用户设置进行纵向分析。

处理增量表中的部分行

如需确定 Dataform 在每次执行期间要处理的行子集,请向增量表 SQLX 定义文件添加条件 WHERE 子句。在 WHERE 子句中,您可以指定增量条件和非增量条件。Dataform 会在表执行期间应用增量条件(不进行完全刷新),并在执行期间应用非增量条件(进行完全刷新)。

如需配置增量表,请按以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 打开增量表定义 SQLX 文件。
  4. 按以下格式输入 WHERE 子句:

    config { type: "incremental" }
    
    SELECT_STATEMENT
    
    ${when(incremental(), `WHERE INCREMENTAL_CONDITION`, `WHERE NON_INCREMENTAL_CONDITION`) }
    

    替换以下内容:

    • SELECT_STATEMENT:定义表的 SELECT 语句。
    • INCREMENTAL_CONDITION:您在 WHERE 子句中指定的条件,用于选择在不进行完全刷新的情况下,Dataform 在表执行期间要处理的行。
    • NON_INCREMENTAL_CONDITION:您在 WHERE 子句中指定的条件,用于选择在表执行期间通过完全刷新让 Dataform 处理的行。
  5. 可选:点击格式化。

以下代码示例展示了一个增量表,该表以增量方式处理 productiondb.logs 表的行:

config { type: "incremental" }

SELECT timestamp, message FROM ${ref("productiondb", "logs")}

${when(incremental(),
   `WHERE date > (SELECT MAX(date) FROM ${self()}) AND country = "UK"`,
   `WHERE country = "UK"`)}

以下代码示例展示了一个增量表,该表会创建 productiondb.customers 表的快照:

config { type: "incremental" }

SELECT CURRENT_DATE() AS snapshot_date, customer_id, name, account_settings FROM ${ref("productiondb", "customers")}

${when(incremental(), `WHERE snapshot_date > (SELECT MAX(snapshot_date) FROM ${self()})`) }

合并增量表中的行

如需验证增量表是否仅包含与所选列组合对应的一行,请将所选列设置为 uniqueKey,以合并具有相同 uniqueKey 值的行。更新表时,Dataform 会合并具有相同 uniqueKey 值的行,而不是附加这些行。

如需在增量表中配置合并,请按照以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 选择增量表定义 SQLX 文件
  4. 在 config 代码块中,按以下格式将所选列设置为 uniqueKey:

    uniqueKey: ["COLUMN_NAME"]
    

    将 COLUMN_NAME 替换为所选列的名称。

  5. 可选:点击格式化。

以下代码示例展示了一个增量表,其中 transaction_id 列设置为 uniqueKey,以验证该表始终包含一行:

config {
  type: "incremental",
  uniqueKey: ["transaction_id"]
}

SELECT timestamp, action FROM weblogs.user_actions
${ when(incremental(), `WHERE timestamp > (SELECT MAX(timestamp) FROM ${self()})`) }

过滤增量表中的行

在增量分区表中,为避免 Dataform 扫描整个表来查找匹配的行,请将 updatePartitionFilter 设置为仅考虑一部分记录。

以下代码示例展示了一个增量分区表,该表使用两种不同类型的过滤条件,通过 updatePartitionFilter 和 uniqueKey 属性配置了合并:

  • 使用 updatePartitionFilter 属性进行目标剪枝:限制在 MERGE 操作期间在目标表中扫描的分区,以查找与 uniqueKey 匹配的行。
  • 使用 WHERE 子句进行来源剪枝:限制在增量运行期间从来源表中提取哪些行。这是使用 ${when(incremental(), ...)} 函数实现的。
config {
  type: "incremental",
  uniqueKey: ["transaction_id"],
  bigquery: {
    partitionBy: "DATE(timestamp)",
    updatePartitionFilter:
        "timestamp >= timestamp_sub(current_timestamp(), interval 24 hour)"
  }
}

SELECT timestamp, action FROM weblogs.user_actions
${ when(incremental(), `WHERE timestamp > (SELECT MAX(timestamp) FROM ${self()})`) }

从分区表提取数据时避免全表扫描

创建引用分区表的增量表时,我们建议您构建表查询,以避免在每次增量更新期间对分区表进行全表扫描。

您可以在表查询中使用常量表达式,以限制 BigQuery 为更新增量表而扫描的分区数量。如需将分区表中的值转换为常量表达式,请使用 BigQuery 脚本在 pre_operations 块中将该值声明为变量。然后,在 SELECT 查询的 WHERE 子句中将该变量用作常量表达式。

通过此配置,Dataform 会根据所引用分区表的最新分区更新增量表,而无需扫描整个表。

如需配置引用分区表并避免全表扫描的增量表,请按照以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 选择增量表定义 SQLX 文件
  4. 在 pre_operations 块中,使用 BigQuery 脚本声明变量。
  5. 使用引用已声明变量的 WHERE 子句过滤定义表的 SELECT 语句。
  6. 可选:点击格式化。

以下代码示例展示了一个增量表,其中引用的 raw_events 表按 event_timestamp 分区:

config {
  type: "incremental",
}

pre_operations {
  DECLARE event_timestamp_checkpoint DEFAULT (
    ${when(incremental(),
    `SELECT max(event_timestamp) FROM ${self()}`,
    `SELECT timestamp("2000-01-01")`)}
  )
}

SELECT
  *
FROM
  ${ref("raw_events")}
WHERE event_timestamp > event_timestamp_checkpoint

在前面的代码示例中,event_timestamp_checkpoint 变量在 pre_operations 块中定义。然后,event_timestamp_checkpoint 变量在 WHERE 子句中用作常量表达式。

通过全面刷新从头开始重新构建增量表

您可以使用带有 --full-refresh 选项的命令行界面或在触发工作流执行时使用完全刷新运行选项,强制从头开始重新构建增量表。

当您在开发工作区中或使用 Dataform CLI 选择完全刷新选项时,Dataform 会在执行期间忽略 ${when(incremental(), ... } 参数,并使用 CREATE OR REPLACE 语句重新创建表。

保护增量表免遭全面刷新

为防止增量表从头开始重新构建以及潜在的数据丢失,您可以将增量表设置为 protected。如果您的数据源是临时的,您可能需要防止重新构建增量表。

如需将增量表标记为 protected,请按照以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 选择增量表定义 SQLX 文件。
  4. 在 config 代码块中,输入 protected: true。
  5. 可选:点击格式化。

以下代码示例展示了标记为 protected 的增量表:

config {
  type: "incremental",
  protected: true
}
SELECT ...

在不进行全面刷新的情况下更改增量表架构

您可以使用 onSchemaChange 配置属性在 SELECT 查询中修改增量表架构,而无需完全刷新表。借助此属性,您可以向表中添加新列或从中移除现有列,同时保留历史数据。此方法有助于防止数据丢失,并避免在 BigQuery 中进行手动更新。

如需使用此功能,您必须安装 Dataform 核心 3.0.11 或更高版本。

设置 onSchemaChange 属性后,Dataform 会检查 SELECT 查询,以确定是否添加或移除了列。然后,Dataform 会执行 onSchemaChange 属性的值指定的操作。

如需设置 onSchemaChange 属性,请按以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 选择增量表定义 SQLX 文件。
  4. 在 config 块中,输入以下代码段:

    onSchemaChange: "ON_CHANGE_ACTION"
    

    将 ON_CHANGE_ACTION 替换为以下操作之一:

    • IGNORE(默认):忽略添加的列,并针对缺失的列显示错误。如果未设置 onSchemaChange,则这是架构更改时的默认行为。
    • FAIL:如果 Dataform 检测到任何架构更改,则会停止操作,这有助于保持架构一致。
    • EXTEND:将查询中的新列添加到增量表中,并为之前的记录添加 NULL 值。如果从查询中的原始架构中移除或缺少列,则会显示错误。您可以使用此设置在运行时向增量表添加新列。
    • SYNCHRONIZE:将查询中的新列添加到增量表中,并为之前的记录添加 NULL 值。移除原始架构中存在但当前查询中缺少的列。

  5. 可选:点击格式化。

以下代码示例展示了一个增量表,其中为 onSchemaChange 属性设置了 EXTEND 操作:

config {
    type: "incremental",
    onSchemaChange: "EXTEND",
}
SELECT ...

添加表文档

本部分介绍了如何向 Dataform 核心 SQLX 文件添加表及其列和记录的说明。

您可以向 Dataform 中的所有表类型(表、增量表和视图)添加表、列和记录说明。

您可能需要记录以下内容:

  • 表格的用途。
  • 表中列或记录的内容或角色。
  • 表与工作流的其他操作的关系,例如依赖于当前表的表或视图。
  • 应用于表的断言。
  • 对表应用的操作前或操作后。
  • 表的所有者,即创建表的用户。如果多个团队成员处理一个工作流,则此信息可能很有用。

为 Knowledge Catalog 添加元数据

BigQuery 中的标准元数据(例如数据集、表和视图)会自动在 Knowledge Catalog 中提供。

您还可以直接在 .sqlx 文件的配置块中为表和视图定义自定义元数据。成功完成操作后,Dataform 会自动启动与 Knowledge Catalog 的元数据同步。此丰富过程会使用 SQLX 配置中定义的语义元数据更新 Knowledge Catalog。

使用元数据键为 Knowledge Catalog 指定信息。丰富流程支持以下元数据结构:

  • 概览:条目的文档和摘要文本。需要 Dataform 核心 3.0.37 版或更高版本。
  • 通用切面:语义详细信息,例如表系统和类型信息。需要 Dataform 核心 3.0.52 版或更高版本。

以下示例配置展示了如何向 Knowledge Catalog 的表配置添加概览和通用元数据切面:

config {
  type: "table",
  metadata: {
    overview: "This table provides standardized trip data.",
    extraProperties: {
        generic: {
              system: "BigQuery",
              type: "table"
        }
      }
  }
}

如需查看元数据更新的状态,请参阅 Dataform 工作流的检查工作区执行日志或 BigQuery 流水线的查看过去的运行记录。

如需验证同步的元数据,您可以在 Knowledge Catalog 中搜索相应资产。如需了解详情,请参阅搜索资源。

添加表说明

如需向 SQLX 文件中的表添加说明,请按照以下步骤操作:

  1. 在 Google Cloud 控制台中,前往 Dataform 页面。

    前往 Dataform

  2. 选择一个代码库。

  3. 选择开发工作区。

  4. 在文件窗格中,点击要修改的表定义 SQLX 文件。

  5. 在文件的 config 块中,按以下格式输入表说明:

    description: "Description of the table",
    
  6. 可选:点击格式化。

以下代码示例展示了添加到 SQLX 表定义文件的 config 块中的表说明:

config {
  type: "table",
  description: "Description of the table",
 }

添加列和记录说明

如需向 SQLX 文件添加各个列和记录的说明,请按照以下步骤操作:

  1. 在表定义文件的 config 块中,输入 columns: {}。
  2. 在 columns: {} 中,按以下格式输入列说明:

    column_name: "Description of the column",
    
  3. 在 columns: {} 中,按以下格式输入记录说明:

      record_name: {
          description: "Description of the record",
          columns: {
            record_column_name: "Description of the record column"
          }
    }
    
  4. 可选:点击格式化。

以下代码示例展示了 SQLX 表定义文件的 config 块中的表、列和记录说明:

config {
  type: "table",
  description: "Description of the table.",
  columns: {
    column1_name: "Description of the first column",
    column2_name: "Description of the second column",
    column3_name: "Description of the third column",
    record_name: {
      description: "Description of the record.",
      columns: {
       record_column1_name: "Description of the first record column",
       record_column2_name: "Description of the second record column",
      }
    }
  }
}
SELECT
  "first_column_value" AS column_1_name,
  "second_column_value" AS column_2_name,
  "third_column_value" AS column_3_name,
  STRUCT("first" AS record_column1_name,
    "second" AS record_column2_name) AS record_name

通过 includes 重复使用列文档

您可以使用 JavaScript include 在 SQL 工作流中重复使用 Dataform 中的列说明。如果 SQL 工作流中有多个具有相同名称和说明的列,您可能需要重复使用列文档。

您可以定义一个包含单个列说明的常量,也可以定义一个包含一组列说明的常量,以便重复使用表中所有列的说明。如需详细了解如何在 Dataform 中创建和使用 include,请参阅使用 include 在单个代码库中重复使用代码。

以下代码示例展示了 includes/docs.js JavaScript 文件中定义的多个常量,其中包含各个列的说明:


// filename is includes/docs.js

const user_id = `A unique identifier for a user`;
const age = `The age of a user`;
const creation_date = `The date this user signed up`;
const user_tenure = `The number of years since the user's creation date`;
const badge_count = `The all-time number of badges the user has received`;
const questions_and_answer_count = `The all-time number of questions and answers the user has created`;
const question_count = `The all-time number of questions the user has created`;
const answer_count = `The all-time number of answers the user has created`;
const last_badge_received_at = `The time the user received their most recent badge`;
const last_posted_at = `The time the user last posted a question or answer`;
const last_question_posted_at = `The time the user last posted an answer`;
const last_answer_posted_at = `The time the user last posted a question`;

module.exports = {
   user_id,
   age,
   creation_date,
   user_tenure,
   badge_count,
   questions_and_answer_count,
   question_count,
   answer_count,
   last_badge_received_at,
   last_posted_at,
   last_question_posted_at,
   last_answer_posted_at,
};

以下代码示例展示了在 includes/docs.js 中定义的 user_id 和 age 常量,这些常量在 definitions/my_table.sqlx SQLX 表定义文件中使用,用于为表中的选定列生成文档:

config {
  type: "table",
  description: "Table description.",
  columns: {
    user_id: docs.user_id,
    column2_name: "Description of the second column",
    column3_name: "Description of the third column",
    age: docs.age,
  }
}

SELECT ...

以下代码示例展示了在 includes/docs.js JavaScript 文件中定义的一组列说明的常量:


// filename is includes/docs.js

const columns = {
    user_id = `A unique identifier for a user`,
    age = `The age of a user`,
    creation_date = `The date this user signed up`,
    user_tenure = `The number of years since the user's creation date`,
    badge_count = `The all-time number of badges the user has received`,
    questions_and_answer_count = `The all-time number of questions and answers the user has created`,
    question_count = `The all-time number of questions the user has created`,
    answer_count = `The all-time number of answers the user has created`,
    last_badge_received_at = `The time the user received their most recent badge`,
    last_posted_at = `The time the user last posted a question or answer`,
    last_question_posted_at = `The time the user last posted an answer`,
    last_answer_posted_at = `The time the user last posted a question`,
}


module.exports = {
  columns
};

以下代码示例展示了 columns 常量,该常量在 includes/table_docs.js 中定义,并在 definitions/my_table.sqlx SQLX 表定义文件中使用,用于为表中的所有列生成文档:

config { type: "table",
description: "My table description",
columns: docs.columns
}

SELECT 1 AS one

添加 BigQuery 标签

本部分介绍了如何在 Dataform 中向表添加标签。

BigQuery 支持向资源添加标签。如需详细了解 BigQuery 中的标签,请参阅标签简介。

如需在 Dataform 中向表添加 BigQuery 标签,请将该标签添加到表定义 SQLX 文件的 config 块中的 bigquery 块。

如需向表定义文件添加 BigQuery 标签,请按以下步骤操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 选择 SQLX 表定义文件。
  4. 在 config 块中,按以下格式添加标签:

    bigquery: {
        labels: {
          LABEL1: "VALUE_OF_LABEL1"
        }
      }
    

    替换以下内容:

    • LABEL1:标签的名称
    • VALUE_OF_LABEL1:标签的值
  5. 可选:如需添加名称中包含特殊字符的标签,请在英文引号 ("") 中输入标签名称。

  6. 可选:点击格式化。

以下代码示例展示了添加到分区表定义 SQLX 文件中的 bigquery 代码块的 department:shipping 和 cost-center:logistics 标签:

config {
  type: "table",
  bigquery: {
    partitionBy: "DATE(ts)",
    labels: {
      department: "shipping",
      "cost-center": "logistics"
    }
  }
}

SELECT CURRENT_TIMESTAMP() AS ts

保留访问权限控制元数据

当 Dataform 执行重新创建对象(例如标准表、视图或完全刷新期间的增量表)的工作流操作时,它通常会在 BigQuery 中生成 CREATE OR REPLACE 语句,以更新对象架构和数据。默认情况下,在 BigQuery 中,表替换操作会自动删除现有的列级访问权限控制元数据,例如外部数据治理系统应用的数据政策和数据治理标记(预览版)。

您可以配置 Dataform,使其在工作流运行期间自动保留现有的 BigQuery 数据政策(包括无标记数据政策)和数据治理标记,只需启用 preserveGovernanceControls 选项即可。启用此选项后,Dataform 会在执行期间执行以下序列:

  1. 从 INFORMATION_SCHEMA.COLUMNS 视图中备份现有列元数据。
  2. 创建一个中间空暂存表。
  3. 将安全政策重新应用于中间表。
  4. 使用数据填充表。
  5. 执行原子表交换以替换原始表。

为表启用自动化治理保留

如需为单个表、增量表或视图启用数据政策和数据治理标记的自动保留,请在对象的 SQLX 定义文件的 config 块中将 preserveGovernanceControls 参数设置为 true。

如需为单个表配置治理保留,请执行以下操作:

  1. 前往您的开发工作区。
  2. 在文件窗格中,展开 definitions/。
  3. 选择 SQLX 表定义文件。
  4. 在 config 代码块中,输入 preserveGovernanceControls: true。
  5. 可选:点击格式化。

以下代码示例展示了启用了治理保留的表定义:

config {
  type: "table",
  preserveGovernanceControls: true
}

SELECT
  customer_id,
  email,
  phone_number
FROM
  ${ref("raw_customers")}

为所有表启用自动治理保留

您可以在代码库的 workflow_settings.yaml 文件中将 preserveGovernanceControls 参数设置为 true,从而为代码库中的所有表、增量表和视图启用自动治理保留作为项目级默认行为。

如需设置项目级默认值,请将 preserveGovernanceControls 参数添加到 workflow_settings.yaml 文件中,如以下示例所示:

defaultProject: my-gcp-project-id
defaultDataset: my_dataform_dataset
defaultLocation: US
defaultAssertionDataset: my_dataform_assertions
dataformCoreVersion: 3.0.62
preserveGovernanceControls: true

如果您在 workflow_settings.yaml 文件中全局启用了治理保留,则可以通过在特定表的 config 块中将 preserveGovernanceControls 参数设置为 false,来替换此行为并停用该表的保留。

使用后续操作重新应用行访问权限政策

虽然启用治理保留功能会自动保护列级元数据(例如 BigQuery 数据政策和数据治理标记),但不会自动恢复行访问权限政策。由于对象替换会删除现有的行级安全规则,因此您可以在 SQLX 表定义文件的 post_operations 块中定义 SQL DDL 语句,以便在创建表后重新应用行访问权限政策。

以下代码示例展示了如何在表的 post_operations 块中重新应用行级访问权限政策:

config {
  type: "table",
}

SELECT
  bike_id,
  start_time,
  duration
FROM
  ${ref("raw_trips")}

post_operations {
  CREATE ROW ACCESS POLICY my_policy
  ON ${self()}
  GRANT TO ('serviceAccount:sa-name@example-gcp-project.')
  FILTER USING (bike_id LIKE 'US-%');
}

限制

使用治理保留功能时,请考虑以下限制:

  • 架构列移除。如果从 SQLX 表定义中移除某个列,Dataform 会在恢复阶段忽略与已删除列关联的所有备份数据政策和标记。
  • 架构列重命名。如果您在 SQLX 表定义中重命名现有列,Dataform 将无法将旧标记或政策关联映射到新列名称。因此,这些列上的现有标记和政策会丢失或交换,您必须手动将标记和政策重新应用于重命名的列。
  • 分区和聚类更改。不支持就地更改使用 preserveGovernanceControls 的现有表的分区或聚簇。BigQuery 会拒绝替换。如需解决此问题,请删除该表并再次运行工作流。您必须手动将列级数据政策和数据治理标记重新应用于重新创建的表。
  • 分区到期移除。从配置中移除 partitionExpirationDays 属性不会从现有表中移除分区到期时间。如需从现有表中移除分区到期时间,您必须将该选项明确设置为所选值,或手动更改表。
  • 表类型。成功运行后,该表会被报告为表克隆(INFORMATION_SCHEMA.TABLES 中的 table_type = 'CLONE')。它的行为类似于标准表,并且其克隆元数据中显示的基表不再存在。
  • 表级元数据和行访问权限政策。表级标记和行级访问权限政策不会在多次运行之间保留。您可以在创建表后重新应用行访问权限政策,方法是在 SQLX 表定义文件的 post_operations 块中定义 SQL DDL 语句。
  • 非事务性表交换。BigQuery DDL 操作不具有事务性。如果工作流执行在原子重命名序列中途失败,并且目标表处于中间状态(例如,表被永久重命名为 TABLE_NAME_old 或保留为 df_tmp_intermediate),则可能需要手动干预。
  • 政策标记。自动化治理保留不支持 BigQuery 政策标记。而是应向列添加 BigQuery 数据治理标记(预览版)和数据政策,以使用内置 SQL 集成。

存储费用注意事项

启用 preserveGovernanceControls 后,Dataform 会在每次运行(或每次对增量表进行完全刷新)时通过临时暂存表重新构建表。与 BigQuery 中的任何表重新创建一样,表数据的先前版本会在时间旅行中保留,保留时长为数据集的配置窗口(默认为 7 天),随后是 7 天的故障安全期。

逻辑存储结算模式是 BigQuery 中的默认结算模式,而物理存储结算模式是按数据集配置的选择启用设置。如果您的数据集使用物理存储结算,则除了活跃表之外,系统还会对这些保留的时间旅行和故障安全数据进行结算。因此,频繁重建的大型表可能会显著增加存储费用。

如需降低此费用,请执行以下操作:

后续步骤