Eine generierte Spalte ist eine Spalte, die immer aus anderen Spalten in einer Zeile berechnet wird. Diese Spalten können eine Abfrage vereinfachen, die Kosten für die Auswertung eines Ausdrucks zur Abfragezeit sparen und indiziert oder als Fremdschlüssel verwendet werden. Auf dieser Seite wird beschrieben, wie Sie diesen Spaltentyp in Ihrer Datenbank für GoogleSQL- und PostgreSQL-Dialektdatenbanken verwalten.
Generierte Spalte zu einer neuen Tabelle hinzufügen
Im folgenden CREATE TABLE-Snippet wird eine Tabelle zum Speichern von Informationen über Nutzer erstellt. Wir haben Spalten für FirstName und LastName und definieren eine generierte Spalte für FullName, die die Verkettung von FirstName und LastName darstellt. Der SQL-Code in Klammern wird als Generierungsausdruck bezeichnet.
Eine generierte Spalte kann als STORED markiert werden, um die Kosten für die Auswertung des Ausdrucks zur Abfragezeit zu sparen. Der Wert von FullName wird daher nur berechnet, wenn eine neue Zeile eingefügt wird oder wenn FirstName oder LastName für eine vorhandene Zeile aktualisiert wird. Der berechnete Wert wird zusammen mit den anderen Spalten in der Tabelle gespeichert.
GoogleSQL
CREATE TABLE Users (
Id STRING(20) NOT NULL,
FirstName STRING(50),
LastName STRING(50),
Age INT64 NOT NULL,
FullName STRING(100) AS (FirstName || ' ' || LastName) STORED
) PRIMARY KEY (Id);
PostgreSQL
CREATE TABLE users (
id VARCHAR(20) NOT NULL,
firstname VARCHAR(50),
lastname VARCHAR(50),
age BIGINT NOT NULL,
fullname VARCHAR(100) GENERATED ALWAYS AS (firstname || ' ' || lastname) STORED,
PRIMARY KEY(id)
);
Sie können eine nicht gespeicherte generierte Spalte erstellen, indem Sie das Attribut STORED in der DDL weglassen. Diese Art von generierter Spalte wird zur Abfragezeit ausgewertet und kann eine Abfrage vereinfachen. In PostgreSQL können Sie mit dem Attribut VIRTUAL eine nicht gespeicherte generierte Spalte erstellen.
GoogleSQL
FullName STRING(MAX) AS (CONCAT(FirstName, " ", LastName))
PostgreSQL
fullname text GENERATED ALWAYS AS (firstname || ' ' || lastname) VIRTUAL
expressionkann ein beliebiger gültiger SQL-Ausdruck sein, der dem Datentyp der Spalte mit folgenden Einschränkungen zugewiesen werden kann.Der Ausdruck kann nur auf Spalten in derselben Tabelle verweisen.
Der Ausdruck darf keine Unterabfragen enthalten.
Ausdrücke mit nicht deterministischen Funktionen wie
PENDING_COMMIT_TIMESTAMP(),CURRENT_DATE()undCURRENT_TIMESTAMP()können nicht in eineSTORED-generierte Spalte oder eine generierte Spalte umgewandelt werden, die indexiert ist.Sie können den Ausdruck einer
STORED- oder indexierten generierten Spalte nicht ändern.
Bei Datenbanken mit GoogleSQL-Dialekt muss eine nicht gespeicherte generierte Spalte vom Typ
STRINGoderBYTESeine Länge vonMAXhaben.Bei Datenbanken mit PostgreSQL-Dialekt muss eine nicht gespeicherte oder virtuelle generierte Spalte vom Typ
VARCHAReine Länge vonMAXhaben.Das Attribut
STOREDnach dem Ausdruck bewirkt, dass das Ergebnis des Ausdrucks zusammen mit anderen Spalten der Tabelle gespeichert wird. Bei späteren Aktualisierungen einer der referenzierten Spalten wird der Ausdruck noch einmal ausgewertet und gespeichert.Generierte Spalten, die nicht
STOREDsind, können nicht alsNOT NULLmarkiert werden.Direkte Schreibvorgänge in generierte Spalten sind nicht zulässig.
Die Spaltenoption
allow_commit_timestampist für generierte Spalten und für Spalten, auf die von generierten Spalten verwiesen wird, nicht zulässig.Bei
STORED- oder generierten Spalten, die indexiert sind, können Sie den Datentyp der Spalte oder von Spalten, auf die die generierte Spalte verweist, nicht ändern.Sie können eine Spalte nicht löschen, auf die von einer generierten Spalte verwiesen wird.
Sie können eine generierte Spalte als Primärschlüssel verwenden, wenn die folgenden zusätzlichen Einschränkungen gelten:
Der generierte Primärschlüssel kann nicht auf andere generierte Spalten verweisen.
Der generierte Primärschlüssel kann auf höchstens eine Nicht-Schlüsselspalte verweisen.
Der generierte Primärschlüssel darf nicht von einer Nicht-Schlüsselspalte mit einer
DEFAULT-Klausel abhängen.
Für die Verwendung generierter Schlüsselspalten gelten die folgenden Regeln:
- Lese-APIs: Sie müssen die Schlüsselspalten vollständig angeben, einschließlich der generierten Schlüsselspalten.
- Mutation APIs: Für
INSERT,INSERT_OR_UPDATEundREPLACEkönnen Sie in Spanner keine generierten Schlüsselspalten angeben. FürUPDATEkönnen Sie optional generierte Schlüsselspalten angeben. FürDELETEmüssen Sie die Schlüsselspalten vollständig angeben, einschließlich der generierten Schlüssel. - DML: Sie können in
INSERT- oderUPDATE-Anweisungen nicht explizit in generierte Schlüssel schreiben. - Abfrage: Im Allgemeinen empfehlen wir, die generierte Schlüsselspalte als Filter in Ihrer Abfrage zu verwenden. Wenn im Ausdruck für die generierte Schlüsselspalte optional nur eine Spalte als Referenz verwendet wird, kann in der Abfrage eine Gleichheitsbedingung (
=) oder eineIN-Bedingung auf die referenzierte Spalte angewendet werden. Weitere Informationen und ein Beispiel finden Sie unter Einen eindeutigen Schlüssel aus einer Wertspalte erstellen.
Die erzeugte Spalte kann wie jede andere Spalte abgefragt werden, wie im folgenden Beispiel gezeigt.
GoogleSQL
SELECT Id, FullName
FROM Users;
PostgreSQL
SELECT id, fullname
FROM users;
Die Abfrage mit Fullname entspricht der Abfrage mit dem generierten Ausdruck. Eine generierte Spalte kann die Abfrage also vereinfachen.
GoogleSQL
SELECT Id, ARRAY_TO_STRING([FirstName, LastName], " ") as FullName
FROM Users;
PostgreSQL
SELECT id, firstname || ' ' || lastname as fullname
FROM users;
Index für eine generierte Spalte erstellen
Sie können eine generierte Spalte auch indexieren oder als Fremdschlüssel verwenden.
Um Suchvorgänge in unserer generierten FullName-Spalte zu unterstützen, können wir einen sekundären Index erstellen, wie im folgenden Snippet gezeigt.
GoogleSQL
CREATE INDEX UsersByFullName ON Users (FullName);
PostgreSQL
CREATE INDEX UserByFullName ON users (fullname);
Generierte Spalte zu einer vorhandenen Tabelle hinzufügen
Mit der folgenden ALTER TABLE-Anweisung können Sie der Tabelle Users eine generierte Spalte hinzufügen, um die Initialen des Nutzers zu generieren und zu speichern.
GoogleSQL
ALTER TABLE Users ADD COLUMN Initials STRING(2)
AS (ARRAY_TO_STRING([SUBSTR(FirstName, 0, 1), SUBSTR(LastName, 0, 1)], "")) STORED;
PostgreSQL
ALTER TABLE users ADD COLUMN initials VARCHAR(2)
GENERATED ALWAYS AS (SUBSTR(firstname, 0, 1) || SUBSTR(lastname, 0, 1)) STORED;
Wenn Sie einer vorhandenen Tabelle eine gespeicherte generierte Spalte hinzufügen, wird ein Vorgang mit langer Ausführungszeit zum Backfill der Spaltenwerte gestartet. Beim Backfill können die gespeicherten generierten Spalten weder gelesen noch abgefragt werden. Der Backfill-Status wird in der INFORMATION_SCHEMA-Tabelle wiedergegeben.
Teilindex mithilfe einer generierten Spalte erstellen
Was wäre, wenn wir nur Nutzer abfragen möchten, die mindestens 18 Jahre alt sind? Ein vollständiger Scan der Tabelle wäre ineffizient, daher verwenden wir einen Teilindex.
Mit der folgenden Anweisung können Sie eine weitere generierte Spalte hinzufügen, die das Alter des Nutzers zurückgibt, wenn das Alter über 18 Jahre liegt. Andernfalls wird
NULLzurückgegeben.GoogleSQL
ALTER TABLE Users ADD COLUMN AgeAbove18 INT64 AS (IF(Age > 18, Age, NULL));PostgreSQL
ALTER TABLE Users ADD COLUMN AgeAbove18 BIGINT GENERATED ALWAYS AS (nullif( Age , least( 18, Age) )) VIRTUAL;Erstellen Sie für diese neue Spalte einen Index und deaktivieren Sie die Indexierung von
NULL-Werten mit dem SchlüsselwortNULL_FILTEREDin GoogleSQL oder dem PrädikatIS NOT NULLin PostgreSQL. Dieser Teilindex ist kleiner und effizienter als ein normaler Index, da er alle Nutzer von 18 Jahren oder jünger ausschließt.GoogleSQL
CREATE NULL_FILTERED INDEX UsersAbove18ByAge ON Users (AgeAbove18);PostgreSQL
CREATE INDEX UsersAbove18ByAge ON users (AgeAbove18) WHERE AgeAbove18 IS NOT NULL;Führen Sie die folgende Abfrage aus, um
IdundAgealler Nutzer über 18 abzurufen.GoogleSQL
SELECT Id, Age FROM Users@{FORCE_INDEX=UsersAbove18ByAge} WHERE AgeAbove18 IS NOT NULL;PostgreSQL
SELECT Id, Age FROM users /*@ FORCE_INDEX = UsersAbove18ByAge */ WHERE AgeAbove18 IS NOT NULL;Um beispielsweise nach einem anderen Alter zu filtern und alle Nutzer ab 21 Jahren abzurufen, verwenden Sie denselben Index und filtern Sie die generierte Spalte so:
GoogleSQL
SELECT Id, Age FROM Users@{FORCE_INDEX=UsersAbove18ByAge} WHERE AgeAbove18 > 21;PostgreSQL
SELECT Id, Age FROM users /*@ FORCE_INDEX = UsersAbove18ByAge */ WHERE AgeAbove18 > 21;Eine indexierte generierte Spalte kann die Kosten für die Auswertung eines Ausdrucks zur Abfragezeit sparen und das doppelte Speichern der Werte (in der Basistabelle und im Index) im Vergleich zu einer
STORED-generierten Spalte vermeiden.
Generierte Spalte entfernen
Mit der folgenden DDL-Anweisung wird eine generierte Spalte aus der Tabelle Users gelöscht:
GoogleSQL
ALTER TABLE Users DROP COLUMN Initials;
PostgreSQL
ALTER TABLE users DROP COLUMN initials;
Ausdruck einer generierten Spalte ändern
GoogleSQL
ALTER TABLE Users ALTER COLUMN FullName STRING(100)
AS (ARRAY_TO_STRING(ARRAY_TO_STRING([LastName, FirstName ], " ")));
PostgreSQL
ALTER TABLE users ADD COLUMN Initials VARCHAR(2)
GENERATED ALWAYS AS (lastname || ' ' || firstname) VIRTUAL;
Der Ausdruck einer generierten STORED-Spalte oder einer indexierten, nicht gespeicherten generierten Spalte kann nicht aktualisiert werden.
Primärschlüssel für eine generierte Spalte erstellen
In Spanner können Sie eine STORED-generierte Spalte im Primärschlüssel verwenden.
Im folgenden Beispiel wird eine DDL-Anweisung gezeigt, mit der die Tabelle UserInfoLog mit einer generierten Spalte ShardId erstellt wird. Der Wert der Spalte ShardId hängt von einer anderen Spalte ab. Sie wird abgeleitet, indem eine MOD-Funktion auf die Spalte UserId angewendet wird. ShardId wird als Teil des Primärschlüssels deklariert.
GoogleSQL
CREATE TABLE UserInfoLog (
ShardId INT64 NOT NULL
AS (MOD(UserId, 2048)) STORED,
UserId INT64 NOT NULL,
FullName STRING(1024) NOT NULL,
) PRIMARY KEY (ShardId, UserId);
PostgreSQL
CREATE TABLE UserInfoLog (
ShardId BIGINT GENERATED ALWAYS
AS (MOD(UserId, '2048'::BIGINT)) STORED NOT NULL,
UserId BIGINT NOT NULL,
FullName VARCHAR(1024) NOT NULL,
PRIMARY KEY(ShardId, UserId));
Normalerweise müssen Sie alle Schlüsselspalten angeben, um effizient auf eine bestimmte Zeile zuzugreifen. Im vorherigen Beispiel müssten Sie also sowohl eine ShardId als auch eine UserId angeben. Spanner kann den Wert der generierten Primärschlüsselspalte jedoch manchmal ableiten, wenn er von einer einzelnen anderen Spalte abhängt und der Wert der Spalte, von der er abhängt, vollständig bestimmt ist. Dies ist der Fall, wenn die Spalte, auf die von der generierten Primärschlüsselspalte verwiesen wird, eine der folgenden Bedingungen erfüllt:
- Er entspricht einem konstanten Wert oder einem gebundenen Parameter in der
WHERE-Klausel oder - Der Wert wird durch einen
IN-Operator in derWHERE-Klausel festgelegt. - Der Wert wird aus einer Equi-Join-Bedingung abgerufen.
Beispiel:
GoogleSQL
SELECT * FROM UserInfoLog
AS T WHERE T.UserId=1;
PostgreSQL
SELECT * FROM UserInfoLog
AS T WHERE T.UserId=1;
Spanner kann den Wert von ShardId aus dem angegebenen UserId ableiten.
Die vorherige Abfrage entspricht nach der Abfrageoptimierung der folgenden Abfrage:
GoogleSQL
SELECT * FROM UserInfoLog
AS T WHERE T.ShardId = MOD(1, 2048)
AND T.UserId=1;
PostgreSQL
SELECT * FROM UserInfoLog
AS T WHERE T.ShardId = MOD(1, 2048)
AND T.UserId=1;
Im nächsten Beispiel wird gezeigt, wie Sie die Tabelle Students erstellen und einen Ausdruck verwenden, mit dem das Feld id der JSON-Spalte StudentInfo abgerufen und als Primärschlüssel verwendet wird:
GoogleSQL
CREATE TABLE Students (
StudentId INT64 NOT NULL
AS (INT64(StudentInfo.id)) STORED,
StudentInfo JSON NOT NULL,
) PRIMARY KEY (StudentId);
PostgreSQL
CREATE TABLE Students (
StudentId BIGINT GENERATED ALWAYS
AS ((StudentInfo ->> 'id')::BIGINT) STORED NOT NULL,
StudentInfo JSONB NOT NULL,
PRIMARY KEY(StudentId));
Attribute einer generierten Spalte ansehen
INFORMATION_SCHEMA von Spanner enthält Informationen zu den generierten Spalten in Ihrer Datenbank. Im Folgenden finden Sie einige Beispiele für Fragen, die Sie durch Abfragen des Informationsschemas beantworten können.
Welche generierten Spalten werden in meiner Datenbank definiert?
GoogleSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, C.IS_STORED
FROM INFORMATION_SCHEMA.COLUMNS as c
WHERE c.GENERATION_EXPRESSION IS NOT NULL;
PostgreSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, C.IS_STORED
FROM INFORMATION_SCHEMA.COLUMNS as c
WHERE c.GENERATION_EXPRESSION IS NOT NULL;
IS_STORED ist entweder YES für gespeicherte generierte Spalten, NO für nicht gespeicherte generierte Spalten oder NULL für nicht generierte Spalten.
Wie ist der aktuelle Status der generierten Spalten in der Tabelle Users?
Wenn Sie einer vorhandenen Tabelle eine generierte Spalte hinzugefügt haben, möchten Sie möglicherweise SPANNER_STATE in einer Abfrage übergeben, um den aktuellen Status der Spalte zu ermitteln.
SPANNER_STATE gibt die folgenden Werte zurück:
COMMITTED: Die Spalte ist vollständig nutzbar.WRITE_ONLY: Backfill für diese Spalte wird durchgeführt. Es ist kein Lesevorgang erlaubt.
Verwenden Sie die folgende Abfrage, um den Status einer Spalte zu ermitteln:
GoogleSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, c.SPANNER_STATE
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE c.TABLE_NAME="Users" AND c.GENERATION_EXPRESSION IS NOT NULL;
PostgreSQL
SELECT c.TABLE_NAME, c.COLUMN_NAME, c.SPANNER_STATE
FROM INFORMATION_SCHEMA.COLUMNS AS c
WHERE c.TABLE_NAME='users' AND c.GENERATION_EXPRESSION IS NOT NULL;
Hinweis: Auf eine generierte Spalte, die nicht gespeichert ist, kann nur über die SQL-Abfrage zugegriffen werden. Wenn sie jedoch indexiert ist, können Sie mit der Read API auf den Wert aus dem Index zugreifen.
Leistung
Eine von STORED generierte Spalte hat keine Auswirkungen auf die Leistung eines Lese- oder Abfragevorgangs. Nicht gespeicherte generierte Spalten, die in einer Abfrage verwendet werden, können sich jedoch aufgrund des Aufwands für die Auswertung des Ausdrucks für die generierte Spalte auf die Leistung auswirken.
Die Leistung von Schreibvorgängen (DML-Anweisungen und Mutationen) wird beeinträchtigt, wenn entweder eine STORED-generierte Spalte oder eine generierte Spalte mit Index verwendet wird. Der Mehraufwand entsteht durch die Auswertung des generierten Spaltenausdrucks, wenn durch den Schreibvorgang eine der Spalten, auf die im generierten Spaltenausdruck verwiesen wird, eingefügt oder geändert wird. Da der Mehraufwand je nach Schreibarbeitslast für die Anwendung, dem Schemadesign und den Datensatzmerkmalen variiert, empfehlen wir, Ihre Anwendungen zu testen, bevor Sie eine generierte Spalte verwenden.
Nächste Schritte
Weitere Informationen finden Sie unter Informationsschema für GoogleSQL-Dialektdatenbanken und Informationsschema für PostgreSQL-Dialektdatenbanken.
Weitere Informationen zu generierten Spalten finden Sie in den Parameterdetails zu TABELLE ERSTELLEN.