Delta Lakeのジェネレーテッドカラム
プレビュー
この機能は パブリック プレビュー段階です。
Delta Lake では、Delta Lake テーブル内の他の列に対するユーザー指定の関数に基づいて値が自動的に生成される特殊な種類の列である生成列がサポートされています。生成された列を持つテーブルに書き込む際に、それらの値を明示的に指定しない場合、Delta Lake は自動的に値をコンピュートします。たとえば、タイムスタンプ列から日付列(テーブルを日付でパーティション分割するため)を自動的に生成できます。テーブルへの書き込みでは、タイムスタンプ列のデータを指定するだけで済みます。ただし、それらの値を明示的に指定する場合、値は制約(<value> <=> <generation expression>) IS TRUEを満たさなければなりません。そうでない場合、書き込みはエラーで失敗します。
生成されたカラムを使用して作成されたテーブルは、デフォルトよりもテーブルライタープロトコルのバージョンが高くなります。Delta Lake 機能の互換性とプロトコルを参照して、テーブル プロトコルのバージョン管理と、テーブル プロトコル バージョンの上位バージョンを使用することの意味を理解してください。
ジェネレーテッドカラムを含むテーブルを作成する
次の例は、生成されたカラムを持つテーブルを作成する方法を示しています:
- SQL
- Python
- Scala
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 では、パーティション列が次のいずれかの式で定義されている場合は常に、クエリーのパーティションフィルターを生成できます。
Photon は Databricks Runtime 10.4 LTS 以下で必要です。Photonは Databricks Runtime 11.3 LTS 以降では必要ありません。
CAST(col AS DATE)colのタイプはTIMESTAMPです。YEAR(col)colのタイプはTIMESTAMPです。YEAR(col), MONTH(col)によって定義される2つのパーティション列、およびcolの型はTIMESTAMPです。YEAR(col), MONTH(col), DAY(col)によって定義された3つのパーティション列があり、colの型はTIMESTAMPです。YEAR(col), MONTH(col), DAY(col), HOUR(col)によって定義された4つのパーティション列と、colの型はTIMESTAMPです。SUBSTRING(col, pos, len)colのタイプですSTRINGDATE_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 のデータのみを読み取るように、自動的にパーティションフィルターを生成します。
もう1つの例として、次のテーブルがあるとします。
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 での ID 列の使用
Delta Lake テーブルで ID 列を宣言すると、並列トランザクションは無効になります。ID カラムは、ターゲットテーブルへの並列書き込みが不要なユースケースでのみ使用してください。
Delta Lake の ID 列は、テーブルに挿入された各レコードに一意の値を割り当てる生成列の一種です。次の例は、テーブル作成ステートメント中に識別列を宣言するための基本的な構文です。
- SQL
- Python
- Scala
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()
ID 列のScalaとPython APIは、Databricks Runtime 16.0 以降で使用できます。
ID 列を持つテーブルを作成するためのすべての SQL 構文オプションについては、「CREATE TABLE [USING]」を参照してください。
オプションで、以下の項目を指定できます:
- 開始値です。
- ステップサイズ。正または負の値を指定できます。
開始値とステップサイズは両方とも1がデフォルトです。0 のステップサイズを指定することはできません。
ID列によって割り当てられる値は一意であり、指定されたステップの方向に、指定されたステップサイズの倍数でインクリメントされますが、連続していることは保証されません。例えば、開始値が0でステップサイズが2の場合、すべての値は正の偶数ですが、一部の偶数はスキップされる可能性があります。
句GENERATED BY DEFAULT AS IDENTITYを使用する場合、INSERT操作ではID列に値を指定できます。手動で値を設定する機能を上書きするために、GENERATED ALWAYS AS IDENTITY となるように節を変更してください。
ID 列は BIGINT 型のみをサポートし、割り当てられた値が BIGINTでサポートされている範囲を超えると操作は失敗します。
ID 列の値とデータの同期については、 ALTER TABLE ...COLUMN 句をご覧ください。
CTAS と ID 列
CREATE TABLE table_name AS SELECT (CTAS) ステートメントを使用する場合、スキーマ、ID 列の制約、またはその他のテーブル仕様を定義することはできません。
ID 列を持つ新しいテーブルを作成し、既存のデータを設定するには、次のようにします。
- 適切なスキーマ、ID 列の定義、およびその他のテーブルプロパティを含めてテーブルを作成してください。
INSERT操作を実行する。
次の例では、DEFAULT キーワードを使用して ID 列を定義します。テーブルに挿入されたデータに ID 列の有効な値が含まれている場合、これらの値が使用されます。
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;
ID 列の制限
ID 列の操作には、次の制限があります:
- ID カラムが有効なテーブルでは、並列トランザクションはサポートされていません。
- テーブルをID列でパーティション分割することはできません。
ALTER TABLEを使用して、ID 列をADD、REPLACE、またはCHANGEすることはできません。- 既存のレコードの ID 列の値を更新できません。
既存のレコードのIDENTITY値を変更するには、レコードを削除し、新しいレコードとしてINSERTする必要があります。
生成列と列マスク
生成された列は、列マスクが適用されている列を参照できません。生成された値が、マスクによって保護されている基になるデータを明らかにするためです。エラーが発生し、クエリーは失敗します。「行フィルターと列マスク」を参照してください。
エラーの例を次に示します
-
マスクされた列を参照する式を持つジェネレーテッドカラムを作成することはできません。COLUMN_MASKS_GENERATED_COLUMN_UNSUPPORTED を発生します。
SQLCREATE TABLE tbl (
a INT MASK masking_function,
generated_col INT GENERATED ALWAYS AS (a + 1)
) USING DELTA; -
すでに生成された列が参照している列には、列マスクを適用できません。以下を発生 COLUMN_MASKS_REFERENCED_BY_GENERATED_COLUMN.ADD_MASK。
SQLCREATE TABLE tbl (
a INT,
generated_col INT GENERATED ALWAYS AS (a + 1)
) USING DELTA;
ALTER TABLE tbl ALTER COLUMN a SET MASK masking_function; -
生成された列が既にマスクされた列を参照しているテーブルからの読み取りもブロックされます。「COLUMN_MASKS_REFERENCED_BY_GENERATED_COLUMN.READ_BLOCKED」が発生します。
これらのエラーをすべて解決するには、生成された列とマスクされた列が重複しないようにテーブルを再設計する必要があります。