sqlalchemy-excel
SQLAlchemy dialect for Excel files — use Excel worksheets as database tables. This is a narrow-scope dialect: it supports basic CRUD and ORM mapping, but not relational features like JOINs or aggregations.
Limitations (Read First)
Before writing any code, understand what this dialect cannot do:
| Feature | Supported? |
|---|---|
| SELECT with WHERE, ORDER BY, LIMIT | ✅ |
| INSERT, UPDATE, DELETE | ✅ |
| CREATE TABLE / DROP TABLE | ✅ |
| ORM with DeclarativeBase | ✅ |
| Schema inspection (tables, columns) | ✅ |
| IN, BETWEEN, LIKE operators | ✅ |
| JOIN (any variant) | ❌ |
| GROUP BY / HAVING | ❌ |
| DISTINCT | ❌ |
| OFFSET | ❌ |
| Subqueries / CTEs | ❌ |
| Aggregate functions (COUNT, SUM, ...) | ❌ |
| ALTER TABLE | ❌ |
| Foreign keys / indexes | ❌ |
| Concurrent writes | ❌ |
| Session.rollback() | No-op (data persists) |
If you need any of the ❌ features, use SQLite, PostgreSQL, or another full-featured database.
Installation
pip install sqlalchemy-excel
excel-dbapi is automatically installed as a dependency.
Quick Start
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import DeclarativeBase, Session, Mapped, mapped_column
engine = create_engine("excel:///data.xlsx")
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "Sheet1"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(User(id=1, name="Alice"))
session.commit()
with Session(engine) as session:
users = session.query(User).all()
URL Format
# Relative path
engine = create_engine("excel:///data.xlsx")
# Absolute path (note four slashes)
engine = create_engine("excel:////home/user/data.xlsx")
# With engine options
engine = create_engine("excel:///data.xlsx", connect_args={"engine": "openpyxl"})
Type Mapping
| SQLAlchemy Type | Excel Storage | Notes |
|---|---|---|
String, Text, VARCHAR, CHAR |
TEXT | All string types map to TEXT |
Integer, SmallInteger, BigInteger |
INTEGER | All integer types map to INTEGER |
Float, Numeric, Decimal |
FLOAT | All numeric types map to FLOAT |
Boolean |
BOOLEAN | |
Date |
DATE | |
DateTime, TIMESTAMP |
DATETIME | |
Time |
TEXT | Stored as text |
Uuid |
TEXT | Stored as text |
BLOB, BINARY, JSON, and ARRAY types are not supported and will raise
CompileError.
ORM Examples
Define a Model
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, Session, Mapped, mapped_column
engine = create_engine("excel:///data.xlsx")
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()
age: Mapped[int] = mapped_column()
Base.metadata.create_all(engine)
Insert
with Session(engine) as session:
session.add(User(id=1, name="Alice", age=30))
session.add(User(id=2, name="Bob", age=25))
session.commit()
Query with Filters
from sqlalchemy import select
with Session(engine) as session:
# Basic query
users = session.query(User).all()
# WHERE clause
user = session.query(User).filter(User.name == "Alice").first()
# IN operator
stmt = select(User).where(User.name.in_(["Alice", "Bob"]))
users = session.scalars(stmt).all()
# BETWEEN operator
stmt = select(User).where(User.age.between(25, 35))
users = session.scalars(stmt).all()
# LIKE operator
stmt = select(User).where(User.name.like("A%"))
users = session.scalars(stmt).all()
# ORDER BY + LIMIT
stmt = select(User).order_by(User.age.desc()).limit(5)
users = session.scalars(stmt).all()
Update and Delete
with Session(engine) as session:
user = session.query(User).filter(User.id == 1).first()
if user:
user.name = "Ann"
session.commit()
with Session(engine) as session:
user = session.query(User).filter(User.id == 2).first()
if user:
session.delete(user)
session.commit()
Core Usage
from sqlalchemy import create_engine, text
engine = create_engine("excel:///data.xlsx")
with engine.connect() as conn:
result = conn.execute(text("SELECT * FROM Sheet1"))
for row in result:
print(row)
Schema Inspection
from sqlalchemy import create_engine, inspect
engine = create_engine("excel:///data.xlsx")
inspector = inspect(engine)
# List all sheets (tables)
print(inspector.get_table_names())
# Get column info
print(inspector.get_columns("Sheet1"))
# Check if a sheet exists
print(inspector.has_table("Sheet1"))
Experimental: Remote Excel via Microsoft Graph API
Status: Experimental — API may change in future releases.
Access Excel files on OneDrive/SharePoint directly:
pip install sqlalchemy-excel[graph]
from sqlalchemy import create_engine
from azure.identity import DefaultAzureCredential
engine = create_engine(
"excel+graph:///drive_id/item_id",
connect_args={"credential": DefaultAzureCredential()},
)
with engine.connect() as conn:
result = conn.execute(text("SELECT * FROM Sheet1"))
for row in result:
print(row)
URL format: excel+graph:///drive_id/item_id where drive_id and item_id are Microsoft Graph resource identifiers.
Query parameters: ?readonly=false to enable write operations.
Related Projects
- excel-dbapi — The underlying PEP 249 DB-API 2.0 driver for Excel files.
License
MIT
Metadata
Release files for sqlalchemy-excel 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sqlalchemy_excel-0.4.0.tar.gz | 33.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlalchemy_excel-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.2 kB
Release files / sqlalchemy_excel-0.4.0.tar.gz
| Download URL | sqlalchemy_excel-0.4.0.tar.gz |
|---|---|
| Size | 33.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6987152d14a38dd92091f9909a58a772b503bbbebeb359b0fbb08376745be290
|
|
BLAKE2b-256 checksum How to use checksums |
b66f6268294bb0694612028d21552ccca5333e8f93d1b800d544cc9ed79951c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 12, 2026.
Transparency logRelease files / sqlalchemy_excel-0.4.0-py3-none-any.whl
| Download URL | sqlalchemy_excel-0.4.0-py3-none-any.whl |
|---|---|
| Size | 13.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dcf1fd253348ae3ff59f637778b8b27306cec183ab82e192b27fe7cdab5544b0
|
|
BLAKE2b-256 checksum How to use checksums |
bb7bb5b091cb718b5dc1fc4ed7bab8172da7085d612910694379464c013880ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 12, 2026.
Transparency log