:从建表到查询的完整实操)
1. 从一次真实的连接失败说起Pymysql 到底解决什么问题如果你刚开始用 Python 碰数据库大概率会遇到这样一个场景脚本写好了import pymysql也加上了结果一运行就报pymysql.err.OperationalError: (1045, Access denied for user rootlocalhost)或者更让人摸不着头脑的RuntimeError: cryptography is required for sha256_password。这些报错背后其实都指向同一件事——你还没有把「Python 进程」和「MySQL 服务」之间的那条通道配置正确。Pymysql 就是干这个的。它是一个纯 Python 实现的 MySQL 客户端库不需要你额外装 C 编译环境pip install pymysql一条命令就能用。它的定位很清晰让你的 Python 代码能够像在 MySQL 命令行里敲 SQL 一样去执行建表、插入、查询、更新、删除这些操作。适合谁适合刚接触数据库开发的学生、需要写数据脚本的后端新人、做数据分析想把结果落库的工程师以及任何想用 Python 快速跑通一个数据库示例的人。我试过在同一个项目里同时用 Pymysql 和 ORM 框架结论是学习阶段先用 Pymysql 把 SQL 和连接的生命周期搞清楚后面再上 ORM 会顺很多。因为 ORM 帮你隐藏的那些细节——连接、游标、提交、关闭——恰恰是出问题时你最需要理解的地方。这篇文章会带你走完一条完整的链路安装模块、建立连接、创建数据库和表、插入数据、查询数据、验证结果、排查常见报错。每一步都有可直接复制的代码你跟着敲一遍就能拥有一个能跑通的数据库操作脚本。在开始之前你需要确认两件事第一本机或远程已经有一个可用的 MySQL 服务并且你知道它的地址、端口、用户名和密码第二你的 Python 环境是 3.7 以上。这两点满足后面的内容就能直接跟做。2. 前置准备安装 Pymysql 与确认 MySQL 服务可用2.1 安装 Pymysql 模块打开终端或命令行执行pip install pymysql如果你用的是虚拟环境先激活环境再执行。安装完成后可以用下面这行代码验证版本import pymysql print(pymysql.__version__)能打印出版本号比如1.1.0就说明模块装好了。这里有个小坑有些教程会让你装mysql-connector-python或MySQLdb那是另外的库API 不一样。本文全程用 Pymysql不要混用。2.2 确认 MySQL 服务在运行在命令行里执行mysql -u root -p -h 127.0.0.1 -P 3306输入密码后如果能进入mysql提示符说明服务正常。如果提示Cant connect to MySQL server先检查 MySQL 服务是否启动。Windows 可以在服务管理器里看Linux 用systemctl status mysqlmacOS 用brew services list。2.3 关于连接参数的说明Pymysql 的connect()方法接受多个参数常用的有参数含义示例host数据库地址127.0.0.1port端口3306user用户名rootpassword密码你的密码database默认数据库pythondbcharset字符编码utf8mb4这里我建议 charset 用utf8mb4而不是utf8。原因是 MySQL 的utf8实际上最多只支持 3 字节存不了 emoji 和一些生僻字utf8mb4才是真正的完整 UTF-8。这个细节在插入中文或特殊符号时特别重要后面排错章节会再提到。2.4 一个容易被忽略的点密码认证插件MySQL 8.0 默认使用caching_sha2_password认证插件而 Pymysql 在旧版本里对这个插件的支持需要额外的cryptography包。如果你连接时报RuntimeError: cryptography is required执行pip install cryptography装完再试。这不是 Pymysql 的 bug而是认证方式升级带来的依赖变化。3. 可复制配置从连接到建表的完整代码这一节是全文的核心我会把连接配置、建库建表、增删改查的代码完整写出来你可以直接复制到一个.py文件里运行。为了让配置更清晰我先给一个连接参数的写法再给完整脚本。3.1 连接配置的两种写法第一种是直接传参import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, password你的密码, charsetutf8mb4 )第二种是用字典解包适合把配置抽出来import pymysql DB_CONFIG { host: 127.0.0.1, port: 3306, user: root, password: 你的密码, charset: utf8mb4, autocommit: False } conn pymysql.connect(**DB_CONFIG)我推荐第二种因为后面你要改地址或密码时只改一处。autocommit设为 False 是默认行为意味着你需要手动commit()这样能更清楚地看到事务的边界。3.2 创建数据库和表Pymysql 连接时如果不指定database就连到了 MySQL 实例本身此时可以执行CREATE DATABASE。代码如下import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, password你的密码, charsetutf8mb4 ) cur conn.cursor() cur.execute(CREATE DATABASE IF NOT EXISTS pythondb DEFAULT CHARACTER SET utf8mb4;) conn.commit() cur.execute(USE pythondb;) create_table_sql CREATE TABLE IF NOT EXISTS testpy ( id INT(10) PRIMARY KEY AUTO_INCREMENT, name VARCHAR(10), age INT UNSIGNED, sex VARCHAR(2) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; cur.execute(create_table_sql) conn.commit() cur.close() conn.close()注意CREATE DATABASE IF NOT EXISTS和CREATE TABLE IF NOT EXISTS这两个写法它们让脚本可以重复运行而不会因为对象已存在报错。这是写可重复执行脚本的基本习惯。3.3 插入数据的两种方式单条插入cur.execute(INSERT INTO testpy VALUES(0, ee, 23, 女);) conn.commit()批量插入用executemany效率更高sql INSERT INTO testpy VALUES(%s, %s, %s, %s); data [ (0, aa, 3, 女), (0, 晨晨, 2, 男), (0, cc, 89, 女) ] cur.executemany(sql, data) conn.commit()这里%s是 Pymysql 的参数占位符不管你插入的是字符串还是数字都用%s。Pymysql 会自动处理转义这比手动拼字符串安全得多能有效避免 SQL 注入。3.4 查询数据的三种方法cur.execute(SELECT * FROM testpy;) # 方法一fetchone逐条取 while True: row cur.fetchone() if row is None: break print(row) # 方法二fetchall一次取全部 cur.execute(SELECT * FROM testpy;) rows cur.fetchall() for row in rows: print(row) # 方法三fetchmany取指定条数 cur.execute(SELECT * FROM testpy;) rows cur.fetchmany(10) for row in rows: print(row)有一个关键点必须强调fetchone、fetchall、fetchmany都是从当前游标位置往后取的。如果你先调用了fetchall游标已经到末尾再调fetchone就会返回 None。所以每次重新查询前要么重新execute要么用cur.scroll(0, modeabsolute)把游标移回开头。3.5 更新和删除cur.execute(UPDATE testpy SET age %s WHERE name %s;, (24, ee)) conn.commit() cur.execute(DELETE FROM testpy WHERE name %s;, (cc,)) conn.commit()更新和删除后一定要commit()否则数据不会真正写入。这是新手最容易忘的一步。4. 验证请求跑通脚本并确认结果4.1 完整可运行脚本把上面的片段拼起来形成一个完整脚本demo.pyimport pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, password你的密码, charsetutf8mb4 ) try: cur conn.cursor() cur.execute(CREATE DATABASE IF NOT EXISTS pythondb DEFAULT CHARACTER SET utf8mb4;) conn.commit() cur.execute(USE pythondb;) cur.execute( CREATE TABLE IF NOT EXISTS testpy ( id INT(10) PRIMARY KEY AUTO_INCREMENT, name VARCHAR(10), age INT UNSIGNED, sex VARCHAR(2) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; ) conn.commit() cur.execute(TRUNCATE TABLE testpy;) conn.commit() sql INSERT INTO testpy VALUES(%s, %s, %s, %s); data [ (0, aa, 3, 女), (0, 晨晨, 2, 男), (0, cc, 89, 女) ] cur.executemany(sql, data) conn.commit() cur.execute(SELECT * FROM testpy;) rows cur.fetchall() print(查询结果) for row in rows: print(row) finally: cur.close() conn.close()运行python demo.py你应该看到类似输出查询结果 (1, aa, 3, 女) (2, 晨晨, 2, 男) (3, cc, 89, 女)4.2 用 MySQL 命令行交叉验证为了确认数据真的落库了另开一个终端进入 MySQLmysql -u root -p然后执行USE pythondb; SELECT * FROM testpy;如果看到和 Python 输出一致的三行数据说明整条链路是通的。这种「Python 写入 命令行读取」的交叉验证方式比只看 Python 打印结果更可靠因为它排除了游标缓存带来的假象。4.3 关于关闭顺序脚本里finally块中先cur.close()再conn.close()这个顺序不能反。游标依附于连接连接关了再关游标会报错。虽然有些版本容错但养成正确顺序的习惯没坏处。5. 本篇常见报错排查401、local proxy failed、reading choices 等这一节把新手最常撞到的几个报错集中处理。每个报错我都给出触发场景和解决方向。5.1 Access denied1045完整报错pymysql.err.OperationalError: (1045, Access denied for user rootlocalhost (using password: YES))原因通常是密码错了或者该用户不允许从当前主机连接。解决确认密码如果是从远程连检查 MySQL 用户表里root%是否存在或者新建一个允许远程的用户。注意不要在生产环境随意开放 root 远程权限。5.2 cryptography is required完整报错RuntimeError: cryptography is required for sha256_password or caching_sha2_password这是 MySQL 8.0 认证插件导致的。执行pip install cryptography即可。如果装完还报检查是不是装到了别的 Python 环境里。5.3 local proxy failed 类连接错误有些环境会报local proxy failed或Cant connect to MySQL server on 127.0.0.1。这类错误和 Pymysql 本身无关是网络层没通。检查MySQL 服务是否启动、端口是否被防火墙拦、host 写的是不是localhost而 MySQL 只监听127.0.0.1。把 host 从localhost改成127.0.0.1有时能直接解决因为localhost可能走 socket 而127.0.0.1走 TCP。5.4 reading choices 相关错误如果你在查询时看到类似reading choices或结果集读取异常多半是游标位置问题。前面说过fetchall之后游标在末尾再fetchone就是 None。解决每次查询前重新execute或用cur.scroll(0, modeabsolute)复位。5.5 OAuth 与认证类报错有些托管数据库会要求 OAuth 或 token 认证此时用用户名密码直连会失败。这类场景需要按服务商文档获取临时凭证再作为 password 传入。如果你用的是本地 MySQL一般不会遇到。5.6 中文乱码插入中文后查询显示???或乱码检查三处连接 charset 是否为utf8mb4、建表时DEFAULT CHARSET是否为utf8mb4、MySQL 服务端character_set_server是否为utf8mb4。三处统一乱码基本消失。5.7 忘记 commit现象是 Python 里查询能看到数据但 MySQL 命令行里看不到。原因是没commit()数据还在事务里没落盘。加上conn.commit()即可。6. 语义一致 CTA把脚本跑通之后可以做什么脚本跑通只是起点。接下来你可能会想让 Python 去调用大模型做数据处理比如把查询结果交给模型分析或者让模型生成 SQL。这类场景需要一个稳定的 API 入口。你可以到 TaoToken API Keys 创建一个 Key然后在代码里用标准 HTTP 请求调用。如果你只是想先试试模型对话效果可以直接打开 模型对话 页面输入一段自然语言让它帮你写建表语句再复制回 Pymysql 脚本里执行。这个流程对刚学 SQL 的人特别友好。要是你打算长期做编码或 Agent 类项目比如让程序自动根据表结构生成增删改查代码可以看看 Coding Plan它更适合持续性的开发任务。接入细节在 接入文档 里有完整说明控制台在 Console。回到数据库本身我建议你下一步做两件事一是把连接配置抽成独立的config.py避免密码硬编码在业务脚本里二是给查询加上异常捕获把pymysql.err.OperationalError和pymysql.err.IntegrityError分开处理。这两步做完你的脚本就从「能跑」变成「能用」了。