Pandas Read SQL Database
Pandas provides a set of functions for directly interacting with SQL databases. Query results can be read directly into a DataFrame, and a DataFrame can also be written back to the database. This allows data analysts to avoid manually handling database connections and result parsing, greatly simplifying the interaction between databases and Python.
Core Function Overview
| Function | Purpose | Return Type |
|---|---|---|
pd.read_sql() |
Executes SQL queries or reads an entire table (a general-purpose function compatible with both scenarios) | DataFrame |
pd.read_sql_query() |
Executes SQL query statements, suitable for complex queries | DataFrame |
pd.read_sql_table() |
Directly reads an entire table, only supports SQLAlchemy connections | DataFrame |
DataFrame.to_sql() |
Writes a DataFrame to a database table | None / int |
In practical work, it is recommended to use
pd.read_sql()because it automatically determines whether to execute a query or read an entire table based on the passed parameters, offering the best compatibility.pd.read_sql_table()Only supports SQLAlchemy engine connections; it does not support nativesqlite3and other DB-API connections.
Establishing a Database Connection
Pandas itself does not connect directly to databases. It requires a third-party library to establish a connection, which is then passed in. There are two mainstream approaches:SQLAlchemy engine(recommended) andnative DB-API connection(lightweight and simple).
Method 1: SQLAlchemy (Recommended)
SQLAlchemy is Python's most mainstream database toolkit. It supports all major databases and has the best compatibility with Pandas:
pip install sqlalchemy
Example
# SQLAlchemy connection string format: database type + driver://username:password@host:port/database name
# SQLite (file-based database, no username or password needed)
engine = create_engine("sqlite:///mydata.db")
# MySQL
engine = create_engine("mysql+pymysql://root:password@localhost:3306/mydb")
# PostgreSQL
engine = create_engine("postgresql+psycopg2://user:password@localhost:5432/mydb")
# SQL Server
engine = create_engine("mssql+pyodbc://user:password@server/mydb?driver=ODBC+Driver+17+for+SQL+Server")
# Verify whether the connection is successful
with engine.connect() as conn:
print("Connection successful")
Method 2: Native sqlite3 Connection (SQLite Only)
Python's built-insqlite3module requires no additional installation and is suitable for lightweight local SQLite databases:
Example
# Connect to a SQLite file database (automatically created if the file does not exist)
conn = sqlite3.connect("mydata.db")
# Connect to an in-memory database (data disappears after the program exits; suitable for testing)
conn_memory = sqlite3.connect(":memory:")
# The connection must be manually closed after use
# conn.close()
pd.read_sql() Reading Data
Basic Syntax
pd.read_sql(sql, con, index_col=None, coerce_float=True, params=None, parse_dates=None, columns=None, chunksize=None)
Main parameter descriptions:
| Parameter | Type | Description |
|---|---|---|
sql |
str | SQL query statement, or table name (related to thecontype) |
con |
Connection object | SQLAlchemy engine or DB-API connection object |
index_col |
str or list | Sets the specified column as the row index of the DataFrame |
params |
list or dict | Parameter values for SQL parameterized queries, preventing SQL injection |
parse_dates |
list or dict | Parses the specified column as datetime type |
chunksize |
int | Reads in chunks, returning an iterator of DataFrames with the specified number of rows per chunk |
1. Reading an Entire Table
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Read the entire employees table
df = pd.read_sql("employees", con=engine)
print(df.head())
print(f"Total {len(df)} rows, {len(df.columns)} columns")
2. Executing SQL Queries
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Query with conditions
df = pd.read_sql("SELECT * FROM employees WHERE department = 'IT'", con=engine)
# Multi-table join query
sql = """
SELECT e.name, e.salary, d.department_name
FROM employees e
JOIN departments d ON e.dept_id = d.id
WHERE e.salary > 10000
ORDER BY e.salary DESC
"""
df = pd.read_sql(sql, con=engine)
print(df.head(10))
3. Parameterized Queries (Preventing SQL Injection)
When query conditions come from user input,always use parameterized queriesand do not use string concatenation for SQL:
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# ❌ Dangerous approach: string concatenation of SQL, risk of SQL injection
dept = "IT"
# df = pd.read_sql(f"SELECT * FROM employees WHERE department = '{dept}'", engine)
# ✅ Safe approach: use ? placeholders (sqlite3) or :name named parameters (SQLAlchemy)
# SQLite / DB-API style (using ?)
df = pd.read_sql(
"SELECT * FROM employees WHERE department = ? AND salary > ?",
con=engine,
params=["IT", 8000] # params is a list, replacing ? positionally
)
# SQLAlchemy named parameter style (using :param_name)
from sqlalchemy import text
with engine.connect() as conn:
df = pd.read_sql(
text("SELECT * FROM employees WHERE department = :dept AND salary > :min_salary"),
con=conn,
params={"dept": "IT", "min_salary": 8000}
)
print(df)
4. Setting the Index Column
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Set the id column in the database as the row index of the DataFrame
df = pd.read_sql("SELECT * FROM employees", con=engine, index_col="id")
print(df.head())
# Use multiple columns as a composite index
df = pd.read_sql(
"SELECT * FROM orders",
con=engine,
index_col=["year", "month"] # Composite index
)
5. Parsing Date Columns
Date fields stored in the database are read as strings by default. Using theparse_datesparameter, they can be directly parsed intodatetime:
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Parse the created_at and updated_at columns as datetime type
df = pd.read_sql(
"SELECT * FROM orders",
con=engine,
parse_dates=["created_at", "updated_at"]
)
print(df.dtypes)
# created_at datetime64[ns]
# updated_at datetime64[ns]
# You can also specify the parsing format (for non-standard date formats)
df = pd.read_sql(
"SELECT * FROM orders",
con=engine,
parse_dates={"created_at": "%Y%m%d"} # Parse a format like "20240115" as a date
)
Reading Large Data in Chunks (chunksize)
When a database table has a very large amount of data, reading it all into memory at once can cause OOM (out of memory). Using thechunksizeparameter, data can be read in batches, processing only a portion at a time:
Example
from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://root:password@localhost/bigdata")
# chunksize=10000 means reading 10000 rows at a time, returning an iterator
chunks = pd.read_sql("SELECT * FROM large_table", con=engine, chunksize=10000)
# Process chunk by chunk (only 10000 rows in memory at a time)
result_list = []
for i, chunk in enumerate(chunks):
# Apply processing logic to each chunk (e.g., filtering, aggregation, etc.)
processed = chunk[chunk["status"] == "active"]
result_list.append(processed)
print(f"Processed chunk {i+1}, valid rows in current chunk: {len(processed)}")
# Combine all processed chunks into a single DataFrame
final_df = pd.concat(result_list, ignore_index=True)
print(f"Final valid data rows: {len(final_df)}")
pd.read_sql_query() and pd.read_sql_table()
pd.read_sql_query(): Only Executes Queries
andpd.read_sql()The functionality is basically the same, but it only accepts SQL query statements, not table names. The parameters are exactly the same, suitable for scenarios where you need to clearly distinguish between "query" and "read table" operations:
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
df = pd.read_sql_query(
"SELECT name, salary FROM employees WHERE salary > 5000",
con=engine
)
print(df)
pd.read_sql_table(): Reads an Entire Table (SQLAlchemy Only)
pd.read_sql_table()Specifically used to read an entire table, with support for filtering columns and rows via parameters, butonly supports SQLAlchemy engine connectionsand does not support native connections such as sqlite3:
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Read specified columns from the employees table
df = pd.read_sql_table(
"employees",
con=engine,
columns=["id", "name", "salary", "department"] # Only read these columns
)
# Also supports specifying schema (the mode in the database)
df = pd.read_sql_table(
"employees",
con=engine,
schema="hr" # Read the employees table under the hr schema
)
print(df.head())
Writing a DataFrame to a Database (to_sql)
UsageDataFrame.to_sql()You can write a DataFrame to a database, with support for creating a new table, appending data, or overwriting the original table:
Basic Syntax
DataFrame.to_sql(name, con, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None, method=None)
if_existsThe parameter determines the behavior when the target table already exists:
| if_exists Value | Behavior | Use Case |
|---|---|---|
'fail'(Default) |
Raises an error if the table already exists | Prevents accidentally overwriting existing data |
'replace' |
Drops the original table first, then recreates the table and writes to it | Full data refresh |
'append' |
Appends data to an existing table without changing the table structure | Incrementally writes new data |
Example
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Prepare sample data
df = pd.DataFrame({
"name": ["Zhang San", "Li Si", "Wang Wu"],
"department": ["IT", "HR", "IT"],
"salary": [12000, 8000, 15000]
})
# Write the DataFrame to the employees table; replace it if it already exists
df.to_sql(
"employees",
con=engine,
if_exists="replace", # Overwrite the original data
index=False # Do not write the DataFrame's row index to the database (usually not needed)
)
# Append new data to the existing table (note: column names and data types must match)
new_employees = pd.DataFrame({
"name": ["Zhao Liu"],
"department": ["Finance"],
"salary": [11000]
})
new_employees.to_sql("employees", con=engine, if_exists="append", index=False)
# Verify the write result
result = pd.read_sql("SELECT * FROM employees", con=engine)
print(result)
Specifying Column Data Types
When writing, you can use thedtypeparameter to explicitly specify the type of each column in the database:
Example
from sqlalchemy import create_engine, Integer, String, Float, DateTime
engine = create_engine("sqlite:///mydata.db")
df = pd.DataFrame({
"id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"],
"score": [92.5, 88.0, 95.3],
})
df.to_sql(
"students",
con=engine,
if_exists="replace",
index=False,
dtype={
"id": Integer(), # Integer type
"name": String(50), # Variable-length string, maximum 50 characters
"score": Float() # Floating-point number
}
)
Complete Usage Example
Below is a complete example of reading sales data from a SQLite database and analyzing it:
Example
import sqlite3
from sqlalchemy import create_engine
# Step 1: Prepare the test database
engine = create_engine("sqlite:///sales.db")
conn = sqlite3.connect("sales.db")
# Create sample data and write it to the database
sales_data = pd.DataFrame({
"order_id": [1001, 1002, 1003, 1004, 1005, 1006],
"product": ["Laptop", "Phone", "Tablet", "Laptop", "Phone", "Tablet"],
"amount": [6999, 3999, 2999, 7299, 4299, 3199],
"quantity": [2, 5, 3, 1, 4, 2],
"order_date": ["2024-01-10", "2024-01-15", "2024-01-20",
"2024-02-05", "2024-02-10", "2024-02-18"]
})
sales_data.to_sql("sales", con=engine, if_exists="replace", index=False)
# Step 2: Read the data and parse the dates
df = pd.read_sql(
"SELECT * FROM sales",
con=engine,
parse_dates=["order_date"]
)
print("Raw data:")
print(df)
print()
# Step 3: Calculate sales by product
sql_agg = """
SELECT product,
COUNT(*) AS order_count,
SUM(amount * quantity) AS total_sales,
AVG(amount) AS avg_unit_price
FROM sales
GROUP BY product
ORDER BY total_sales DESC
"""
summary = pd.read_sql(sql_agg, con=engine)
print("Sales summary by product:")
print(summary)
print()
# Step 4: Write the summary results back to the database
summary.to_sql("sales_summary", con=engine, if_exists="replace", index=False)
print("Summary data has been written to the sales_summary table")
conn.close()
The execution result of the above code is:
原始数据: order_id product amount quantity order_date 0 1001 笔记本 6999 2 2024-01-10 1 1002 手机 3999 5 2024-01-15 2 1003 平板 2999 3 2024-01-20 3 1004 笔记本 7299 1 2024-02-05 4 1005 手机 4299 4 2024-02-10 5 1006 平板 3199 2 2024-02-18 各产品销售汇总: product 订单数 总销售额 平均单价 0 手机 2 36991 4149.0 1 笔记本 2 21297 7149.0 2 平板 2 15395 3099.0 汇总数据已写入 sales_summary 表
Common Issues and Notes
1. The connection needs to be closed after use
When using a DB-API native connection (such as sqlite3), you need to manually close the connection after the operation. It is recommended to use thewithstatement to manage it automatically:
Example
import pandas as pd
# Use the with statement; the connection is automatically closed when exiting, and it will not leak even if an exception occurs
with sqlite3.connect("mydata.db") as conn:
df = pd.read_sql("SELECT * FROM employees", con=conn)
print(df) # The connection has been automatically closed, but the df data is still available
2. Do not read all data from a large table directly
For large tables with millions of rows or more, directlySELECT *reading all the data will exhaust memory. You should prioritize filtering data at the SQL level (WHERE, LIMIT), or usechunksizechunked reading.
3. read_sql_table only supports SQLAlchemy
If you use a DB-API native connection such as sqlite3 to call itpd.read_sql_table(), an error will be raisedNotImplementedError. Please usepd.read_sql()or switch to a SQLAlchemy engine.
4. The index parameter of to_sql defaults to True
By defaultto_sqlthe DataFrame's row index (0, 1, 2...) is also written to the database, creating a column namedindexcolumn, which is usually redundant. It is recommended to explicitly passindex=False。
5. Database drivers need to be installed separately
When SQLAlchemy connects to different databases, you also need to install the corresponding driver package:
| Database | Driver package | Installation command |
|---|---|---|
| MySQL | PyMySQL | pip install pymysql |
| PostgreSQL | psycopg2 | pip install psycopg2-binary |
| SQL Server | pyodbc | pip install pyodbc |
| Oracle | cx_Oracle | pip install cx_Oracle |
| SQLite | Built-in | No installation required |