PostgreSQL 12には、生成された列と呼ばれる新機能が付属しています。 。他の人気のあるRDBMSは、「計算列」または「仮想列」として生成された列をすでにサポートしています。 Postgres 12では、PostgreSQLでも使用できるようになりました。詳細については、以下をお読みください。
生成された列とは何ですか?
生成された列は、ビューのようなものですが、列用です。基本的な例は次のとおりです:
db=# CREATE TABLE t (w real, h real, area real GENERATED ALWAYS AS (w*h) STORED);
CREATE TABLE
db=# INSERT INTO t (w, h) VALUES (10, 20);
INSERT 0 1
db=# SELECT * FROM t;
w | h | area
----+----+------
10 | 20 | 200
(1 row)
db=#
テーブルを作成しましたt wと呼ばれる2つの通常の列があります およびh 、および areaと呼ばれる生成された列 。 領域の値 行の作成時に計算され、ディスクに保持されます。
生成された列の値は、行が更新されるときに再計算されます:
db=# UPDATE t SET w=40;
UPDATE 1
db=# SELECT * FROM t;
w | h | area
----+----+------
40 | 20 | 800
(1 row)
db=#
このような機能は、以前は通常トリガーで実現されていましたが、生成された列を使用すると、はるかにエレガントでクリーンになります。
生成された列について知っておくべきいくつかのポイント:
- 永続性 :現在、生成された列の値は永続化する必要があり、クエリ時にオンザフライで計算することはできません。 「STORED」キーワードは列定義に存在する必要があります。
- 表現 :値の計算に使用される式は不変である必要があります つまり、決定論的である必要があります。テーブルの他の列に依存できますが、生成された他の列には依存できません。
- インデックス :生成された列はインデックスで使用できますが、パーティション化されたテーブルのパーティションキーとして使用することはできません。
- コピーとpg_dump :生成された列の値は、「pg_dump」および「COPYtable」コマンドの出力では不要であるため省略されています。
COPY (SELECT * FROM t) TO STDOUT
を使用して、それらを明示的にCOPYに含めることができます。COPY t TO STDOUT
ではなく 。
生成された列を使用して、テーブルに全文検索のサポートを追加しましょう。シェイクスピアのすべての戯曲の全文を保存した表は次のとおりです。
CREATE TABLE scenes (
workid text, -- denotes the name of the play (like "macbeth")
act integer, -- the act (like 1)
scene integer, -- the scene within the act (like 7)
description text, -- short desc of the scene (like "Macbeth's castle.")
body text -- full text of the scene
);
データの外観は次のとおりです。
shakespeare=# SELECT workid, act, scene, description, left(body, 200) AS body_start
shakespeare-# FROM scenes WHERE workid='macbeth' AND act=1 AND scene=1;
workid | act | scene | description | body_start
---------+-----+-------+-----------------+----------------------------------------------
macbeth | 1 | 1 | A desert place. | [Thunder and lightning. Enter three Witches]+
| | | | +
| | | | First Witch: When shall we three meet again +
| | | | In thunder, lightning, or in rain? +
| | | | +
| | | | Second Witch: When the hurlyburly's done, +
| | | | When the battle's lost and won. +
| | | |
(1 row)
「body」の値に語彙素を含む列を追加します。関数to_tsvectorは、必要な語彙素を返します。
shakespeare=# SELECT to_tsvector('english', 'move moving moved movable mover movability');
to_tsvector
-------------------------------------
'movabl':4,6 'move':1,2,3 'mover':5
(1 row)
to_tsvector
によって返される値のタイプ tsvectorです。
テーブルを変更して、生成された列を追加しましょう:
ALTER TABLE scenes
ADD tsv tsvector
GENERATED ALWAYS AS (to_tsvector('english', body)) STORED;
\d
で変更を確認できます :
shakespeare=# \d scenes
Table "public.scenes"
Column | Type | Collation | Nullable | Default
-------------+----------+-----------+----------+----------------------------------------------------------------------
workid | text | | not null |
act | integer | | not null |
scene | integer | | not null |
description | text | | |
body | text | | |
tsv | tsvector | | | generated always as (to_tsvector('english'::regconfig, body)) stored
Indexes:
"scenes_pkey" PRIMARY KEY, btree (workid, act, scene)
そして、まさにそのように、フルテキスト検索を実行できるようになりました:
shakespeare=# SELECT
workid, act, scene, ts_headline(body, q)
FROM (
SELECT
workid, act, scene, body, ts_rank(tsv, q) as rank, q
FROM
scenes, plainto_tsquery('uneasy head') q
WHERE
tsv @@ q
ORDER BY
rank DESC
LIMIT
5
) p
ORDER BY
rank DESC;
workid | act | scene | ts_headline
----------+-----+-------+-----------------------------------------------------------
henry4p2 | 3 | 1 | <b>Uneasy</b> lies the <b>head</b> that wears a crown. +
| | | +
| | | Enter WARWICK and Surrey +
| | | +
| | | Earl of Warwick
henry5 | 2 | 2 | <b>head</b> assembled them? +
| | | +
| | | Lord Scroop: No doubt, my liege, if each man do his best.+
| | | +
| | | Henry V: I doubt not that; since we are well persuaded +
| | | We carry not a heart with us from hence
(2 rows)
shakespeare=#
事前に計算された/「キャッシュされた」データが必要な場合、特に書き込みが少なく読み取りが多いワークロードでは、生成された列がアプリケーション/サーバー側のコードを大幅に簡素化するのに役立ちます。
CREATETABLEおよびALTERTABLEのv12ドキュメントを読んで、更新された構文を確認できます。