Files
lancedb/docs/web/feature-engineering/add-computed-column.mdx
T
Jack YeandClaude Sonnet 5 de95ed3f6d docs: overhaul curation, feature engineering, security and deployment guides (#4748)
A broad documentation pass across the areas LanceDB Enterprise and
Embedded
diverge on most:

**Data Curation** -- GPU Function execution, Slurm scheduling (and what
Slurm is, for readers new to it), blob late materialization as its own
page, joins, aggregation/profiling, sampling, near-duplicate detection,
chunking, distributed shuffle, and IVF-partition clustering.

**Feature Engineering** -- computed columns and materialized views,
restructured to lead with the Function-based path and show the SQL form
side by side with Python throughout; a single Auto Refresh page instead
of
duplicating scheduling details on every refresh page; adaptive
execution;
checkpointing; GPU and Slurm placement for refresh workers; a Training
Cache page for the PyTorch data loader's automatic cache benefit.

**Security & Governance** -- an Audit Log reference (schema, access,
grants), Secrets promoted to an ordinary catalog object with their own
SQL
DDL and a vending/rotation guide, and privilege-table fixes verified
against the authorization source.

**Deployment & Maintenance** -- Horizontal Scaling and OpenTelemetry
moved
into a real Deployment area; Automated Cleanup folded into Intelligent
Auto-Maintenance (every other maintenance decision already lived there);
RTE Compaction and Advanced Conflict Resolution documented in depth.

**Landing page** -- the Enterprise feature comparison table reorganized
(several redundant or stale rows removed, several new areas added), and
the
"why Enterprise" bullets replaced with ones tied to a concrete page
(Performance, Scale, Fault tolerance, Multicloud deployment, Simplify
operations) instead of vague promises.

Plus a full pass addressing lancedb-gatekeeper's review: a real
test-safety
bug (a quickstart test could silently overwrite and delete a
deployment's
own `frames` table), a fabricated SQL statement with no backing
implementation, an actual dedup-guarantee bug in `take_row_ids`, and
dozens
of documentation-accuracy fixes verified against source rather than
taken
on faith -- each either fixed, or left with an explicit note on why it
needs a feature or deployment owner's sign-off instead of being guessed
at.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_018PXMCbRH6i5w47TsjeEUMT

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-03 16:17:24 -08:00

226 lines
8.3 KiB
Plaintext

---
title: "Create a computed column"
sidebarTitle: "Create Computed Column"
description: "Declare a column from a SQL expression. Nothing is evaluated until you refresh it."
icon: table-columns
keywords: ["computed columns", "add_columns", "transforms", "schema"]
availability:
embedded: unavailable
enterprise: available
---
import {
PySchemaAddSetup,
} from '/snippets/tables.mdx';
Lance can write a new column without rewriting the columns already in the
table. That matters most on a table that also holds large images, video or
embeddings, where rewriting everything just to add one small derived field
would be wasteful. See [Lance format](/basics/lance) for the storage model
this relies on.
A computed column is how LanceDB formalizes that capability for feature
engineering specifically: instead of writing the derived values immediately,
it is declared with an expression and starts with no values. Declaring it is
cheap on any table size, because nothing is evaluated until
[a refresh](/feature-engineering/refresh-column) asks for it.
<Note>
`computed` requires LanceDB Enterprise. Ordinary eager `add_columns`
transforms, without `computed`, remain available in LanceDB Embedded.
</Note>
## Fill a column with a Function {#functions}
<Badge color="red">Enterprise</Badge>
When the value cannot be expressed in SQL -- an embedding, a model call, an
image decode -- the column is filled by a registered
[function](/management/function) instead of a SQL expression. It needs a
deployment with Functions enabled.
Connect with an [Enterprise connection](/basics/catalog#enterprise).
Continuing the [`caption` Function](/curation/create-function#chain)
registered in Create a Function, against a `frames` table that already has
a `jpeg` column:
<CodeGroup>
```python Python icon="python"
version = db.create_function_async(caption).wait()
table.add_columns({"caption": version(jpeg=col("jpeg"))})
```
```sql SQL icon="database"
ALTER TABLE frames ADD COLUMN caption AS caption(jpeg) WITH (version = 'fv_1')
```
</CodeGroup>
A Function application is called with its parameters bound to source
columns through `col(...)`, keyed by the output column name. It commits a
new, empty table version the same way a SQL expression does, so the usual
[versioning](/management/table#versioning) applies.
Both a Function application and a SQL expression accept the same
`auto_refresh_enabled` and `auto_refresh_interval_seconds` options, and both
refresh and toggle the same way; see [Auto Refresh](/enterprise/auto-refresh).
## Fill a column with a SQL expression {#expressions}
A value that SQL can compute doesn't need a Function. Declare it with
`computed` instead, as an expression evaluated over the row:
<CodeGroup>
```python Python icon="python"
table = db.create_table("computed_demo", [{"x": 1}, {"x": 2}])
table.add_columns(computed={"doubled": "x * 2"})
# AddColumnsResult(version=2)
```
```sql SQL icon="database"
ALTER TABLE computed_demo ADD COLUMN doubled AS x * 2
```
</CodeGroup>
That commits a new table version with the column present and empty. It is an
ordinary table version, so the usual [versioning](/management/table#versioning) applies,
including that you can travel back to before the column existed.
The expression can reference any column in the table, including another
computed column -- declaration order is dependency order, so it may
reference one declared earlier in the same mapping, but not one declared
after it:
<CodeGroup>
```python Python icon="python"
table.add_columns(computed={
"power_score": "cast(x as double) / 2.0",
"is_strong": "power_score > 0.5",
})
```
```sql SQL icon="database"
ALTER TABLE computed_demo ADD COLUMN power_score AS cast(x as double) / 2.0
ALTER TABLE computed_demo ADD COLUMN is_strong AS power_score > 0.5
```
</CodeGroup>
The functions available are the ones the
[SQL dialect](/sql/dql/functions) documents.
A literal in the expression has to fit the column it meets: `x / 2.0` is
refused when `x` is an integer column, so write `cast(x as double) / 2.0`.
<Warning>
Keep the expression to top-level columns. A column refresh can fail on an
expression that reads a field inside a struct, such as
`stats.magic + stats.strength`. Try a new expression on a small table first.
</Warning>
## Compute now or on refresh {#computed-vs-transforms}
`add_columns` takes either, and the difference matters:
- **SQL expressions in `transforms`** are evaluated *now*, for every row, as
part of the call. Good for a small table, or a column you want populated
immediately. A `FunctionApplication` passed through `transforms` instead
declares empty Function-bound columns, which a refresh fills; that needs a
deployment with Functions enabled, and the embedded library refuses it.
- **`computed`** stores the expression and evaluates nothing. The column is
committed empty and filled later by
[`refresh_column`](/feature-engineering/refresh-column).
`computed` and `transforms` cannot be combined in one call. Reach for `computed` when the table is
large enough that you would rather control when the work happens.
Automatic refresh is on by default for each computed column, and takes effect
when your Enterprise deployment enables its refresh scheduler. Until then,
call [`refresh_column`](/feature-engineering/refresh-column) yourself. Pass
`auto_refresh_enabled=False` to opt a column out, or
`auto_refresh_interval_seconds` to set how often it refreshes:
<CodeGroup>
```python Python icon="python"
table.add_columns(
computed={"doubled": "x * 2"},
auto_refresh_interval_seconds=300,
)
```
```sql SQL icon="database"
ALTER TABLE computed_demo ADD COLUMN doubled AS x * 2
WITH (auto_refresh_interval_seconds = 300)
```
</CodeGroup>
See [Auto Refresh](/enterprise/auto-refresh) for the
full behavior, including how to change the setting later.
An eager `transforms` call looks like this, starting from a small product
table:
<CodeGroup>
<CodeBlock filename="Python" language="Python" icon="python">
{PySchemaAddSetup}
</CodeBlock>
</CodeGroup>
<CodeGroup>
```python Python icon="python"
# Add a discounted price column (10% discount)
table.add_columns({"discounted_price": "cast((price * 0.9) as float)"})
```
```sql SQL icon="database"
ALTER TABLE schema_evolution_add_example ADD COLUMN discounted_price
AS cast((price * 0.9) as float)
```
</CodeGroup>
SQL's `ADD COLUMN ... AS` always declares the column empty and defers the
expression to a refresh, the same as `computed` does -- there is no SQL
form of eager `transforms`. The Python call above is the only way to get
discounted_price filled immediately; declaring it in SQL fills it only
once [`REFRESH COLUMN`](/sql/dml/refresh-column) runs.
For feature engineering on large existing tables, group related derived
features into one `add_columns` call instead of running many separate
writes: this creates one new table version for the schema change and
computes every new column from the existing rows in it, rather than growing
the table's version history with many small updates.
<Info>
On local tables, Python `add_columns` also accepts pandas or Polars
DataFrames, PyArrow Tables, RecordBatches, RecordBatchReaders, and iterables
of batches. Pass only the new columns, with exactly one row per current live
table row in storage scan order. DataFrame indexes are ignored, and reader
batch boundaries need not match table fragments. Keep the table unchanged
between computing the values and adding them. Readers are consumed once.
Data inputs cannot be combined with `computed` columns and are not supported
on remote tables. Python callables and batch UDFs are not accepted; compute
those values first, then pass the result as a DataFrame or reader.
</Info>
## Change columns used by a computed column {#input-constraints}
<Warning>
While a computed column's declaration references another column, that column
cannot be renamed, retyped, or dropped. Drop the computed column first
if you need to change one of its inputs.
</Warning>
This is worth knowing before deriving a column from something you expect to
reshape later. Dropping the computed column releases the constraint.
## Related guides {#next}
<CardGroup cols={2}>
<Card title="Refresh a computed column" icon="arrows-rotate" href="/feature-engineering/refresh-column">
Fill the rows that lack values.
</Card>
<Card title="Functions" icon="code" href="/management/function">
Fill a column with Python.
</Card>
</CardGroup>