生成列は、他の列から計算される特殊な列です。格納生成列と仮想生成列に分類されます。Hologres V3.1 以降では、格納生成列がサポートされています。格納生成列は、データの書き込みまたは更新時に自動的に計算され、ストレージ領域を消費します。仮想生成列は現在サポートされていません。このトピックでは、Hologres で格納生成列を使用する方法について説明します。
シナリオ
-
必要なフィールドの自動計算:計算ロジックを手動で処理する必要がなくなります。
-
データ一貫性:人的ミスやコードロジックの問題による不整合を防止します。
-
クエリパフォーマンスの最適化:高頻度のクエリシナリオでは、格納生成列の読み取りは通常の列の読み取りと同等です。
-
ビジネスロジックの簡素化:定型的なデータ変換操作における SQL の複雑さを低減します。
ビジネス要件に基づいて生成列を適切に使用することで、開発効率を大幅に向上させ、データの信頼性を確保できます。
構文
生成列を宣言するには GENERATED ALWAYS AS 句を使用し、格納生成列を指定するには STORED キーワードを使用します。
-
生成列を含むテーブルを作成します。
CREATE TABLE generated_col_t ( [...,] col1 INT, col2 INT GENERATED ALWAYS AS (col1 + 1) STORED ); -
生成列を含む論理パーティションテーブルを作成し、その生成列をパーティションキーとして使用します。
CREATE TABLE generated_col_logical_part ( a TEXT, b INT, ts TIMESTAMP NOT NULL, d TIMESTAMP GENERATED ALWAYS AS (date_trunc('day', ts)) STORED NOT NULL ) LOGICAL PARTITION BY LIST(d);
注意事項
-
CREATE TABLE
-
生成列を定義する場合、IMMUTABLE な関数または式のみがサポートされます。CURRENT_DATE や RANDOM など、IMMUTABLE でない関数はサポートされていません。
-
生成列の式は、別の生成列を参照できません。また、生成列に
default値を定義することもできません。 -
パーティションテーブルを作成する場合、論理パーティションテーブル のパーティションキーとして生成列を設定できますが、物理パーティションテーブル のパーティションキーとして生成列を設定することはできません。パーティションテーブル内の通常の列は生成列に設定できます。
-
CREATE FOREIGN TABLE を使用して外部テーブルを作成する場合、生成列はサポートされていません。
-
生成列は、プライマリキー、分散キー、セグメントキー、クラスター化インデックス、ビットマップインデックス、辞書エンコード列など、各種 Hologres インデックスとして設定できます。
-
-
ALTER TABLE
-
列を生成列として追加することはできません。
-
生成列は削除できます。ただし、生成列を削除する前に、その生成列が参照する列を削除することはできません。
-
生成列のデータ型、または生成列が参照する列のデータ型は変更できません。これを実現するには、REBUILD 機能の使用を推奨します。詳細については、「REBUILD」をご参照ください。
-
生成列の名前は変更できます。
-
-
DML/DQL
-
データを挿入または更新する際は、文から生成列を省略するか、値として
defaultを指定できます。生成列に特定の値を直接挿入することはできません。 -
データを更新する場合、生成列またはそれが参照する列が分散キーである場合、その列の更新はサポートされていません。
-
固定実行計画を使用して更新を実行する場合、プライマリキーに生成列が含まれているときは、プライマリキーに生成列が参照するすべての列も含める必要があります。
-
固定実行計画を使用して部分列更新を実行する場合、生成列が複数の通常列を参照しているときは、それらの列の一部のみを更新することはサポートされていません。
-
生成列を含むテーブルに対するその他の操作はすべてサポートされています。これには、HQE エンジンによる読み取り/書き込み操作、固定実行計画による読み取り/書き込み操作、および Copy などの操作が含まれます。
-
-
その他の操作
-
CREATE TABLE LIKEは、生成列を含むテーブルをサポートしています。生成列のプロパティを保持するには、hg_experimental_enable_create_table_like_propertiesパラメーターを有効にする必要があります。 -
CREATE TABLE AS を使用する場合、生成列を含む元のテーブルはサポートされていません。
-
生成列を含むテーブルのパラメーターを変更するには、REBUILD 構文 (テーブルグループの移行を含む) を使用できます。詳細については、「REBUILD」をご参照ください。HG_MOVE_TABLE_TO_TABLE_GROUP 構文によるテーブルグループの移行はサポートされていません。
-
生成列を含むテーブルに対して
INSERT OVERWRITE操作を実行するには、Hologres V3.1 以降でサポートされているネイティブのINSERT OVERWRITE構文を使用します。従来のhg_insert_overwrite構文はサポートされていません。詳細については、「INSERT OVERWRITE」をご参照ください。
-
例
-
生成列を含むテーブルを作成します。
CREATE TABLE generated_col_t ( id INT PRIMARY KEY, col1 INT, col2 INT GENERATED ALWAYS AS (col1 + 1) STORED ); -
データをインポートします。
-
生成列以外のすべての列にデータをインポートできます。例:
INSERT INTO generated_col_t VALUES (1, 1); INSERT INTO generated_col_t(id, col1) VALUES (2, 2);SELECT * FROM generated_col_t;クエリを実行すると、次の結果が返されます。id col1 col2 1 1 2 2 2 3 -
データのインポート時に、生成列に
defaultキーワードを使用します。例:INSERT INTO generated_col_t VALUES (3, 3, default); INSERT INTO generated_col_t(id, col1, col2) VALUES (4, 4, default);SELECT * FROM generated_col_t;クエリを実行すると、次の結果が返されます。id col1 col2 4 4 5 2 2 3 3 3 4 1 1 2 -
非サポート:生成列に直接データをインポートします。例:
INSERT INTO generated_col_t VALUES (5, 5, 6); INSERT INTO generated_col_t(id, col1, col2) VALUES (6, 6, 7);次のエラーが返されます。
ERROR: cannot insert into column "col2" Detail: Column "col2" is a generated column.
-
-
データを更新します。
-
生成列以外の列は更新できます。例:
UPDATE generated_col_t SET col1 = 2 WHERE id = 1;SELECT * FROM generated_col_t;クエリを実行すると、次の結果が返されます。id col1 col2 2 2 3 3 3 4 4 4 5 1 2 3 -- この行は変更されています -
更新時に、生成列に
defaultキーワードを使用します。例:UPDATE generated_col_t SET col1 = 3, col2 = default WHERE id = 2;SELECT * FROM generated_col_t;クエリを実行すると、次の結果が返されます。id col1 col2 3 3 4 2 3 4 -- この行は変更されています 4 4 5 1 2 3 -
非サポート:生成列を直接更新します。例:
UPDATE generated_col_t SET col2 = 4 WHERE id = 3;次のエラーが返されます。
ERROR: column "col2" can only be updated to DEFAULT Detail: Column "col2" is a generated column.
-
特定のパラメーター型に対して関数が IMMUTABLE であるかどうかは、次の SQL クエリで確認できます。たとえば、to_char 関数が IMMUTABLE となるのは、入力が TIMESTAMP WITH TIME ZONE 型である場合に限られます。したがって、この関数を生成列で使用する場合は、パラメーター型が一致していることを確認する必要があります。
SELECT n.nspname AS "Schema",
p.proname AS "Name",
pg_catalog.pg_get_function_result(p.oid) AS "Result data type",
pg_catalog.pg_get_function_arguments(p.oid) AS "Argument data types",
CASE p.prokind
WHEN 'a' THEN 'agg'
WHEN 'w' THEN 'window'
WHEN 'p' THEN 'proc'
ELSE 'func'
END AS "Type",
CASE
WHEN p.provolatile = 'i' THEN 'immutable'
WHEN p.provolatile = 's' THEN 'stable'
WHEN p.provolatile = 'v' THEN 'volatile'
END AS "Volatility",
CASE
WHEN p.proparallel = 'r' THEN 'restricted'
WHEN p.proparallel = 's' THEN 'safe'
WHEN p.proparallel = 'u' THEN 'unsafe'
END AS "Parallel",
pg_catalog.pg_get_userbyid(p.proowner) AS "Owner",
CASE WHEN prosecdef THEN 'definer' ELSE 'invoker' END AS "Security",
pg_catalog.array_to_string(p.proacl, E'\n') AS "Access privileges",
l.lanname AS "Language",
p.prosrc AS "Source code",
pg_catalog.obj_description(p.oid, 'pg_proc') AS "Description"
FROM pg_catalog.pg_proc p
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
LEFT JOIN pg_catalog.pg_language l ON l.oid = p.prolang
-- Target function
WHERE p.proname OPERATOR(pg_catalog.~) '^(to_char)$' COLLATE pg_catalog.default
AND pg_catalog.pg_function_is_visible(p.oid)
ORDER BY 1, 2, 4;