Pandas lee bases de datos SQL
Pandas ofrece un conjunto de funciones que interactúan directamente con bases de datos SQL, permitiendo leer los resultados de consultas directamente como DataFrame y también escribir DataFrames de vuelta a la base de datos. Esto permite a los analistas de datos no tener que manejar manualmente la conexión a la base de datos y el análisis de resultados, simplificando en gran medida el flujo de interacción entre la base de datos y Python.
Resumen de funciones principales
| Función | Propósito | Tipo de retorno |
|---|---|---|
pd.read_sql() |
Ejecuta consultas SQL o lee toda la tabla (función genérica compatible con ambos escenarios) | DataFrame |
pd.read_sql_query() |
Ejecuta sentencias de consulta SQL, adecuado para consultas complejas | DataFrame |
pd.read_sql_table() |
Lee directamente toda la tabla, solo compatible con conexiones SQLAlchemy | DataFrame |
DataFrame.to_sql() |
Escribe un DataFrame en una tabla de la base de datos | None / int |
En la práctica, se recomienda usar
pd.read_sql(), que automáticamente determina según los parámetros pasados si ejecutar una consulta o leer la tabla completa, con la mejor compatibilidad.pd.read_sql_table()Solo admite conexiones con motor SQLAlchemy, no admite conexiones nativassqlite3como DB-API.
Establecer conexión a la base de datos
Pandas en sí no se conecta directamente a la base de datos; es necesario establecer una conexión mediante una biblioteca de terceros y pasarla. Hay dos formas principales:Motor SQLAlchemy(recomendado) yconexión nativa DB-API(ligera y sencilla).
Método 1: SQLAlchemy (recomendado)
SQLAlchemy es el paquete de herramientas de bases de datos más utilizado en Python, compatible con todas las bases de datos principales y con la mejor compatibilidad con Pandas:
pip install sqlalchemy
Ejemplo
# Formato de cadena de conexión SQLAlchemy: tipo de base de datos+driver://usuario:contraseña@host:puerto/nombre_base_datos
# SQLite (base de datos de archivo, sin necesidad de usuario ni contraseña)
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")
# Verificar si la conexión fue exitosa
with engine.connect() as conn:
print("Conexión exitosa")
Método 2: conexión nativa sqlite3 (solo SQLite)
Módulo integrado de Pythonsqlite3, no requiere instalación adicional, ideal para bases de datos SQLite locales ligeras:
Ejemplo
# Conectar a una base de datos SQLite de archivo (se crea automáticamente si el archivo no existe)
conn = sqlite3.connect("mydata.db")
# Conectar a una base de datos en memoria (los datos desaparecen al salir del programa, ideal para pruebas)
conn_memory = sqlite3.connect(":memory:")
# Después de su uso, es necesario cerrar la conexión manualmente
# conn.close()
pd.read_sql() leer datos
Sintaxis básica
pd.read_sql(sql, con, index_col=None, coerce_float=True, params=None, parse_dates=None, columns=None, chunksize=None)
Descripción de los parámetros principales:
| Parámetro | Tipo | Descripción |
|---|---|---|
sql |
str | Sentencia SQL de consulta, o nombre de tabla (según elcontipo) |
con |
Objeto de conexión | Motor SQLAlchemy u objeto de conexión DB-API |
index_col |
str o list | Establecer las columnas especificadas como índice de filas del DataFrame |
params |
list o dict | Valores de parámetros para consultas SQL parametrizadas, previene la inyección SQL |
parse_dates |
list o dict | Analizar las columnas especificadas como tipo datetime |
chunksize |
int | Lectura por bloques; cada bloque devuelve un iterador de DataFrame con el número de filas especificado |
1. Leer toda la tabla
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Leer toda la tabla employees
df = pd.read_sql("employees", con=engine)
print(df.head())
print(f"Total {len(df)} filas, {len(df.columns)} columnas")
2. Ejecutar consultas SQL
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Consulta con condiciones
df = pd.read_sql("SELECT * FROM employees WHERE department = 'IT'", con=engine)
# Consulta con unión de múltiples tablas
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. Consultas parametrizadas (prevención de inyección SQL)
Cuando las condiciones de la consulta provienen de la entrada del usuario,asegúrese de usar consultas parametrizadas, no use concatenación de cadenas SQL:
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# ❌ Escritura peligrosa: concatenación de cadenas SQL, con riesgo de inyección SQL
dept = "IT"
# df = pd.read_sql(f"SELECT * FROM employees WHERE department = '{dept}'", engine)
# ✅ Escritura segura: usar el marcador de posición ? (sqlite3) o el parámetro nombrado :name (SQLAlchemy)
# Estilo SQLite / DB-API (usando ?)
df = pd.read_sql(
"SELECT * FROM employees WHERE department = ? AND salary > ?",
con=engine,
params=["IT", 8000] # params es una lista, reemplaza ? por posición
)
# Estilo de parámetros nombrados SQLAlchemy (usando :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. Establecer la columna índice
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Establecer la columna id de la base de datos como índice de filas del DataFrame
df = pd.read_sql("SELECT * FROM employees", con=engine, index_col="id")
print(df.head())
# Usar múltiples columnas como índice compuesto
df = pd.read_sql(
"SELECT * FROM orders",
con=engine,
index_col=["year", "month"] # Índice compuesto
)
5. Analizar columnas de fecha
Los campos de fecha almacenados en la base de datos se leen por defecto como cadenas; usandoparse_datesel parámetro se pueden analizar directamente comodatetime:
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Analizar las columnas created_at y updated_at como tipo datetime
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]
# También se puede especificar el formato de análisis (para formatos de fecha no estándar)
df = pd.read_sql(
"SELECT * FROM orders",
con=engine,
parse_dates={"created_at": "%Y%m%d"} # Analizar un formato como "20240115" como fecha
)
Lectura de grandes volúmenes de datos por bloques (chunksize)
Cuando la cantidad de datos en la tabla de la base de datos es muy grande, leer todo de una vez en memoria puede provocar OOM (desbordamiento de memoria). Usandochunksizeel parámetro se pueden leer los datos en lotes, procesando solo una parte cada vez:
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://root:password@localhost/bigdata")
# chunksize=10000 indica que cada vez se leen 10000 filas, devolviendo un iterador
chunks = pd.read_sql("SELECT * FROM large_table", con=engine, chunksize=10000)
# Procesar bloque por bloque (solo hay 10000 filas en memoria cada vez)
result_list = []
for i, chunk in enumerate(chunks):
# Ejecutar lógica de procesamiento en cada bloque (como filtrado, agregación, etc.)
processed = chunk[chunk["status"] == "active"]
result_list.append(processed)
print(f"Procesado bloque {i+1}, filas válidas en el bloque actual: {len(processed)}")
# Combinar todos los bloques procesados en un solo DataFrame
final_df = pd.concat(result_list, ignore_index=True)
print(f"Finalmente un total de {len(final_df)} filas de datos válidos")
pd.read_sql_query() y pd.read_sql_table()
pd.read_sql_query(): solo ejecutar consultas
ypd.read_sql()La funcionalidad es básicamente la misma, pero solo acepta sentencias SQL de consulta, no nombres de tabla. Los parámetros son exactamente los mismos, adecuado para escenarios donde se necesita distinguir claramente entre operaciones de "consulta" y "lectura de tabla":
Ejemplo
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(): leer toda la tabla (solo SQLAlchemy)
pd.read_sql_table()Diseñado específicamente para leer tablas completas, admite filtrar columnas y filas mediante parámetros, perosolo admite conexiones con motor SQLAlchemy, no admite conexiones nativas como sqlite3:
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Leer columnas específicas de la tabla employees
df = pd.read_sql_table(
"employees",
con=engine,
columns=["id", "name", "salary", "department"] # Leer solo estas columnas
)
# También admite especificar schema (esquema en la base de datos)
df = pd.read_sql_table(
"employees",
con=engine,
schema="hr" # Leer la tabla employees en el esquema hr
)
print(df.head())
Escribir DataFrame en la base de datos (to_sql)
UsoDataFrame.to_sql()Puede escribir el DataFrame en la base de datos, con soporte para crear nuevas tablas, agregar datos o sobrescribir la tabla original:
Sintaxis básica
DataFrame.to_sql(name, con, schema=None, if_exists='fail', index=True, index_label=None, chunksize=None, dtype=None, method=None)
if_existsEl parámetro determina el comportamiento cuando la tabla de destino ya existe:
| Valor de if_exists | Comportamiento | Escenario de uso |
|---|---|---|
'fail'(por defecto) |
Se produce un error si la tabla ya existe | Evita sobrescribir accidentalmente datos existentes |
'replace' |
Primero elimina la tabla original y luego la vuelve a crear para escribir los datos | Actualización completa de los datos |
'append' |
Agrega datos a la tabla existente sin cambiar la estructura de la tabla | Escritura incremental de nuevos datos |
Ejemplo
from sqlalchemy import create_engine
engine = create_engine("sqlite:///mydata.db")
# Preparar los datos de ejemplo
df = pd.DataFrame({
"name": ["Zhang San", "Li Si", Wang Wu],
"department": ["IT", "HR", "IT"],
"salary": [12000, 8000, 15000]
})
# Escribir el DataFrame en la tabla employees; si ya existe, reemplazarla
df.to_sql(
"employees",
con=engine,
if_exists="replace", # Sobrescribir los datos existentes
index=False # No escribir el índice de filas del DataFrame en la base de datos (normalmente no es necesario)
)
# Agregar nuevos datos a la tabla existente (nota: los nombres de columna y los tipos de datos deben coincidir)
new_employees = pd.DataFrame({
"name": ["Zhao Liu"],
"department": ["Finance"],
"salary": [11000]
})
new_employees.to_sql("employees", con=engine, if_exists="append", index=False)
# Verificar el resultado de la escritura
result = pd.read_sql("SELECT * FROM employees", con=engine)
print(result)
Especificar el tipo de dato de las columnas
Al escribir, se puede pasar el parámetrodtypepara especificar explícitamente el tipo de cada columna en la base de datos:
Ejemplo
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(), # Tipo entero
"name": String(50), # Cadena de longitud variable, con un máximo de 50 caracteres
"score": Float() # Número de punto flotante
}
)
Ejemplo de uso completo
A continuación se muestra un ejemplo completo de cómo leer datos de ventas desde una base de datos SQLite y analizarlos:
Ejemplo
import sqlite3
from sqlalchemy import create_engine
# Primer paso: preparar la base de datos de prueba
engine = create_engine("sqlite:///sales.db")
conn = sqlite3.connect("sales.db")
# Crear los datos de ejemplo y escribirlos en la base de datos
sales_data = pd.DataFrame({
"order_id": [1001, 1002, 1003, 1004, 1005, 1006],
"product": ["Portátil", "Móvil", "Tablet", "Portátil", "Móvil", "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)
# Segundo paso: leer los datos y analizar las fechas
df = pd.read_sql(
"SELECT * FROM sales",
con=engine,
parse_dates=["order_date"]
)
print("Datos originales:")
print(df)
print()
# Tercer paso: calcular las ventas por producto
sql_agg = """
SELECT product,
COUNT(*) AS num_pedidos,
SUM(amount * quantity) AS ventas_totales,
AVG(amount) AS precio_promedio
FROM sales
GROUP BY product
ORDER BY ventas_totales DESC
"""
summary = pd.read_sql(sql_agg, con=engine)
print("Resumen de ventas por producto:")
print(summary)
print()
# Cuarto paso: escribir los resultados estadísticos de vuelta en la base de datos
summary.to_sql("sales_summary", con=engine, if_exists="replace", index=False)
print("Los datos resumidos se han escrito en la tabla sales_summary")
conn.close()
El resultado de ejecutar el código anterior es:
原始数据: 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 表
Problemas comunes y advertencias
1. Las conexiones deben cerrarse después de su uso
Al usar conexiones nativas DB-API (como sqlite3), la conexión debe cerrarse manualmente después de la operación. Se recomienda usar la declaraciónwithpara gestionarla automáticamente:
Ejemplo
import pandas as pd
# Usar la declaración with: la conexión se cierra automáticamente al salir; incluso si se produce una excepción, no se filtrará la conexión
with sqlite3.connect("mydata.db") as conn:
df = pd.read_sql("SELECT * FROM employees", con=conn)
print(df) # La conexión se ha cerrado automáticamente, pero los datos de df siguen disponibles
2. No lea todos los datos de tablas grandes directamente
Para tablas grandes con más de varios millones de filas, utilizarSELECT *para leer todos los datos directamente agotará la memoria. Se debe filtrar los datos a nivel de SQL (WHERE, LIMIT) preferentemente, o utilizarchunksizela lectura por bloques.
3. read_sql_table solo admite SQLAlchemy
Si se utiliza una conexión nativa DB-API (como sqlite3) para llamar apd.read_sql_table(), se producirá el errorNotImplementedError. Por favor, utilicepd.read_sql()o cambie al motor SQLAlchemy.
4. El parámetro index de to_sql es True por defecto
Por defecto,to_sqltambién escribe el índice de filas del DataFrame (0, 1, 2...) en la base de datos, formando una columna llamadaindex, que generalmente es innecesaria. Se recomienda pasar explícitamenteindex=False。
5. El controlador de la base de datos debe instalarse por separado
Al conectar SQLAlchemy con diferentes bases de datos, también debe instalarse el paquete de controlador correspondiente:
| Base de datos | Paquete de controlador | Comando de instalación |
|---|---|---|
| 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 | Integrado | No requiere instalación |