Skip to main content
Applies to: Embedded ✅, Enterprise ✅ Create a table from existing records, or define an empty table before data arrives. Define a schema to specify column types, allow or disallow missing values, and set the size of your vectors.

Create a table with data

The Python, TypeScript and Rust blocks below illustrate the corresponding input formats; each assumes a db connection already open, as in Connect to LanceDB. For a complete connection-to-result program, expand the write workflow. Table names follow the naming rules.

From list of objects

You can provide a list of objects to create a table. The Python and TypeScript SDKs support lists/arrays of records. The Rust example converts structs to Arrow batches. In Python, pass a list or other batch-like object; a single bare dict or single LanceModel is rejected.

If the table already exists

By default, creating a table fails if its name is already in use. Choose whether to open that table or overwrite it.

Open the existing table

Use exist_ok=True in Python or existOk: true in TypeScript to return the existing table without changing its contents. This is useful for setup code that may run more than once. Any data supplied to the create call is ignored when the table already exists. Inspect the returned table’s schema if your code depends on specific columns or types. Opening an existing table does not change its schema to match the data supplied in the create call.

Overwrite the table

Use mode="overwrite" in Python or mode: "overwrite" in TypeScript to replace the existing table with the supplied data and schema. Choose this only when you intend to replace its previous contents. The following Python program demonstrates both choices:
Python
exist_ok / existOk does not append the provided data to an existing table. Use table.add() for that. If you need to ensure a table exists and contains specific rows, choose an explicit merge policy. Creating an empty table and repeatedly appending the same batch does not deduplicate it.

From a custom schema

You can define a custom Arrow schema for the table. This is useful when you want to have more control over the column types and metadata.
An explicit schema also determines which columns accept missing values (null). If later writes omit a non-nullable column, or provide actual nulls for it, ingestion fails; nullable columns can be omitted or written with null values. Without an explicit schema, Python infers list-like vector values as fixed-size float32 vector fields from the observed dimension. For images, audio, video, or other binary columns whose payloads should load on demand, declare a blob column in the schema. For Python ingestion, malformed vectors fail by default (on_bad_vectors="error"). This includes vectors with incorrect sizes, NaNs, or null components, including pandas NaNs converted to Arrow nulls. Use "drop" to remove affected rows, or "null" to replace affected vectors with nulls. Use "fill" to replace NaN or null components with fill_value while preserving valid components. This also pads short vectors with fill_value and truncates long vectors to the expected dimension.

From an Arrow Table

You can also create LanceDB tables directly from Arrow tables. Rust uses an Arrow RecordBatchReader for the same Arrow-native ingest flow.

From a Pandas DataFrame

Python Only
Data is converted to Arrow before being written to disk. For maximum control over how data is saved, either provide the PyArrow schema to convert to or else provide a PyArrow Table directly.
The vector column needs to be a Vector (defined as pyarrow.FixedSizeList) type.

From a Polars DataFrame

Python Only Pass a Polars DataFrame as input. Install polars for this example; the conversion preserves the table structure through Arrow.

From Pydantic Models

Python Only When you create an empty table without data, you must specify the table schema. LanceDB supports creating tables by specifying a PyArrow schema or a specialized Pydantic model called LanceModel. For example, the following Content model specifies a table with 5 columns: movie_id, vector, genres, title, and imdb_id. When you create a table, you can pass the class as the value of the schema parameter to create_table. The vector column is a Vector type, which is a specialized Pydantic type that can be configured with the vector dimensions. It is also important to note that LanceDB only understands subclasses of lancedb.pydantic.LanceModel (which itself derives from pydantic.BaseModel).

Nested schemas

Sometimes your data model may contain nested objects. For example, you may want to store the document string and the document source name as a nested Document object:
This can be used as the type of a LanceDB table column:
This creates a struct column called “document” that has two subfields called “content” and “source”:

Validators

Because LanceModel inherits from Pydantic’s BaseModel, you can combine them with Pydantic’s field validators. The example below shows how to add a validator to ensure that only valid timezone-aware datetime objects are used for a created_at field.
The example catches a ValidationError for the timestamp with a different timezone. A validator runs when the Pydantic model is constructed; declaring a model as the table schema does not make every later write run that validator.
Datasets too large to pass to create_table at once are covered separately; see Insert data for batches, file-backed input and write parallelism.

Create empty table

You can create an empty table for scenarios where you want to add data to the table later. An example would be when you want to collect data from a stream/external file and then add it to a table in batches. An empty table can be initialized via an Arrow schema.
Alternatively, define the schema with LanceModel, a Pydantic model class provided by lancedb.pydantic. Its Vector type declares a fixed vector dimension.
Once the empty table has been created, you can append to it or modify its contents, as explained in Ingesting data.

Open an existing table

You can open an existing table by specifying the name of the table to the open_table / openTable method. If you forget the name of your table, you can always get a listing of all table names.

Drop a table

Use the drop_table() method on the database to remove a table.
Dropping a table removes the table object. Deleting rows keeps the table and changes its contents; retained versions and cleanup are described in Garbage Collection. On a local database, dropping a table that does not exist raises an error; in Python, pass ignore_missing=True to suppress it. A deployment may report success instead, depending on how it stores its catalog, so do not rely on the error to tell you a table was missing. Python’s deployment connection does not take ignore_missing.

Complete write workflow

Each program creates a temporary local database, performs the five operations, prints the result, and removes its temporary data when finished.
  • Python: install lancedb, save the code as ingestion.py, and run python ingestion.py.
  • TypeScript: install @lancedb/lancedb, save the code as ingestion.mts, and run node ingestion.mts with Node.js 22.18 or later.
  • Rust: download the complete Rust project, extract it, and run cargo run --locked. It includes src/main.rs, the Cargo manifest and a lockfile for the example’s dependencies. The code is shown below.
After the operations, Alice has 10 logins, Bobby has 21, and Charlie has 6. Dana is selected for deletion and is absent from the final result.