24. How to Load SQL Query Results into a pandas DataFrame
24.6 Common Mistakes: Huge Tables and String-Format SQL
Why. Two classroom disasters: (1) loading an entire production table into RAM; (2) building SQL with Python f-strings from user or cell input. The second is how SQL injection starts — even in a “harmless” notebook. Fix both early.
What.
read_sql_table/SELECT *without filters on a huge table → slow laptop, crashed kernel, angry DBA.- String format / f-string SQL like
f"... WHERE city = '{city}'"→ unsafe ifcityever comes from outside your own typed value. - Parameterized queries pass values separately; the driver escapes them. In pandas: use
params=with?(qmark) or%(name)s/:namedepending on the backend.
How.
Safer query habit
- Never load a full big table “just to see”.
- Always add a date window or
LIMITwhile exploring. - Do not assemble SQL with
+or f-strings for values. - Put filters in
params=(or bind parameters your driver documents). - Keep column/table names under your control (whitelist) — parameters are for values, not for inventing column names from user text.
- Log
df.shapeafter every pull; if it is millions of rows by accident, stop and fix the SQL.
Worked example A — Fragile vs parameterized (SQLite ?).
import sqlite3
import pandas as pd
con = sqlite3.connect("data/blinkit_lab.db")
city = "Pune" # in real apps this might come from a form — still use params
# Fragile habit — do not use for external input
bad = f"SELECT * FROM orders WHERE city = '{city}'"
# Safer — values travel separately from SQL text
good = "SELECT order_id, city, amount_inr FROM orders WHERE city = ?"
df = pd.read_sql_query(good, con, params=("Pune",))
print(df)
con.close()
Worked example B — Named style sketch (SQLAlchemy / many servers).
import pandas as pd
from sqlalchemy import create_engine, text
engine = create_engine("sqlite:///data/blinkit_lab.db")
sql = text(
"""
SELECT city, status, amount_inr
FROM orders
WHERE status = :status AND amount_inr >= :min_amt
"""
)
df = pd.read_sql_query(
sql,
engine,
params={"status": "Delivered", "min_amt": 100},
)
print(df)
engine.dispose()
Worked example C — Huge-table checklist (fictional Monday).
Shraddha almost ran pd.read_sql_table("orders", engine) on production. Checklist stopped her:
- Row estimate from DBA /
COUNT(*)on a filtered subset. - Date filter for “today IST”.
- Column list without receipt JSON blobs.
LIMIT 1000smoke test first.- Then remove
LIMITonly if the filtered shape is sane.
Ravindra Bagale's Tip
f-string SQL = shortcut temptation. params= = professional habit. Mothe table = pehle LIMIT.