Delta Lake 生成的列

重要

此功能目前以公共预览版提供。

Delta Lake 支持生成的列,这些列是一种特殊类型的列,其值基于用户指定的函数在 Delta 表中的其他列上自动生成。 当写入具有生成的列的表且未显式为其提供值时,Delta Lake 会自动计算这些值。 例如,可以从时间戳列自动生成日期列(用于按日期对表进行分区);对表进行的任何写入都只需为时间戳列指定数据。 但是,如果显式为这些表提供值,则这些值必须满足约束 (<value> <=> <generation expression>) IS TRUE,否则写入将失败并出现错误。

重要

使用生成的列创建的表的表写入器协议版本高于默认版本。 请参阅 Azure Databricks 如何管理 Delta Lake 功能兼容性?,了解表协议版本控制以及拥有更高版本的表协议版本意味着什么。

创建包含生成的列的表

下面的示例介绍如何使用生成的列创建表:

SQL

CREATE TABLE default.people10m (
  id INT,
  firstName STRING,
  middleName STRING,
  lastName STRING,
  gender STRING,
  birthDate TIMESTAMP,
  dateOfBirth DATE GENERATED ALWAYS AS (CAST(birthDate AS DATE)),
  ssn STRING,
  salary INT
)

Python

DeltaTable.create(spark) \
  .tableName("default.people10m") \
  .addColumn("id", "INT") \
  .addColumn("firstName", "STRING") \
  .addColumn("middleName", "STRING") \
  .addColumn("lastName", "STRING", comment = "surname") \
  .addColumn("gender", "STRING") \
  .addColumn("birthDate", "TIMESTAMP") \
  .addColumn("dateOfBirth", DateType(), generatedAlwaysAs="CAST(birthDate AS DATE)") \
  .addColumn("ssn", "STRING") \
  .addColumn("salary", "INT") \
  .execute()

Scala

DeltaTable.create(spark)
  .tableName("default.people10m")
  .addColumn("id", "INT")
  .addColumn("firstName", "STRING")
  .addColumn("middleName", "STRING")
  .addColumn(
    DeltaTable.columnBuilder("lastName")
      .dataType("STRING")
      .comment("surname")
      .build())
  .addColumn("lastName", "STRING", comment = "surname")
  .addColumn("gender", "STRING")
  .addColumn("birthDate", "TIMESTAMP")
  .addColumn(
    DeltaTable.columnBuilder("dateOfBirth")
     .dataType(DateType)
     .generatedAlwaysAs("CAST(dateOfBirth AS DATE)")
     .build())
  .addColumn("ssn", "STRING")
  .addColumn("salary", "INT")
  .execute()

生成的列像普通列一样存储。 也就是说,它们会占用存储空间。

以下限制适用于生成的列:

  • 生成表达式可以使用 Spark 中的任何 SQL 函数,这些函数在给定相同的参数值时始终返回相同的结果,但以下类型的函数除外:
    • 用户定义的函数。
    • 聚合函数。
    • 窗口函数。
    • 返回多行的函数。

每当以下表达式之一定义了分区列时,Delta Lake 就可为查询生成分区筛选器:

注意

Databricks Runtime 10.4 LTS 及更低版本中需要 Photon。 Databricks Runtime 11.3 LTS 及更高版本中不需要 Photon。

  • CAST(col AS DATE)col 的类型为 TIMESTAMP
  • YEAR(col)col 的类型为 TIMESTAMP
  • YEAR(col), MONTH(col) 定义的两个分区列,col 的类型为 TIMESTAMP
  • YEAR(col), MONTH(col), DAY(col) 定义的三个分区列,col 的类型为 TIMESTAMP
  • YEAR(col), MONTH(col), DAY(col), HOUR(col) 定义的四个分区列,col 的类型为 TIMESTAMP
  • SUBSTRING(col, pos, len)col 的类型为 STRING
  • DATE_FORMAT(col, format)col 的类型为 TIMESTAMP
    • 只能使用以下模式的日期格式:yyyy-MMyyyy-MM-dd-HH
    • 在 Databricks Runtime 10.4 LTS 及更高版本中,还可以使用以下模式:yyyy-MM-dd

如果上述表达式之一定义了分区列,并且查询使用生成表达式的基础列筛选数据,则 Delta Lake 会查找基础列和生成的列之间的关系,并在可能的情况下,基于生成的分区列填充分区筛选器。 例如,如果提供了以下表:

CREATE TABLE events(
eventId BIGINT,
data STRING,
eventType STRING,
eventTime TIMESTAMP,
eventDate date GENERATED ALWAYS AS (CAST(eventTime AS DATE))
)
PARTITIONED BY (eventType, eventDate)

然后运行以下查询:

SELECT * FROM events
WHERE eventTime >= "2020-10-01 00:00:00" <= "2020-10-01 12:00:00"

则 Delta Lake 会自动生成一个分区筛选器,这样,即使未指定分区筛选器,上述查询也只会读取分区 date=2020-10-01 中的数据。

另举一例,如果提供了以下表:

CREATE TABLE events(
eventId BIGINT,
data STRING,
eventType STRING,
eventTime TIMESTAMP,
year INT GENERATED ALWAYS AS (YEAR(eventTime)),
month INT GENERATED ALWAYS AS (MONTH(eventTime)),
day INT GENERATED ALWAYS AS (DAY(eventTime))
)
PARTITIONED BY (eventType, year, month, day)

然后运行以下查询:

SELECT * FROM events
WHERE eventTime >= "2020-10-01 00:00:00" <= "2020-10-01 12:00:00"

则 Delta Lake 会自动生成一个分区筛选器,这样,即使未指定分区筛选器,上述查询也只会读取分区 year=2020/month=10/day=01 中的数据。

可以使用 EXPLAIN 子句并检查提供的计划,以查看 Delta Lake 是否自动生成任何分区筛选器。

在 Delta Lake 中使用标识列

重要

在 Delta 表上声明标识列会禁用并发事务。 仅在不需要对目标表进行并发写入的用例中使用标识列。

Delta Lake 标识列是一种生成的列,将为插入到表中的每条记录分配唯一值。 以下示例演示了在 create table 语句期间声明标识列的基本语法:

SQL

CREATE TABLE table_name (
  id_col1 BIGINT GENERATED ALWAYS AS IDENTITY,
  id_col2 BIGINT GENERATED ALWAYS AS IDENTITY (START WITH -1 INCREMENT BY 1),
  id_col3 BIGINT GENERATED BY DEFAULT AS IDENTITY,
  id_col4 BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH -1 INCREMENT BY 1)
 )

Python

from delta.tables import DeltaTable, IdentityGenerator
from pyspark.sql.types import LongType

DeltaTable.create()
  .tableName("table_name")
  .addColumn("id_col1", dataType=LongType(), generatedAlwaysAs=IdentityGenerator())
  .addColumn("id_col2", dataType=LongType(), generatedAlwaysAs=IdentityGenerator(start=-1, step=1))
  .addColumn("id_col3", dataType=LongType(), generatedByDefaultAs=IdentityGenerator())
  .addColumn("id_col4", dataType=LongType(), generatedByDefaultAs=IdentityGenerator(start=-1, step=1))
  .execute()

Scala

import io.delta.tables.DeltaTable
import org.apache.spark.sql.types.LongType

DeltaTable.create(spark)
  .tableName("table_name")
  .addColumn(
    DeltaTable.columnBuilder(spark, "id_col1")
      .dataType(LongType)
      .generatedAlwaysAsIdentity().build())
  .addColumn(
    DeltaTable.columnBuilder(spark, "id_col2")
      .dataType(LongType)
      .generatedAlwaysAsIdentity(start = -1L, step = 1L).build())
  .addColumn(
    DeltaTable.columnBuilder(spark, "id_col3")
      .dataType(LongType)
      .generatedByDefaultAsIdentity().build())
  .addColumn(
    DeltaTable.columnBuilder(spark, "id_col4")
      .dataType(LongType)
      .generatedByDefaultAsIdentity(start = -1L, step = 1L).build())
  .execute()

注意

Databricks Runtime 16.0 及更高版本提供用于标识列的 Scala 和 Python API。

若要查看用于创建包含标识列的表的所有 SQL 语法选项,请参阅 CREATE TABLE [USING]

可以选择指定以下内容:

  • 起始值。
  • 步长,可以是正值或负值。

起始值和步长默认为 1。 不能指定步长 0

标识列分配的值是唯一的,朝指定步长的方向按指定步长大小的倍数递增,但不保证是连续的。 例如,如果起始值为 0,步长为 2,则所有值都是正偶数,但有些偶数可能会被跳过。

使用子句 GENERATED BY DEFAULT AS IDENTITY 时,插入操作可以为标识列指定值。 将该子句修改为 GENERATED ALWAYS AS IDENTITY,以替代手动设置值的功能。

标识列仅支持 BIGINT 类型,如果分配的值超过 BIGINT 支持的范围,则操作将会失败。

若要了解如何将标识列值与数据同步,请参阅 ALTER TABLE

CTAS 和标识列

在使用 CREATE TABLE table_name AS SELECT (CTAS) 语句时,不能定义架构、标识列约束或任何其他表规范。

要使用标识列创建新表并使用现有数据填充该表,请执行以下操作:

  1. 创建具有正确架构的表,包括标识列定义和他其表属性。
  2. 运行 INSERT 操作。

以下示例使用 DEFAULT 关键字来定义标识列。 如果插入到表中的数据包括标识列的有效值,则使用这些值。

CREATE OR REPLACE TABLE new_table (
  id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 5),
  event_date DATE,
  some_value BIGINT
);

-- Inserts records including existing IDs
INSERT INTO new_table
SELECT id, event_date, some_value FROM old_table;

-- Insert records and generate new IDs
INSERT INTO new_table
SELECT event_date, some_value FROM new_records;

标识列限制

使用标识列时存在以下限制:

  • 启用了标识列的表不支持并发事务。
  • 不能按标识列对表进行分区。
  • 不能使用 ALTER TABLEADDREPLACECHANGE 标识列。
  • 不能更新现有记录的标识列的值。

注意

若要更改现有记录的 IDENTITY 值,必须删除该记录并将其 INSERT 为新记录。