VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
Syntax
VIRTUAL vs STORED
| VIRTUAL | STORED | |
|---|---|---|
| Storage | No disk storage — computed on read | Written to disk on INSERT/UPDATE |
| Computation timing | Every SELECT | Every INSERT/UPDATE |
| Disk space | Saves space | Uses space like a regular column |
| Can be indexed | Yes (index stores the computed values) | Yes |
| Can be a foreign key target | No | No |
| Default | Yes — VIRTUAL is the default if omitted | Opt-in |
STORED when the computation is expensive and you read the column far more than you write it. Use VIRTUAL (the default) when storage is a concern or writes happen frequently.
Expressions
Generated column expressions can reference other columns in the same row. They cannot reference other generated columns or call non-deterministic functions.NOW(), RAND(), UUID(), subqueries, stored functions.
Extracting fields from JSON is a common use case:
Indexing Generated Columns
You can add an index on a generated column. ForVIRTUAL columns, MySQL materializes the computed value into the index.
INDEX (LOWER(email))), which internally creates a hidden generated column:
Inserting and Updating
You cannot assign a value to a generated column in anINSERT or UPDATE. The column value is always derived from the expression. You can write DEFAULT explicitly, but that’s all:
ALTER TABLE
Add a generated column to an existing table:Frequently Asked Questions
Can I use a generated column in a WHERE clause?
Yes. ForVIRTUAL columns without an index, MySQL computes the expression during the scan. For STORED columns or indexed VIRTUAL columns, the value is already materialized. Use EXPLAIN to verify whether the index is being used.
Does a generated column update automatically when the source columns change?
Yes. MySQL recomputesVIRTUAL generated columns on every read. STORED generated columns are recomputed and written on every INSERT or UPDATE that affects the columns in the expression.
Can I use a generated column as a partition key?
Yes, forSTORED generated columns. VIRTUAL columns cannot be used as partition keys.
Troubleshooting
| Problem | Solution |
|---|---|
ERROR 3105: The value specified for generated column is not allowed | You assigned an explicit value to a generated column in INSERT/UPDATE — omit it or use DEFAULT |
ERROR 3106: 'Non-deterministic functions' is not allowed | The expression uses NOW(), RAND(), UUID(), or a subquery — replace with deterministic alternatives |
| Generated column not using index | Check EXPLAIN — the WHERE clause expression must exactly match the generated column’s expression; WHERE LOWER(email) uses a LOWER(email) generated column index, but WHERE LOWER(TRIM(email)) does not |
STORED column slowing down writes | Every INSERT/UPDATE recomputes and stores the expression — switch to VIRTUAL if read performance isn’t the priority |
ALTER TABLE adding generated column is slow | MySQL rebuilds the table for STORED generated columns; VIRTUAL column additions are much faster (metadata-only in many cases) |

