重要
此功能目前以公共预览版提供。
Delta Lake 支持生成的列,这些列是一种特殊类型的列,其值基于用户指定的函数在 Delta 表中的其他列上自动生成。 当写入具有生成的列的表且未显式为其提供值时,Delta Lake 会自动计算这些值。 例如,可以从时间戳列自动生成日期列(用于按日期对表进行分区);对表进行的任何写入都只需为时间戳列指定数据。 但是,如果显式为这些表提供值,则这些值必须满足约束(<value> <=> <generation expression>) IS TRUE
,否则写入将失败并出现错误。
重要
使用生成的列创建的表的表写入器协议版本高于默认版本。 请参阅 Delta Lake 功能兼容性和协议 ,了解表协议版本控制以及具有更高版本的表协议版本意味着什么。
下面的示例介绍如何使用生成的列创建表:
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
)
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()
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-MM
和yyyy-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 表上声明标识列会禁用并发事务。 仅在不需要对目标表进行并发写入的用例中使用标识列。
Delta Lake 标识列是一种生成的列,将为插入到表中的每条记录分配唯一值。 以下示例演示了在 create table 语句期间声明标识列的基本语法:
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)
)
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()
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 子句。
在使用 CREATE TABLE table_name AS SELECT
(CTAS) 语句时,不能定义架构、标识列约束或任何其他表规范。
要使用标识列创建新表并使用现有数据填充该表,请执行以下操作:
创建具有正确架构的表,包括标识列定义和他其表属性。
运行
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 TABLE
来ADD
、REPLACE
或CHANGE
标识列。 - 不能更新现有记录的标识列的值。
备注
若要更改现有记录的 IDENTITY
值,必须删除该记录并将其 INSERT
为新记录。