PostgreSQL 12 enthält eine neue Funktion namens generierte Spalten . Andere beliebte RDBMS unterstützen bereits generierte Spalten als „berechnete Spalten“ oder „virtuelle Spalten“. Mit Postgres 12 können Sie es jetzt auch in PostgreSQL verwenden. Lesen Sie weiter, um mehr zu erfahren.
Was ist eine generierte Spalte?
Eine generierte Spalte ist so etwas wie eine Ansicht, aber für Spalten. Hier ist ein einfaches Beispiel:
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=#
Wir haben eine Tabelle t erstellt mit zwei regulären Spalten namens w und h und eine generierte Spalte namens area . Der Wert von Bereich wird zum Zeitpunkt der Zeilenerstellung berechnet und auf der Festplatte gespeichert.
Der Wert der generierten Spalten wird neu berechnet, wenn die Zeile aktualisiert wird:
db=# UPDATE t SET w=40;
UPDATE 1
db=# SELECT * FROM t;
w | h | area
----+----+------
40 | 20 | 800
(1 row)
db=#
Diese Funktionalität wurde früher normalerweise mit Triggern erreicht, aber mit generierten Spalten wird dies viel eleganter und sauberer.
Einige Punkte, die Sie über generierte Spalten wissen sollten:
- Beharrlichkeit Hinweis:Derzeit muss der Wert generierter Spalten beibehalten werden und kann nicht während der Abfragezeit berechnet werden. Das Schlüsselwort „STORED“ muss in der Spaltendefinition vorhanden sein.
- Der Ausdruck :Der zur Berechnung des Werts verwendete Ausdruck muss unveränderlich sein , das heißt, sie muss deterministisch sein. Sie kann von anderen Spalten, aber nicht von anderen generierten Spalten der Tabelle abhängen.
- Indizes :Generierte Spalten können in Indizes verwendet werden, aber nicht als Partitionsschlüssel für partitionierte Tabellen.
- Kopieren und pg_dump Hinweis:Die Werte der generierten Spalten werden in der Ausgabe der Befehle „pg_dump“ und „COPY table“ weggelassen, da dies unnötig ist. Sie können sie mit
COPY (SELECT * FROM t) TO STDOUT
ausdrücklich in COPY einschließen stattCOPY t TO STDOUT
.
Ein praktisches Beispiel
Lassen Sie uns einer Tabelle mit generierten Spalten Unterstützung für die Volltextsuche hinzufügen. Hier ist eine Tabelle, die den gesamten Text aller Stücke von Shakespeare speichert:
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
);
So sehen die Daten aus:
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)
Wir fügen eine Spalte hinzu, die die Lexeme im Wert von „body“ enthält. Die Funktion to_tsvector gibt die benötigten Lexeme zurück:
shakespeare=# SELECT to_tsvector('english', 'move moving moved movable mover movability');
to_tsvector
-------------------------------------
'movabl':4,6 'move':1,2,3 'mover':5
(1 row)
Der Typ des von to_tsvector
zurückgegebenen Werts ist tsvector.
Lassen Sie uns die Tabelle ändern, um eine generierte Spalte hinzuzufügen:
ALTER TABLE scenes
ADD tsv tsvector
GENERATED ALWAYS AS (to_tsvector('english', body)) STORED;
Sie können die Änderung mit \d
sehen :
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)
Und genau so können Sie jetzt Volltextsuchen durchführen:
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=#
Weiterlesen
Wenn Sie vorberechnete/„zwischengespeicherte“ Daten benötigen, insbesondere bei einer Arbeitslast von wenigen Schreibvorgängen und vielen Lesevorgängen, sollten generierte Spalten dazu beitragen, Ihren Anwendungs-/serverseitigen Code erheblich zu vereinfachen.
Sie können die v12-Dokumentation von CREATE TABLE und ALTER TABLE lesen, um die aktualisierte Syntax zu sehen.