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. In diesem Artikel wird beschrieben, wie Sie diesen Spaltentyp in Ihrer Datenbank verwalten.
Generierte Spalte zu einer neuen Tabelle hinzufügen
Im folgenden CREATE TABLE
-Snippet erstellen wir eine Tabelle, in der Informationen zu Nutzern gespeichert werden. Wir haben Spalten für FirstName
und LastName
und definieren eine generierte Spalte für FullName
, die die Verkettung von FirstName
und LastName
ist.
GoogleSQL
CREATE TABLE Users (
Id STRING(20) NOT NULL,
FirstName STRING(50),
LastName STRING(50),
Age INT64 NOT NULL,
FullName STRING(100) AS (ARRAY_TO_STRING([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)
);
Der Wert von FullName
wird berechnet, wenn eine neue Zeile eingefügt wird oder wenn FirstName
und/oder LastName
für eine vorhandene Zeile aktualisiert werden. Der berechnete Wert wird zusammen mit den anderen Spalten aus der Tabelle gespeichert. Der SQL-Code in Klammern wird als Generierungsausdruck bezeichnet.
expression
kann ein beliebiger gültiger SQL-Ausdruck sein, der dem Spaltendatentyp mit den folgenden Einschränkungen zugewiesen werden kann.Der Ausdruck kann nur auf Spalten in derselben Tabelle verweisen.
Der Ausdruck darf keine Unterabfragen enthalten.
Der Ausdruck darf keine nicht deterministischen Funktionen wie
PENDING_COMMIT_TIMESTAMP()
,CURRENT_DATE()
undCURRENT_TIMESTAMP()
enthalten.Sie können den Ausdruck einer generierten Spalte nicht ändern.
Das Attribut
STORED
, das auf den Ausdruck folgt, speichert das Ergebnis des Ausdrucks zusammen mit anderen Spalten der Tabelle. Nachfolgende Aktualisierungen einer der referenzierten Spalten führen dazu, dass Spanner den Ausdruck neu bewertet und speichert.Spanner lässt keine generierten Spalten zu, es sei denn, sie verwenden das Attribut
STORED
.Direkte Schreibvorgänge in generierte Spalten sind nicht zulässig.
Die Spaltenoption
allow_commit_timestamp
ist in generierten Spalten oder Spalten, auf die generierte Spalten verweisen, nicht zulässig.Sie können den Datentyp einer generierten Spalte oder von Spalten, auf die die generierte Spalte verweist, nicht ändern.
Sie können keine Spalte löschen, auf die eine generierte Spalte verweist.
Sie können eine generierte Spalte mit den folgenden zusätzlichen Einschränkungen als Primärschlüssel verwenden:
Der generierte Primärschlüssel kann nicht auf andere generierte Spalten verweisen.
Der generierte Primärschlüssel kann höchstens auf eine Nicht-Schlüsselspalte verweisen.
Der generierte Primärschlüssel darf nicht von einer Nicht-Schlüsselspalte mit einer
DEFAULT
-Klausel abhängen.
Bei der Verwendung von generierten 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_UPDATE
undREPLACE
können Sie mit Spanner keine generierten Schlüsselspalten angeben. FürUPDATE
können Sie optional generierte Schlüsselspalten angeben. FürDELETE
mü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 empfiehlt es sich, die generierte Schlüsselspalte als Filter in Ihrer Abfrage zu verwenden. Wenn der Ausdruck für die generierte Schlüsselspalte nur eine Spalte als Verweis verwendet, kann die Abfrage eine Gleichheitsbedingung (
=
) oder eineIN
-Bedingung auf die referenzierte Spalte anwenden. Weitere Informationen und ein Beispiel finden Sie unter 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;
Dies entspricht der folgenden Anweisung, in der die gespeicherte generierte Spalte nicht verwendet wird.
GoogleSQL
SELECT Id, ARRAY_TO_STRING([FirstName, LastName], " ") as FullName
FROM Users;
PostgreSQL
SELECT id, firstname || ' ' || lastname as fullname
FROM users;
Neben dem Erstellen der Abfrage und der Kosteneinsparung des Ausdrucks bei der Abfrage kann eine generierte Spalte auch indexiert oder als Fremdschlüssel verwendet werden.
Index für eine generierte Spalte erstellen
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 fügen wir der Tabelle Users
eine generierte Spalte hinzu, 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;
Das Hinzufügen einer gespeicherten generierten Spalte zu einer vorhandenen Tabelle ist ein lang andauernder Vorgang zum Backfill der Spaltenwerte. Während des Backfills können die gespeicherten generierten Spalten nicht gelesen oder abgefragt werden. Der Backfill-Status spiegelt sich im INFORMATION_SCHIEDS wider.
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.
Verwenden Sie die folgende Anweisung, um eine weitere generierte Spalte hinzuzufügen, die das Alter des Nutzers zurückgibt, wenn er über 18 Jahre alt ist. Andernfalls wird
NULL
zurückgegeben.GoogleSQL
ALTER TABLE Users ADD COLUMN AgeAbove18 INT64 AS (IF(Age > 18, Age, NULL)) STORED;
PostgreSQL
ALTER TABLE Users ADD COLUMN AgeAbove18 BIGINT GENERATED ALWAYS AS (nullif( Age , least( 18, Age) )) STORED;
Erstellen Sie einen Index für diese neue Spalte und deaktivieren Sie die Indexierung von
NULL
-Werten mit dem SchlüsselwortNULL_FILTERED
in GoogleSQL oder dem PrädikatIS NOT NULL
in PostgreSQL. Dieser Teilindex ist kleiner und effizienter als ein normaler Index, da er alle Personen unter 18 Jahren 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
Id
undAge
aller 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;
Wenn Sie nach einem anderen Alter filtern möchten, um beispielsweise alle Nutzer abzurufen, die über 21 Jahre alt sind, verwenden Sie denselben Index und Filter für die generierte Spalte. Gehen Sie dazu so vor:
GoogleSQL
SELECT Id, Age FROM Users@{FORCE_INDEX=UsersAbove18ByAge} WHERE AgeAbove18 > 21;
PostgreSQL
SELECT Id, Age FROM users /*@ FORCE_INDEX = UsersAbove18ByAge */ WHERE AgeAbove18 > 21;
Generierte Spalte entfernen
Mit der folgenden DDL-Anweisung wird eine generierte Spalte aus der Tabelle Users
gelöscht.
ALTER TABLE Users
DROP COLUMN Initials;
Generierten Spaltenausdruck ändern
Der Ausdruck einer generierten Spalte kann nicht geändert werden. Stattdessen müssen Sie die vorhandene Spalte löschen und eine neue generierte Spalte mit dem neuen Ausdruck erstellen.
Primärschlüssel für eine generierte Spalte erstellen
In Spanner können Sie eine generierte Spalte im Primärschlüssel verwenden.
Das folgende Beispiel zeigt eine DDL-Anweisung, mit der die Tabelle UserInfoLog
mit einer generierten ShardId
-Spalte erstellt wird. Der Wert der Spalte ShardId
hängt von einer anderen Spalte ab. Es wird mithilfe einer MOD
-Funktion in der Spalte UserId
abgeleitet. ShardId
ist 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 zugreifen zu können. Im vorherigen Beispiel würde dies bedeuten, dass sowohl ShardId
als auch UserId
angegeben werden. Spanner kann jedoch manchmal den Wert der generierten Primärschlüsselspalte ableiten, wenn dieser von einer einzelnen anderen Spalte abhängt und der Wert der Spalte, von der es abhängig ist, vollständig bestimmt ist. Dies gilt, wenn die Spalte, auf die in der generierten Primärschlüsselspalte verwiesen wird, eine der folgenden Bedingungen erfüllt:
- Es ist gleich einem konstanten Wert oder gebundenen Parameter in der
WHERE
-Klausel oder - Der Wert wird von einem
IN
-Operator in derWHERE
-Klausel festgelegt. - Der Wert bezieht sich auf eine Equi-Join-Bedingung.
Für die folgende Abfrage beispielsweise:
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 der folgenden Abfrage nach der Abfrageoptimierung:
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;
Das nächste Beispiel zeigt, wie Sie die Tabelle Students
erstellen und einen Ausdruck verwenden, der das Feld id
der JSON-Spalte StudentInfo
abruft und als Primärschlüssel verwendet:
GoogleSQL
CREATE TABLE Students (
StudentId INT64 NOT NULL
AS (CAST(JSON_VALUE(StudentInfo, "$.id") AS INT64)) STORED,
StudentInfo JSON NOT NULL,
) PRIMARY KEY (StudentId);
PostgreSQL
CREATE TABLE Students (
StudentId BIGINT GENERATED ALWAYS
AS (((StudentInfo ->> 'id'::TEXT))::BIGINT) STORED NOT NULL,
StudentInfo JSONB NOT NULL,
PRIMARY KEY(StudentId));
Attribute einer generierten Spalte ansehen
Das 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?
SELECT c.TABLE_NAME, c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS as c
WHERE c.GENERATION_EXPRESSION IS NOT NULL;
Wie ist der aktuelle Status der generierten Spalten in der Tabelle Users
?
Wenn Sie einer vorhandenen Tabelle eine generierte Spalte hinzugefügt haben, können Sie 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: Eine generierte Spalte wirkt sich nicht auf die Leistung eines Lese- oder Abfragevorgangs aus. Sie kann jedoch die Leistung von Schreibvorgängen (DML-Anweisungen und -Mutationen) beeinträchtigen, da der Spaltenausdruck einer generierten Spalte nicht richtig ausgewertet werden muss, wenn durch einen Schreibvorgang eine der Spalten geändert wird, auf die im generierten Spaltenausdruck verwiesen wird. Da der Aufwand je nach Schreibarbeitslast für die Anwendung, Schemadesign und Dataset-Eigenschaften variiert, empfehlen wir Ihnen, ein Benchmarking Ihrer Anwendungen durchzuführen, bevor Sie eine generierte Spalte verwenden.
Nächste Schritte
Weitere Informationen zum Informationsschema von Spanner.
Weitere Informationen zu generierten Spalten finden Sie in den Details zum Parameter CREATE TABLE.