Pandas如何连接数据库_read_sql()与SQLAlchemy读取MySQL数据

1次阅读

根本原因是未传入有效的数据库连接对象;pd.read_sql()需SQLAlchemy Engine实例,而非URL字符串或Connection对象,且须确保字符集(utf8mb4)、时区配置正确,并用chunksize分块处理大表。

Pandas如何连接数据库_read_sql()与SQLAlchemy读取MySQL数据

为什么 pd.read_sql()mysql 总是报 TypeError: 'NoneType' Object is not callable

根本原因是没传对数据库连接对象——pd.read_sql() 要的是一个能执行 SQL 的“连接句柄”,不是 SQLAlchemy 的 EngineConnection 对象本身,更不是字符串 URL。

常见错误写法:pd.read_sql("select * FROM users", "mysql://user:pass@localhost/db") —— 这里第二个参数是 URL 字符串,但 read_sql() 不会自动解析它,直接当连接对象用就崩了。

  • 正确做法:必须先用 SQLAlchemy 创建 Engine,再把它传进去(Engine 支持 connect() 方法,所以被接受)
  • 不推荐用 create_engine(...).connect() 后的结果传入:那是 Connection 对象,虽然能用,但容易漏关连接、引发连接泄漏
  • 如果用的是 PyMySQL 或 mysqlclient 底层驱动,确保已安装对应包(pip install PyMySQL),否则 create_engine 会静默 fallback 到不兼容的驱动
from sqlalchemy import create_engine import pandas as pd <p>engine = create_engine("mysql+pymysql://user:pass@localhost:3306/db") df = pd.read_sql("SELECT id, name FROM users LIMIT 10", engine)

read_sql_query()read_sql_table() 该选哪个?

二者底层都调用 read_sql(),但语义和限制不同,选错会导致意外行为或报错。

  • read_sql_query():只接受 SQL 查询字符串,比如 SELECT、带 WHERE 或子查询的语句;不能填表名,否则报 DatabaseError: Execution failed on sql...
  • read_sql_table():只接受表名(字符串),内部拼 SELECT * FROM {table};不支持 JOIN、WHERE、别名,也不能读视图(部分数据库不支持)
  • 性能上没本质区别,但 read_sql_table() 在某些方言下会额外查元数据(比如字段类型),略慢一点;而复杂查询必须用 read_sql_query()

如果你要加条件、分页、聚合,老老实实用 read_sql_query();如果只是全量导出一张小表,read_sql_table() 写起来少几个字符,但别指望它更高效。

MySQL 中文乱码、datetime 字段变 NaT 怎么办?

这不是 Pandas 的锅,是连接层编码和时区没对齐。SQLAlchemy 默认不强制设置字符集和时区,MySQL 客户端协议一松懈,数据就变形。

  • 在连接 URL 末尾加上 ?charset=utf8mb4(不是 utf8!MySQL 的 utf8 实际是 utf8mb3,不支持 emoji)
  • 显式指定时区:URL 加 &timezone=UTC,或创建 engine 时传 connect_args={"timezone": "UTC"}
  • 如果 MySQL 服务端时区是 +08:00,而 Python 环境默认 UTC,DATETIME 字段可能被错误转换成 NaT,尤其配合 parse_dates 参数时
  • pd.read_sql(..., parse_dates=["created_at"])NULL 值敏感,字段含空值时建议先用 dtype 指定为 String,再手动转
engine = create_engine(     "mysql+pymysql://user:pass@localhost:3306/db?charset=utf8mb4&timezone=UTC" )

大表怎么读才不 OOM?

直接 read_sql("SELECT * FROM huge_table", engine) 很容易把内存吃光,尤其字段多、文本长的时候。Pandas 本身不支持流式读取,得靠分块绕过去。

  • chunksize 参数(单位是行数),返回的是 TextFileReader 迭代器,每次只载入一块:
  • 别用 for df in pd.read_sql(..., chunksize=10000) 然后拼 pd.concat——这等于又全加载进内存了
  • 真正省内存的做法:每块单独处理(比如清洗后存 CSV / 写数据库 / 统计中间结果),不保留原始块
  • 注意:chunksizeread_sql_table() 有效,但对含 ORDER BYLIMITread_sql_query() 无效(SQL 层无法分块)

如果真要分页查大表,得自己写带 OFFSET/LIMIT循环,或者用主键范围(如 id BETWEEN ? AND ?)来切片,避免深分页性能坍塌。

text=ZqhQzanResources