Execute SQL queries directly on Pandas DataFrames using sqlglot
Project description
FrameQL
Execute SQL queries directly on Pandas DataFrames — no database required.
FrameQL lets you write standard SQL against in-memory DataFrames. It uses sqlglot to parse SQL and executes the resulting query plan entirely in Python/Pandas. This is useful when you have data already loaded in memory and want to express complex transformations as SQL rather than chained DataFrame operations.
Installation
pip install frameql
Requires Python 3.9+.
Quick Start
import pandas as pd
from frameql import FrameQL
users = pd.DataFrame({
"user_id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"],
})
orders = pd.DataFrame({
"order_id": [101, 102, 103],
"user_id": [1, 1, 2],
"amount": [50.0, 30.0, 20.0],
})
engine = FrameQL({"users": users, "orders": orders})
result = engine.query("""
SELECT u.name, SUM(o.amount) AS total_spent
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
GROUP BY u.name
ORDER BY total_spent DESC
""")
print(result)
# name total_spent
# 0 Alice 80.0
# 1 Bob 20.0
Detailed Usage
Initializing the Engine
Pass a dictionary of table name → DataFrame to FrameQL. Table names are case-insensitive.
from frameql import FrameQL
engine = FrameQL({
"employees": employees_df,
"departments": departments_df,
})
Running SELECT Queries
Use engine.query(sql) for any SELECT statement. It returns a pd.DataFrame.
result = engine.query("SELECT name FROM employees WHERE salary > 60000")
Running DML Statements
Use engine.execute(sql) for INSERT, UPDATE, and DELETE. It mutates the in-memory DataFrames and returns None. It also accepts SELECT and routes to query() in that case.
# INSERT
engine.execute("INSERT INTO employees (id, name, salary) VALUES (4, 'Dana', 70000)")
# UPDATE
engine.execute("UPDATE employees SET salary = salary * 1.1 WHERE department = 'Engineering'")
# DELETE
engine.execute("DELETE FROM employees WHERE salary < 40000")
# SELECT (returns a DataFrame)
result = engine.execute("SELECT COUNT(*) AS cnt FROM employees")
Accessing Modified Tables
After DML operations, the modified DataFrame is available at engine.tables[table_name]:
engine.execute("DELETE FROM orders WHERE amount < 25")
print(engine.tables["orders"])
How It Works
- SQL Parsing —
sqlglotparses the SQL string into an AST. - Logical Plan — FrameQL scans the AST upfront to build a
QueryPlan, resolving column references, collecting aggregate specifications, and identifying window functions before any execution begins. - Execution Pipeline — The plan is executed in SQL-standard order:
WHERE→ filter rowsGROUP BY→ aggregate- Window functions (computed after GROUP BY, before SELECT)
HAVING→ post-aggregate filterSELECT→ project columnsDISTINCT→ deduplicateORDER BY→ sort (aliases from SELECT are visible here)LIMIT→ truncate
CTEs are materialized as temporary tables scoped to the query and cleaned up automatically.
Features (v0.1.0)
SELECT
SELECT *, column list, aliases, arithmetic expressions, literalsDISTINCTLIMIT
Filtering
WHEREwith=,!=,<,<=,>,>=AND,OR,NOTIS NULL/IS NOT NULLINwith literal list or subquery- Tuple / multi-column
IN:(a, b) IN (SELECT x, y FROM …) EXISTS/NOT EXISTS(correlated and non-correlated)ANY/ALLquantifiers
Joins
INNER JOIN,LEFT JOIN- Equi-joins and non-equi-join conditions
- Mixed conditions on JOIN
ONclauses (column = column AND column = literal)
Aggregation
GROUP BYwithSUM,COUNT,AVG,MIN,MAXCOUNT(DISTINCT col)- Bare aggregates without
GROUP BY HAVINGclause (reuses computed aggregates, no recomputation)
Window Functions
ROW_NUMBER(),RANK(),DENSE_RANK()SUM(),AVG(),MIN(),MAX(),COUNT()as window functionsLAG()andLEAD()with optional offset and default valuePARTITION BYandORDER BYwithin window spec
Subqueries
- Scalar subqueries in
SELECTandWHERE - Subquery in
FROMclause (derived table) - Correlated subqueries
Set Operations
UNION(deduplicates),UNION ALLINTERSECT,EXCEPTORDER BYandLIMITon set operation results- Set operations inside CTEs and subqueries
CTEs
WITHclause (Common Table Expressions)- Chained CTEs (each CTE can reference earlier ones)
- CTEs containing window functions, aggregations, or set operations
DML
INSERT INTO … VALUES (…)— single and multi-rowINSERT INTO … SELECT …UPDATE … SET … WHERE …with subqueries and expressionsDELETE FROM … WHERE …with subqueriesCASEexpressions inSETclauses
Limitations / Not Supported
RIGHT JOINandFULL OUTER JOINCROSS JOINsyntax (workaround:INNER JOINwith always-true condition, or omit ON)- Recursive CTEs
- Window frame clauses (
ROWS BETWEEN …,RANGE BETWEEN …) ROLLUP,CUBE,GROUPING SETS- String functions (
UPPER,LOWER,TRIM,SUBSTRING, etc.) - Date/time functions
COALESCE,NULLIF,IIFLIKEandILIKEpattern matching- Multi-column
COUNT(DISTINCT col1, col2)— use single-column form MERGE/UPSERT- Schema enforcement or type coercion on INSERT
Running Tests
pip install pytest
pytest tests/ -v
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file frameql-0.1.0.tar.gz.
File metadata
- Download URL: frameql-0.1.0.tar.gz
- Upload date:
- Size: 37.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0f763d63715af2e58f7d991b6c026762fa3e191e5a0779db05b8d2d526b0369
|
|
| MD5 |
228aca9fb8f31162aeceedd071d9b2d9
|
|
| BLAKE2b-256 |
37e85f2e31f2eed92e7b5b22cabe15bfabaab437f1d9087647b25343eaa9683d
|
File details
Details for the file frameql-0.1.0-py3-none-any.whl.
File metadata
- Download URL: frameql-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d724e0f2be8d64a300d7f39184a07ef21808f187fccfc65c198ea53c22222aa
|
|
| MD5 |
9db3515a879cb394cb4f3bd8e538dc28
|
|
| BLAKE2b-256 |
4dac267cfb8f9c4f8e25814e7f8a9b58fd8bc8faa161a544d71659384dea4399
|