Una columna generada es una columna que siempre se calcula a partir de otras columnas de una fila. Estas columnas pueden simplificar una consulta, ahorrar el costo de evaluar una expresión en el momento de la consulta y se pueden indexar o usar como clave externa. En esta página, se describe cómo administrar este tipo de columna en tu base de datos para bases de datos con dialecto de GoogleSQL y bases de datos con dialecto de PostgreSQL.
Agrega una columna generada a una tabla nueva
En el siguiente fragmento de CREATE TABLE, creamos una tabla para almacenar información sobre los usuarios. Tenemos columnas para FirstName y LastName, y definimos una columna generada para FullName, que es la concatenación de FirstName y LastName. El SQL entre paréntesis se denomina expresión de generación.
Una columna generada se puede marcar como STORED para ahorrar el costo de evaluar la expresión en el momento de la consulta. Como resultado, el valor de FullName solo se calcula cuando se inserta una fila nueva o cuando se actualiza FirstName o LastName para una fila existente. El valor calculado se almacena junto con otras columnas de la tabla.
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)
);
Puedes crear una columna generada no almacenada si omites el atributo STORED en el DDL. Este tipo de columna generada se evalúa en el momento de la consulta y puede simplificar una consulta. En PostgreSQL, puedes crear una columna generada no almacenada con el atributo VIRTUAL.
GoogleSQL
FullName STRING(MAX) AS (CONCAT(FirstName, " ", LastName))
PostgreSQL
fullname text GENERATED ALWAYS AS (firstname || ' ' || lastname) VIRTUAL
expressionpuede ser cualquier expresión de SQL válida que se pueda asignar al tipo de datos con columnas mediante las siguientes restricciones.La expresión solo puede hacer referencia a columnas en la misma tabla.
La expresión solo puede usar funciones inmutables. Una función inmutable es aquella que muestra los mismos resultados cada vez que se llama con los mismos valores de argumentos. Para obtener más información, consulta Volatilidad de la función.
La expresión debe ser escalar, lo que significa que muestra un solo valor.
La expresión no puede contener subconsultas.
No puedes modificar la expresión de una columna generada
STOREDo indexada.
Para las bases de datos con dialecto de GoogleSQL, una columna generada no almacenada de tipo
STRINGoBYTESdebe tener una longitud deMAX.Para las bases de datos con dialecto de PostgreSQL, una columna generada no almacenada o virtual de tipo
VARCHARdebe tener una longitud deMAX.El atributo
STOREDque sigue a la expresión almacena el resultado de la expresión junto con otras columnas de la tabla. Las actualizaciones posteriores en cualquiera de las columnas referenciadas hacen que Spanner vuelva a evaluar y almacenar la expresión.Las columnas generadas que no son
STOREDno se pueden marcar comoNOT NULL.No se permite la escritura directa en columnas generadas.
La opción de columna
allow_commit_timestampno está permitida en las columnas generadas ni en ninguna columna a la que se haga referencia en las columnas generadas.Para las columnas
STOREDo generadas que están indexadas, no puedes cambiar el tipo de datos de la columna ni de ninguna columna a la que haga referencia la columna generada.No puedes descartar una columna a la que hace referencia una columna generada.
Puedes usar una columna generada como clave primaria con las siguientes restricciones adicionales:
La clave primaria generada no puede hacer referencia a otras columnas generadas.
La clave primaria generada puede hacer referencia, como máximo, a una columna sin clave.
La clave primaria generada no puede depender de una columna sin clave con una cláusula
DEFAULT.
Las siguientes reglas se aplican cuando se usan columnas de claves generadas:
- APIs de lectura: Debes especificar por completo las columnas de claves, incluidas las columnas de claves generadas.
- APIs de mutación: Para
INSERT,INSERT_OR_UPDATEyREPLACE, Spanner no te permite especificar columnas de claves generadas. ParaUPDATE, puedes especificar de forma opcional columnas de claves generadas. ParaDELETE, debes especificar por completo las columnas de claves, incluidas las claves generadas. - DML: No puedes escribir de forma explícita en claves generadas en instrucciones
INSERToUPDATE. - Consulta: En general, te recomendamos que uses la columna de claves generada como filtro en tu consulta. De manera opcional, si la expresión para la columna de claves generada
usa solo una columna como referencia, la consulta puede aplicar una condición de igualdad
(
=) oINa la columna referenciada. Para obtener más información y un ejemplo, consulta Crea una clave única derivada de una columna de valor.
Se puede consultar la columna generada como cualquier otra columna, como se muestra en el siguiente ejemplo.
GoogleSQL
SELECT Id, FullName
FROM Users;
PostgreSQL
SELECT id, fullname
FROM users;
La consulta que usa Fullname es equivalente a la consulta con la expresión generada. Por lo tanto, una columna generada puede simplificar la consulta.
GoogleSQL
SELECT Id, ARRAY_TO_STRING([FirstName, LastName], " ") as FullName
FROM Users;
PostgreSQL
SELECT id, firstname || ' ' || lastname as fullname
FROM users;
Crea un índice en una columna generada
También puedes indexar o usar una columna generada como clave externa.
Para ayudar con las búsquedas en nuestra columna generada FullName, podemos crear un índice secundario como se muestra en el siguiente fragmento.
GoogleSQL
CREATE INDEX UsersByFullName ON Users (FullName);
PostgreSQL
CREATE INDEX UserByFullName ON users (fullname);
Crea un índice en una expresión escalar
También puedes crear un índice secundario en una expresión escalar sin crear una columna generada correspondiente en tu tabla.
En lugar de agregar una columna generada VenueCity a la tabla y, luego, crear un índice en la columna, puedes crear un Expression Index.
GoogleSQL
CREATE TABLE Venues (
Id INT64 NOT NULL,
VenueData JSON
) PRIMARY KEY (Id);
CREATE INDEX VenuesByCity ON Venues((JSON_VALUE(VenueData.address.city)));
PostgreSQL
CREATE TABLE Venues (
Id BIGINT NOT NULL PRIMARY KEY,
VenueData JSONB
);
CREATE INDEX VenuesByCity ON Venues((VenueData -> 'address' ->> 'city'));
Agrega una columna generada a una tabla existente
Con la siguiente instrucción ALTER TABLE, podemos agregar una columna generada a la tabla Users para generar y almacenar las iniciales del usuario.
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;
Si agregas una columna generada almacenada a una tabla existente, se inicia una operación de larga duración para reabastecer los valores de la columna. Durante el reabastecimiento, no se pueden leer ni consultar las columnas generadas almacenadas. El estado de reabastecimiento se refleja en la tabla INFORMATION_SCHEMA.
Crea un índice parcial con una columna generada
¿Qué sucede si solo queremos consultar a los usuarios mayores de 18 años? Un análisis completo de la tabla sería ineficiente, por lo que usamos un índice parcial.
Usa la siguiente instrucción para agregar otra columna generada que muestre la edad del usuario si es mayor de 18 años y muestre
NULLen caso contrario.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;Crea un índice en esta columna nueva y inhabilita la indexación de
NULLvalores con laNULL_FILTEREDpalabra clave en GoogleSQL o elIS NOT NULLpredicado en PostgreSQL. Este índice parcial es más pequeño y eficiente que un índice normal porque excluye a todos los que tienen 18 años o menos.GoogleSQL
CREATE NULL_FILTERED INDEX UsersAbove18ByAge ON Users (AgeAbove18);PostgreSQL
CREATE INDEX UsersAbove18ByAge ON users (AgeAbove18) WHERE AgeAbove18 IS NOT NULL;Para recuperar el
Idy laAgede todos los usuarios mayores de 18 años, ejecuta la siguiente consulta.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;Para filtrar por una edad diferente, por ejemplo, para recuperar a todos los usuarios mayores de 21 años, usa el mismo índice y filtra en la columna generada de la siguiente manera:
GoogleSQL
SELECT Id, Age FROM Users@{FORCE_INDEX=UsersAbove18ByAge} WHERE AgeAbove18 > 21;PostgreSQL
SELECT Id, Age FROM users /*@ FORCE_INDEX = UsersAbove18ByAge */ WHERE AgeAbove18 > 21;Una columna generada indexada puede ahorrar el costo de evaluar una expresión en el momento de la consulta y evitar almacenar los valores dos veces (en la tabla base y el índice) en comparación con una columna generada
STORED.
Quita una columna generada
La siguiente instrucción DDL descarta una columna generada de la tabla Users:
GoogleSQL
ALTER TABLE Users DROP COLUMN Initials;
PostgreSQL
ALTER TABLE users DROP COLUMN initials;
Modifica una expresión de columna generada
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;
No se permite actualizar la expresión de una columna generada STORED ni de una columna generada no almacenada indexada.
Crea una clave primaria en una columna generada
En Spanner, puedes usar una columna generada STORED en la clave primaria.
En el siguiente ejemplo, se muestra una instrucción DDL que crea la tabla UserInfoLog con una columna generada ShardId. El valor de la columna ShardId depende de otra columna. Se deriva mediante el uso de una función MOD en la columna UserId. ShardId se declara como parte de la clave primaria.
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));
Por lo general, para acceder de manera eficiente a una fila específica, debes especificar todas las columnas de claves. En el ejemplo anterior, esto significaría proporcionar un ShardId y un UserId. Sin embargo, Spanner a veces puede inferir el valor de la columna de clave primaria generada si depende de otra columna única y si el valor de la columna de la que depende está completamente determinado. Esto es verdadero si la columna a la que hace referencia la columna de clave primaria generada cumple con una de las siguientes condiciones:
- Es igual a un valor constante o un parámetro vinculado en la cláusula
WHERE. - Obtiene su valor establecido por un operador
INen la cláusulaWHERE. - Obtiene su valor de una condición de unión de igualdad.
Por ejemplo, en la siguiente consulta:
GoogleSQL
SELECT * FROM UserInfoLog
AS T WHERE T.UserId=1;
PostgreSQL
SELECT * FROM UserInfoLog
AS T WHERE T.UserId=1;
Spanner puede inferir el valor de ShardId del UserId proporcionado.
La consulta anterior es equivalente a la siguiente consulta después de la optimización de la consulta:
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;
En el siguiente ejemplo, se muestra cómo crear la tabla Students y usar una expresión que recupera el campo id de la columna JSON StudentInfo y lo usa como clave primaria:
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));
Visualiza las propiedades de una columna generada
INFORMATION_SCHEMA de Spanner contiene información sobre las columnas generadas en tu base de datos. Estos son algunos ejemplos de las preguntas que puedes responder cuando consultas el esquema de información.
¿Qué columnas generadas se definen en mi base de datos?
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 es YES para las columnas generadas almacenadas, NO para las columnas generadas no almacenadas o NULL para las columnas no generadas.
¿Cuál es el estado actual de las columnas generadas en la tabla Users?
Si agregaste una columna generada a una tabla existente, es posible que desees pasar SPANNER_STATE en una consulta para averiguar el estado actual de la columna.
SPANNER_STATE muestra los siguientes valores:
COMMITTED: La columna es completamente utilizable.WRITE_ONLY: La columna se está reabasteciendo. No se permite la lectura.
Usa la siguiente consulta para encontrar el estado de una columna:
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;
Nota: Solo se puede acceder a una columna generada no almacenada con la consulta en SQL. Sin embargo, si está indexada, puedes usar la API de lectura para acceder al valor del índice.
Rendimiento
Una columna generada STORED no afecta el rendimiento de una operación de lectura o consulta. Sin embargo, las columnas generadas no almacenadas que se usan en una consulta pueden afectar su rendimiento debido a la sobrecarga de evaluar la expresión de columna generada.
El rendimiento de las operaciones de escritura (instrucciones DML y mutaciones) se ve afectado cuando se usa una columna generada STORED o una columna generada indexada. La sobrecarga se debe a la evaluación de la expresión de columna generada cuando la operación de escritura inserta o modifica cualquiera de las columnas a las que se hace referencia en la expresión de columna generada. Dado que la sobrecarga varía según la carga de trabajo de escritura para la aplicación, el diseño del esquema y las características del conjunto de datos, te recomendamos que realices pruebas comparativas de tus aplicaciones antes de usar una columna generada.
¿Qué sigue?
Obtén más información sobre el esquema de información de Spanner para bases de datos con dialecto de GoogleSQL y el esquema de información para bases de datos con dialecto de PostgreSQL.
Obtén más detalles sobre las columnas generadas en los detalles del parámetro CREATE TABLE.