mirror of
https://github.com/lancedb/lancedb.git
synced 2026-11-13 14:16:54 +00:00
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>
226 lines
8.3 KiB
Plaintext
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>
|