Delta Lake 生成的列

重要

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

Delta Lake 生成列会根据表中其他列上的用户定义表达式,自动计算并存储其值。 在未为生成的列提供值的情况下写入表时,Delta Lake 会自动计算它们。 如果确实提供了值,它们必须满足 (<value> <=> <generation expression>) IS TRUE,否则写入将失败。 请参阅 Azure Databricks 上的限制条件。

例如,您可以从base_price * 1.1计算出一个price_with_tax列,而无需在写入时为price_with_tax指定数据。

与常规列一样,生成的列以物理方式存储在表的基础数据文件中。

注意

启用生成的列会升级表编写器协议。 这可能会影响与外部 Delta Lake 客户端的兼容性。 请参阅 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()

支持的表达式

生成表达式可以使用任何始终为相同输入返回相同结果的确定性 SQL 函数。 例如:

  • 算术:base_price * 1.1
  • 字符串函数: CONCAT(first_name, ' ', last_name)SUBSTRING(col, 1, 3)
  • 日期函数: CAST(birthDate AS DATE)YEAR(eventTime)

不支持以下函数类型:

  • 用户定义的函数
  • 聚合函数
  • 窗口函数
  • 返回多行的函数

分区筛选器生成

注意

Databricks 建议对所有新的 Delta Lake 表进行液体聚类分析。 请参阅对表使用 liquid 聚类分析

当你使用生成列对表进行分区,并基于基列进行查询时,如果可能,Delta Lake 会自动推导出分区过滤条件。 无需对生成的分区列进行显式筛选。 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

示例:单个分区

例如,给定以下表格:

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" AND eventTime <= "2020-10-01 12:00:00"

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

使用 EXPLAIN 子句,并检查所提供的执行计划,查看 Delta Lake 是否会自动生成任何分区过滤器。

示例:多个分区

例如,给定以下表格:

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" AND eventTime <= "2020-10-01 12:00:00"

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

使用 EXPLAIN 子句,并检查所提供的执行计划,以查看 Delta Lake 是否会自动生成任何分区筛选条件。

标识列

重要

在 Delta Lake 表中定义标识列会导致无法进行并发事务。 仅在不需要并发写入目标表的用例中使用标识列。 请参阅标识列的限制

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 ... COLUMN 子句

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 (id, event_date, some_value)
SELECT id, event_date, some_value FROM old_table;

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

标识列限制

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

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

注意

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

生成的列和列掩码

生成的列无法引用应用了列掩码的列,因为生成的值将显示掩码保护的基础数据。 这会引发错误,查询失败。 请参阅 行筛选器和列掩码

下面是错误的示例:

若要解决所有这些错误,必须重新设计表,以便生成的列和掩码列不会重叠。