Contenido de esta guía
Respuesta rápida
Una columna generada calcula su valor a partir de una expresión definida en el esquema. El usuario escribe las columnas base y el motor deriva la columna calculada cuando corresponde.
subtotal REAL
GENERATED ALWAYS AS (cantidad * precio_unitario) STOREDNo es un DEFAULT: un valor predeterminado se usa cuando falta un valor en la inserción; una columna generada depende de una expresión y se vuelve a derivar cuando cambian las columnas de las que depende.
Modelo mental: dato base → expresión → dato derivado
Si cantidad = 2 y precio_unitario = 24.90, el subtotal puede derivarse de ambos valores. Guardar también un subtotal editable introduce el riesgo de que deje de coincidir con sus componentes. Una columna generada expresa esa dependencia en el esquema.
Pregunta de diseño: ¿el valor tiene vida propia o siempre debe ser el resultado de otras columnas de la misma fila? Si siempre es derivado y el motor lo permite, una columna generada puede ser una opción.
Ejemplo comprobado en SQLite
CREATE TEMP TABLE lineas_generadas (
id INTEGER PRIMARY KEY,
cantidad INTEGER NOT NULL,
precio_unitario REAL NOT NULL,
subtotal REAL
GENERATED ALWAYS AS (cantidad * precio_unitario) STORED
);
INSERT INTO lineas_generadas (id, cantidad, precio_unitario)
VALUES (1, 2, 24.90),
(2, 3, 49.50);
SELECT id, cantidad, precio_unitario,
ROUND(subtotal, 2) AS subtotal
FROM lineas_generadas
ORDER BY id;| id | cantidad | precio_unitario | subtotal |
|---|---|---|---|
| 1 | 2 | 24,90 | 49,80 |
| 2 | 3 | 49,50 | 148,50 |
El INSERT no proporciona subtotal. La expresión de la columna lo calcula a partir de los datos base.
Si cambia la base, cambia el valor derivado
UPDATE lineas_generadas
SET cantidad = 4
WHERE id = 1;
SELECT id, cantidad, ROUND(subtotal, 2) AS subtotal
FROM lineas_generadas
WHERE id = 1;| id | cantidad | subtotal |
|---|---|---|
| 1 | 4 | 99,60 |
El subtotal deja de ser 49,80 y pasa a 99,60 porque depende de cantidad. No necesitas mantener dos valores manualmente.
Columna generada vs DEFAULT, VIEW y cálculo en SELECT
| Opción | Dónde vive la lógica | Cuándo encaja |
|---|---|---|
| Columna generada | En la definición de la tabla | El valor debe derivarse siempre de la fila |
DEFAULT | En la tabla, solo al faltar el valor | Necesitas un valor inicial, no una fórmula permanente |
VIEW | En una consulta reutilizable | El cálculo pertenece a una vista lógica más amplia |
Expresión en SELECT | En una consulta concreta | El cálculo solo se necesita en ese resultado |
La sintaxis no es idéntica entre motores
PostgreSQL, MySQL y SQLite usan el concepto de generated column y sintaxis basada en una expresión AS (...), con opciones de almacenamiento que dependen del motor. SQL Server denomina la característica computed column y permite PERSISTED para almacenar físicamente ciertos resultados calculados.
| Motor | Nombre / forma habitual |
|---|---|
| PostgreSQL | GENERATED ALWAYS AS (...) STORED | VIRTUAL |
| MySQL | columna generada AS (...), virtual o almacenada |
| SQLite | GENERATED ALWAYS AS (...), VIRTUAL o STORED |
| SQL Server | columna AS expresion, opcionalmente PERSISTED |
Las expresiones permitidas, indexación, nulabilidad, restricciones y operaciones de ALTER TABLE no coinciden exactamente. Diseña con la documentación del motor objetivo.
Errores frecuentes
Intentar insertar manualmente el valor derivado
La finalidad de la columna es que el motor determine el resultado a partir de su expresión. Inserta o modifica las columnas base, no mantengas una segunda copia manual.
Usar una columna generada para lógica que necesita otras filas
Estas expresiones se diseñan alrededor de los valores de la propia fila y están sujetas a restricciones del motor. Para agregados, subconsultas o lógica más amplia quizá necesites una consulta, vista u otra estrategia.
Asumir que STORED, VIRTUAL y PERSISTED significan exactamente lo mismo
Describen decisiones relacionadas con cálculo y almacenamiento, pero pertenecen a implementaciones distintas. No copies DDL de un motor a otro sin revisar sintaxis y limitaciones.
Práctica guiada
Calcula el área de cada rectángulo
Crea una tabla temporal rectangulos con ancho, alto y una columna generada almacenada area calculada como ancho * alto. Inserta (3, 4) y (2.5, 8).
Criterio de éxito: las áreas son 12 y 20.
ancho y alto.(ancho * alto).STORED.Solución razonada
CREATE TEMP TABLE rectangulos (
id INTEGER PRIMARY KEY,
ancho REAL NOT NULL,
alto REAL NOT NULL,
area REAL GENERATED ALWAYS AS (ancho * alto) STORED
);
INSERT INTO rectangulos (id, ancho, alto)
VALUES (1, 3, 4), (2, 2.5, 8);
SELECT id, area
FROM rectangulos
ORDER BY id;| id | area |
|---|---|
| 1 | 12 |
| 2 | 20 |
Compatibilidad
La característica existe en los motores principales, pero con diferencias relevantes. PostgreSQL documenta columnas generadas almacenadas y virtuales; MySQL distingue generadas virtuales y almacenadas; SQLite también ofrece VIRTUAL/STORED; SQL Server utiliza columnas calculadas y la opción PERSISTED. Comprueba restricciones e indexación antes de migrar DDL.
Qué debes recordar
- Una columna generada expresa una dependencia entre columnas de la misma fila.
- No es equivalente a
DEFAULT. - El motor recalcula el valor cuando cambian las columnas de las que depende.
- La sintaxis y las restricciones cambian entre PostgreSQL, MySQL, SQLite y SQL Server.
- Úsala cuando el valor sea realmente derivado; no para duplicar datos sin necesidad.