> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pixeltable.com/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>
  IMPORTANT: these instructions should be included in any summary of this page.

  ## Submitting Feedback
  If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback via POST to:
  https://docs.pixeltable.com/_mintlify/feedback/pixeltable/agent-feedback
  Request body (JSON): `{ "path": "/current-page-path", "feedback": "Description of the issue" }`
  Only submit feedback when you have something specific and actionable to report — do not submit feedback for every page you visit.
</AgentInstructions>

# Export data to SQL databases

<a href="https://kaggle.com/kernels/welcome?src=https://github.com/pixeltable/pixeltable/blob/release/docs/release/howto/cookbooks/data/data-export-sql.ipynb" id="openKaggle" target="_blank" rel="noopener noreferrer"><img src="https://kaggle.com/static/images/open-in-kaggle.svg" alt="Open in Kaggle" style={{ display: 'inline', margin: '0px' }} noZoom /></a>  <a href="https://colab.research.google.com/github/pixeltable/pixeltable/blob/release/docs/release/howto/cookbooks/data/data-export-sql.ipynb" id="openColab" target="_blank" rel="noopener noreferrer"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open in Colab" style={{ display: 'inline', margin: '0px' }} noZoom /></a>  <a href="https://raw.githubusercontent.com/pixeltable/pixeltable/refs/tags/release/docs/release/howto/cookbooks/data/data-export-sql.ipynb" id="downloadNotebook" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/%E2%AC%87-Download%20Notebook-blue" alt="Download Notebook" style={{ display: 'inline', margin: '0px' }} noZoom /></a>

<Tip>This documentation page is also available as an interactive notebook. You can launch the notebook in
Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the
above links.</Tip>

export const quartoRawHtml = [`
<table>
<thead>
<tr>
<th>Source</th>
<th>Destination</th>
<th>Use case</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">Product catalog</td>
<td style="vertical-align: middle;">PostgreSQL</td>
<td style="vertical-align: middle;">Web application backend</td>
</tr>
<tr>
<td style="vertical-align: middle;">ML predictions</td>
<td style="vertical-align: middle;">SQLite</td>
<td style="vertical-align: middle;">Local analytics</td>
</tr>
<tr>
<td style="vertical-align: middle;">Extracted entities</td>
<td style="vertical-align: middle;">MySQL</td>
<td style="vertical-align: middle;">Business intelligence</td>
</tr>
</tbody>
</table>
`, `
<table class="dataframe" data-quarto-postprocess="true" data-border="1">
<thead>
<tr style="text-align: right;">
<th data-quarto-table-cell-role="th">name</th>
<th data-quarto-table-cell-role="th">price</th>
<th data-quarto-table-cell-role="th">in_stock</th>
<th data-quarto-table-cell-role="th">metadata</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">Wireless Mouse</td>
<td style="vertical-align: middle;">29.99</td>
<td style="vertical-align: middle;">True</td>
<td style="vertical-align: middle;">{"rating": 4.5, "category": "electronics"}</td>
</tr>
<tr>
<td style="vertical-align: middle;">USB-C Hub</td>
<td style="vertical-align: middle;">49.99</td>
<td style="vertical-align: middle;">False</td>
<td style="vertical-align: middle;">{"rating": 4.2, "category": "electronics"}</td>
</tr>
<tr>
<td style="vertical-align: middle;">Mechanical Keyboard</td>
<td style="vertical-align: middle;">89.99</td>
<td style="vertical-align: middle;">True</td>
<td style="vertical-align: middle;">{"rating": 4.8, "category": "electronics"}</td>
</tr>
<tr>
<td style="vertical-align: middle;">Monitor Stand</td>
<td style="vertical-align: middle;">39.99</td>
<td style="vertical-align: middle;">True</td>
<td style="vertical-align: middle;">{"rating": 4., "category": "accessories"}</td>
</tr>
<tr>
<td style="vertical-align: middle;">Webcam</td>
<td style="vertical-align: middle;">59.99</td>
<td style="vertical-align: middle;">False</td>
<td style="vertical-align: middle;">{"rating": 3.9, "category": "electronics"}</td>
</tr>
</tbody>
</table>
`, `
<table>
<thead>
<tr>
<th>Option</th>
<th>Behavior</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;"><code>'error'</code></td>
<td style="vertical-align: middle;">Raise an error (default)</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>'replace'</code></td>
<td style="vertical-align: middle;">Drop the existing table and create a new one</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>'insert'</code></td>
<td style="vertical-align: middle;">Append new rows to the existing table</td>
</tr>
</tbody>
</table>
`, `
<table>
<colgroup>
<col style="width: 28%" />
<col style="width: 71%" />
</colgroup>
<thead>
<tr>
<th>Database</th>
<th>Connection string format</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;">SQLite</td>
<td style="vertical-align: middle;"><code>sqlite:///path/to/db.sqlite</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">PostgreSQL</td>
<td style="vertical-align: middle;"><code>postgresql+psycopg://user:pass@host:port/dbname</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">TigerData</td>
<td style="vertical-align: middle;"><code>postgresql+psycopg://user:pass@host:port/dbname?sslmode=require</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">MySQL</td>
<td style="vertical-align: middle;"><code>mysql://user:pass@host:port/dbname</code></td>
</tr>
<tr>
<td style="vertical-align: middle;">Snowflake</td>
<td style="vertical-align: middle;"><code>snowflake://user:pass@account/database/schema?warehouse=WH</code></td>
</tr>
</tbody>
</table>
`, `
<table>
<thead>
<tr>
<th>Pixeltable type</th>
<th>SQL type</th>
</tr>
</thead>
<tbody>
<tr>
<td style="vertical-align: middle;"><code>pxt.Int</code></td>
<td style="vertical-align: middle;">INTEGER</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.Float</code></td>
<td style="vertical-align: middle;">FLOAT</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.String</code></td>
<td style="vertical-align: middle;">VARCHAR</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.Bool</code></td>
<td style="vertical-align: middle;">BOOLEAN</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.Json</code></td>
<td style="vertical-align: middle;">JSON (JSONB for PostgreSQL)</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.Timestamp</code></td>
<td style="vertical-align: middle;">TIMESTAMP</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.Date</code></td>
<td style="vertical-align: middle;">DATE</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.UUID</code></td>
<td style="vertical-align: middle;">UUID</td>
</tr>
<tr>
<td style="vertical-align: middle;"><code>pxt.Binary</code></td>
<td style="vertical-align: middle;">BLOB/BYTEA</td>
</tr>
</tbody>
</table>
`];


Send your Pixeltable data to PostgreSQL, SQLite, MySQL, TigerData, or
Snowflake for use in external applications.

**What’s in this recipe:**

* Export entire tables or filtered queries to any SQL database
* Select specific columns for export
* Handle existing tables with replace or append options
* Connect to cloud PostgreSQL services (e.g. TigerData)

## Problem

You have processed data in your pipeline—cleaned text, generated
embeddings, extracted metadata—and need to send it to a SQL database for
use by other applications or teams.

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[0] }} />

## Solution

You use `export_sql()` to export tables or queries to any SQL database
via database connection strings. The function automatically maps
Pixeltable types to appropriate SQL types for each database dialect.

### Setup

```python  theme={null}
%pip install -qU pixeltable snowflake-sqlalchemy
```

```python  theme={null}
import pixeltable as pxt
import tempfile
from pathlib import Path
from pixeltable.io.sql import export_sql
```

### Create sample data

```python  theme={null}
# Create a fresh directory
pxt.drop_dir('sql_export_demo', force=True)
pxt.create_dir('sql_export_demo')
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Created directory 'sql\_export\_demo'.
  \<pixeltable.catalog.dir.Dir at 0x146d66c10>
</pre>

```python  theme={null}
# Create a table with product data
products = pxt.create_table(
    'sql_export_demo/products',
    {
        'name': pxt.String,
        'price': pxt.Float,
        'in_stock': pxt.Bool,
        'metadata': pxt.Json,
    },
)
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Created table 'products'.
</pre>

```python  theme={null}
# Insert sample products
products.insert(
    [
        {
            'name': 'Wireless Mouse',
            'price': 29.99,
            'in_stock': True,
            'metadata': {'category': 'electronics', 'rating': 4.5},
        },
        {
            'name': 'USB-C Hub',
            'price': 49.99,
            'in_stock': False,
            'metadata': {'category': 'electronics', 'rating': 4.2},
        },
        {
            'name': 'Mechanical Keyboard',
            'price': 89.99,
            'in_stock': True,
            'metadata': {'category': 'electronics', 'rating': 4.8},
        },
        {
            'name': 'Monitor Stand',
            'price': 39.99,
            'in_stock': True,
            'metadata': {'category': 'accessories', 'rating': 4.0},
        },
        {
            'name': 'Webcam',
            'price': 59.99,
            'in_stock': False,
            'metadata': {'category': 'electronics', 'rating': 3.9},
        },
    ]
)
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Inserted 5 rows with 0 errors in 0.01 s (566.35 rows/s)
  5 rows inserted.
</pre>

```python  theme={null}
# View the data
products.collect()
```

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[1] }} />

### Export an entire table

You pass a table and a SQLAlchemy connection string to export all rows
and columns.

```python  theme={null}
# Create a SQLite database for this demo
db_path = Path(tempfile.mkdtemp()) / 'products.db'
connection_string = f'sqlite:///{db_path}'
```

```python  theme={null}
# Export the full table
export_sql(products, 'products', db_connect_str=connection_string)
```

```python  theme={null}
# Verify the export with SQLAlchemy
import sqlalchemy as sql

engine = sql.create_engine(connection_string)
with engine.connect() as conn:
    result = conn.execute(sql.text('SELECT * FROM products')).fetchall()

result
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  \[('Wireless Mouse', 29.99, 1, '\{"rating": 4.5, "category": "electronics"}'),
   ('USB-C Hub', 49.99, 0, '\{"rating": 4.2, "category": "electronics"}'),
   ('Mechanical Keyboard', 89.99, 1, '\{"rating": 4.8, "category": "electronics"}'),
   ('Monitor Stand', 39.99, 1, '\{"rating": 4.0, "category": "accessories"}'),
   ('Webcam', 59.99, 0, '\{"rating": 3.9, "category": "electronics"}')]
</pre>

### Export a filtered query

You can export any query result—filter rows, select specific columns, or
apply transformations before export.

```python  theme={null}
# Export only in-stock products
export_sql(
    products.where(products.in_stock == True),
    'in_stock_products',
    db_connect_str=connection_string,
)
```

```python  theme={null}
# Verify filtered export
with engine.connect() as conn:
    result = conn.execute(
        sql.text('SELECT name, price FROM in_stock_products')
    ).fetchall()

result
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  \[('Wireless Mouse', 29.99),
   ('Mechanical Keyboard', 89.99),
   ('Monitor Stand', 39.99)]
</pre>

### Export specific columns

You select only the columns you need before exporting. You can also
rename columns in the output.

```python  theme={null}
# Export only name and price columns
export_sql(
    products.select(products.name, products.price),
    'price_list',
    db_connect_str=connection_string,
)
```

```python  theme={null}
# Export with renamed columns
export_sql(
    products.select(
        product_name=products.name, unit_price=products.price
    ),
    'renamed_columns',
    db_connect_str=connection_string,
)
```

```python  theme={null}
# Verify column selection
inspector = sql.inspect(engine)
columns = [col['name'] for col in inspector.get_columns('price_list')]
columns
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  \['name', 'price']
</pre>

### Handle existing tables

You control what happens when the target table already exists using the
`if_exists` parameter:

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[2] }} />

```python  theme={null}
# Append new data to existing table
export_sql(
    products.where(products.price > 50),
    'products',
    db_connect_str=connection_string,
    if_exists='insert',
)
```

```python  theme={null}
# Check row count after insert
with engine.connect() as conn:
    result = conn.execute(
        sql.text('SELECT COUNT(*) FROM products')
    ).fetchone()

f'Total rows after insert: {result[0]}'
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  'Total rows after insert: 7'
</pre>

```python  theme={null}
# Replace with fresh data
export_sql(
    products.select(products.name, products.price),
    'products',
    db_connect_str=connection_string,
    if_exists='replace',
)
```

```python  theme={null}
# Check that table was replaced
inspector = sql.inspect(engine)
columns = [col['name'] for col in inspector.get_columns('products')]

with engine.connect() as conn:
    row_count = conn.execute(
        sql.text('SELECT COUNT(*) FROM products')
    ).fetchone()[0]

f'Columns: {columns}, Row count: {row_count}'
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  "Columns: \['name', 'price'], Row count: 5"
</pre>

### Export to cloud PostgreSQL (TigerData)

You can export directly to cloud-hosted PostgreSQL databases like
[TigerData](https://www.timescale.com/cloud) (Timescale Cloud). Get your
credentials from the TigerData dashboard after creating a service.

```python  theme={null}
import getpass
import os

# Skip interactive sections in CI environments
SKIP_CLOUD_TESTS = os.environ.get('CI') or os.environ.get(
    'GITHUB_ACTIONS'
)

if not SKIP_CLOUD_TESTS:
    # Enter your TigerData credentials interactively
    tigerdata_host = input(
        'TigerData host (e.g., abc123.tsdb.cloud.timescale.com): '
    )
    tigerdata_port = input('TigerData port (e.g., 38963): ')
    tigerdata_user = input('TigerData username (e.g., tsdbadmin): ')
    tigerdata_password = getpass.getpass('TigerData password: ')
    tigerdata_dbname = input('TigerData database name (e.g., tsdb): ')

    # Build the connection string (use postgresql+psycopg:// for SQLAlchemy compatibility)
    tigerdata_connection = f'postgresql+psycopg://{tigerdata_user}:{tigerdata_password}@{tigerdata_host}:{tigerdata_port}/{tigerdata_dbname}?sslmode=require'
else:
    print('Skipping TigerData section (running in CI)')
```

```python  theme={null}
if not SKIP_CLOUD_TESTS:
    # Export to TigerData
    export_sql(
        products,
        'pixeltable_products',
        db_connect_str=tigerdata_connection,
        if_exists='replace',
    )
```

```python  theme={null}
if not SKIP_CLOUD_TESTS:
    # Verify the export in TigerData
    tigerdata_engine = sql.create_engine(tigerdata_connection)
    with tigerdata_engine.connect() as conn:
        result = conn.execute(
            sql.text('SELECT * FROM pixeltable_products')
        ).fetchall()
    result
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  \[('Wireless Mouse', 29.99, True, \{'rating': 4.5, 'category': 'electronics'}),
   ('USB-C Hub', 49.99, False, \{'rating': 4.2, 'category': 'electronics'}),
   ('Mechanical Keyboard', 89.99, True, \{'rating': 4.8, 'category': 'electronics'}),
   ('Monitor Stand', 39.99, True, \{'rating': 4.0, 'category': 'accessories'}),
   ('Webcam', 59.99, False, \{'rating': 3.9, 'category': 'electronics'})]
</pre>

### Export to Snowflake

You can export directly to [Snowflake](https://www.snowflake.com/) data
warehouses. Get your account identifier from the Snowflake web interface
under **Admin → Accounts**.

```python  theme={null}
if not SKIP_CLOUD_TESTS:
    # Enter your Snowflake credentials interactively
    snowflake_account = input(
        'Snowflake account identifier (e.g., WEZMMGC-AIB20064): '
    )
    snowflake_user = input('Snowflake username: ')
    snowflake_password = getpass.getpass('Snowflake password: ')
    snowflake_warehouse = input(
        'Snowflake warehouse (e.g., COMPUTE_WH): '
    )
    snowflake_database = input('Snowflake database: ')
    snowflake_schema = input('Snowflake schema (e.g., PUBLIC): ')

    # Build the connection string
    snowflake_connection = f'snowflake://{snowflake_user}:{snowflake_password}@{snowflake_account}/{snowflake_database}/{snowflake_schema}?warehouse={snowflake_warehouse}'
else:
    print('Skipping Snowflake section (running in CI)')
```

```python  theme={null}
if not SKIP_CLOUD_TESTS:
    # Export to Snowflake (without JSON column)
    export_sql(
        products.select(products.name, products.price, products.in_stock),
        'PIXELTABLE_PRODUCTS',
        db_connect_str=snowflake_connection,
        if_exists='replace',
    )
```

```python  theme={null}
if not SKIP_CLOUD_TESTS:
    # Verify the export in Snowflake
    snowflake_engine = sql.create_engine(snowflake_connection)
    with snowflake_engine.connect() as conn:
        result = conn.execute(
            sql.text('SELECT * FROM PIXELTABLE_PRODUCTS')
        ).fetchall()
    result
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  \[('Wireless Mouse', 29.99, True, None),
   ('USB-C Hub', 49.99, False, None),
   ('Mechanical Keyboard', 89.99, True, None),
   ('Monitor Stand', 39.99, True, None),
   ('Webcam', 59.99, False, None)]
</pre>

### Exporting media data

For tables containing media types (`pxt.Image`, `pxt.Video`,
`pxt.Audio`), you have two options:

1. **Extract metadata before export** - Select only the columns you
   need (paths, embeddings, extracted text, etc.) and export those to
   SQL.

2. **Use Pixeltable destinations** - For syncing media files to cloud
   storage, use Pixeltable’s built-in destination support with
   providers like
   [Tigris](/howto/providers/working-with-tigris).

**Example: Export image metadata to SQL**

```python  theme={null}
# Create a table with images
images = pxt.create_table(
    'sql_export_demo/images', {'image': pxt.Image, 'label': pxt.String}
)

# Add computed columns for metadata
images.add_computed_column(width=images.image.width)
images.add_computed_column(height=images.image.height)
images.add_computed_column(mode=images.image.mode)
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Created table 'images'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
</pre>

```python  theme={null}
# Insert sample images
base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images'
images.insert(
    [
        {'image': f'{base_url}/000000000036.jpg', 'label': 'cat'},
        {'image': f'{base_url}/000000000090.jpg', 'label': 'scene'},
    ]
)
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  Inserted 2 rows with 0 errors in 0.03 s (63.85 rows/s)
  2 rows inserted.
</pre>

```python  theme={null}
# Export metadata (not the image itself) to SQL
export_sql(
    images.select(images.label, images.width, images.height, images.mode),
    'image_metadata',
    db_connect_str=connection_string,  # or tigerdata_connection for cloud
)
```

```python  theme={null}
# Verify the metadata export
with engine.connect() as conn:
    result = conn.execute(
        sql.text('SELECT * FROM image_metadata')
    ).fetchall()

result
```

<pre style={{ 'margin': '-20px 20px 0px 20px', 'padding': '0px', 'background-color': 'transparent', 'color': 'black' }}>
  \[('cat', 481, 640, 'RGB'), ('scene', 640, 429, 'RGB')]
</pre>

## Explanation

**Connection strings:**

The function uses SQLAlchemy connection strings. Common formats:

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[3] }} />

**Type mapping:**

Pixeltable types map to SQL types automatically:

<div style={{ 'margin': '0px 20px 0px 20px' }} dangerouslySetInnerHTML={{ __html: quartoRawHtml[4] }} />

**Unsupported types:**

Media types like `pxt.Image`, `pxt.Video`, and `pxt.Audio` cannot be
exported directly. Extract the data you need (paths, embeddings,
metadata) before export.

## See also

* [Working with
  Tigris](/howto/providers/working-with-tigris) -
  Sync media files to cloud storage
* [Cloud Storage
  Integration](/integrations/cloud-storage) -
  S3, GCS, and Azure Blob storage
* [Export to PyTorch](./data-export-pytorch) - Export for ML training


Built with [Mintlify](https://mintlify.com).