生成列は常に他の列から計算される特別な列です。 ですから、これは列におけるテーブルに対するビューのようなものです。 生成列には格納と仮想の2種類があります。 格納生成列はそれが書かれた(挿入または更新)時に計算され、あたかも通常の列のようにストレージが割り当てられます。 仮想列にはストレージは割り当てられず、列が読み出された時に計算されます。 つまり、仮想生成列はビューに似ており、格納生成列はマテリアライズドビューに似ています。(常に自動的に更新される点は除きます。) 今の所PostgreSQLは格納生成列のみを実装しています。
生成列を作るには、CREATE TABLE
でGENERATED ALWAYS AS
節を使ってください。例を示します。
CREATE TABLE people (
...,
height_cm numeric,
height_in numeric GENERATED ALWAYS AS (height_cm / 2.54) STORED
);
種類を格納生成列として選択するためにキーワードSTORED
を選択する必要があります。
より詳しくはCREATE TABLEをご覧ください。
生成列には直接書き込みができません。
INSERT
あるいはUPDATE
コマンドでは値を生成列に指定できませんが、キーワードDEFAULT
が指定できます。
デフォルトを備えた列と生成列の違いを考えてみましょう。
列のデフォルトは、他に値が指定されないときに、最初に行が挿入された時に一度だけ評価されます。
生成列は、行が変更された時に常に更新され、上書きはできません。
デフォルトを備えた列はテーブルの他の列を参照することはできませんが、生成式は通常それを行います。
デフォルトを備えた列は揮発性の関数、たとえばrandom()
や現在時刻を参照する関数を使用できますが、これは生成列では許されていません。
生成列の定義と生成列を伴うテーブルには以下の制限が適用されます。
生成式は不変関数のみが使用でき、副問合せ、あるいは現在の行以外へのいかなる参照も使用できません。
生成式はほかの生成列を参照できません。
生成式はtableoid
以外のシステム列を参照できません。
生成列は列デフォルトも識別定義も持てません。
生成列はパーティションキーの一部にはなれません。
外部テーブルは生成列を持つことができます。 詳細はCREATE FOREIGN TABLEをご覧ください。
継承とパーティショニングの場合:
親列が生成列である場合、その子列も生成列でなければなりません。 しかし、子列は異なる生成式を持つことができます。 行の挿入または更新時に実際に適用される生成式は、物理的に行が存在するテーブルに関連付けられたものです。 (これは列デフォルトの動作とは異なります。 列デフォルトの場合、問い合わせで指定されたテーブルに関連付けられたデフォルト値が適用されます。)
親列が生成列でない場合、その子列も生成列であってはなりません。
継承されたテーブルの場合、CREATE TABLE ... INHERITS
にGENERATED
句を持たない子テーブル継承を書き込むと、そのGENERATED
句は自動的に親からコピーされます。
ALTER TABLE ... INHERIT
は、親列と子列が生成状態に一致していることを要求しますが、それらの生成式が一致することを要求しません。
パーティション化されたテーブルの場合も同様です。
CREATE TABLE ... PARTITION OF
にGENERATED
句を持たない子テーブル継承を書き込むと、そのGENERATED
句は自動的に親からコピーされます。
ALTER TABLE ... ATTACH PARTITION
は、親列と子列が生成状態に一致していることを要求しますが、それらの生成式が一致することを要求しません。
多重継承では、一つの親列が生成列なら、すべての親列は生成列でなければなりません。 すべての親列が同じ生成式を持たない場合は、子列の望ましい式を明示的に指定する必要があります。
生成列の利用の際には以下の追加の考慮が必要です。
生成列は元になる基底列とは別にアクセス権限を維持します。 ですから、ある特定のロールが生成列を読み出しつつも、元になる基底列からは読み出さないように調整できます。
概念的には、生成列はBEFORE
トリガが走った後に更新されます。
ですから、BEFORE
トリガの中で基底列に加えられた変更は生成列に反映されます。
しかし逆に生成列をBEFORE
トリガの中でアクセスすることは許されません。
生成列はロジカルレプリケーションからスキップされ、CREATE PUBLICATION
の列リストでは指定できません。