Python 链接数据库与基础增删改查(CRUD)操作详解
一、环境准备
1.1 核心环境与工具
Python 版本:推荐 Python 3.8+(本文使用 Python 3.10,兼容性更强)。
MySQL 数据库:推荐 MySQL 8.0 或 MariaDB 10.5+(需提前启动服务)。
开发工具:PyCharm、VS Code(配 Python 插件)或 Jupyter Notebook。
数据库管理工具:Navicat、SQLyog 或 PHPMyAdmin,用于执行 SQL 语句和管理数据。
核心依赖:
pymysql(Python 连接 MySQL 的第三方库,轻量且常用)。
1.2 依赖安装
pymysql:pip install pymysql
import pymysql # 无报错则安装成功
1.3 测试数据库与表创建
python_db 和用户表 users:-- 1. 创建数据库(若不存在),指定 UTF-8 编码CREATE DATABASE IF NOT EXISTS python_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;-- 2. 使用数据库USE python_db;-- 3. 创建用户表(存储用户ID、姓名、年龄、邮箱、注册时间)CREATE TABLE IF NOT EXISTS users ( id INT PRIMARY KEY AUTO_INCREMENT, -- 主键自增 name VARCHAR(50) NOT NULL, -- 姓名,非空 age TINYINT UNSIGNED, -- 年龄(0-255,无符号) email VARCHAR(100) UNIQUE NOT NULL,-- 邮箱,唯一且非空 create_time DATETIME DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认当前时间) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
二、Python 链接数据库的核心:pymysql 用法
pymysql 是 Python 操作 MySQL 的主流库,兼容 MySQL 5.5+ 版本,支持 SQL 执行、事务、预处理语句等核心功能。链接数据库的核心步骤的是:建立连接 → 创建游标 → 执行 SQL → 处理结果 → 关闭资源。2.1 核心连接参数
host:数据库地址(本地为localhost,远程为服务器 IP)。port:MySQL 端口号(默认 3306)。user:MySQL 用户名(如root)。password:MySQL 密码(你的登录密码)。database:要连接的数据库名(本文为python_db)。charset:字符编码(必须为utf8mb4,支持中文和 emoji)。
2.2 封装数据库工具类(推荐)
import pymysqlfrom pymysql.err import OperationalError, ProgrammingError, IntegrityErrorclass MySQLDB:
"""MySQL 数据库工具类:封装连接、CRUD 操作、资源关闭"""
def __init__(self, host='localhost', port=3306, user='root', password='123456', database='python_db', charset='utf8mb4'):
# 初始化连接参数
self.host = host
self.port = port
self.user = user
self.password = password
self.database = database
self.charset = charset
self.conn = None # 数据库连接对象
self.cursor = None # 游标对象(执行 SQL 用)
def connect(self):
"""建立数据库连接"""
try:
self.conn = pymysql.connect(
host=self.host,
port=self.port,
user=self.user,
password=self.password,
database=self.database,
charset=self.charset,
cursorclass=pymysql.cursors.DictCursor # 游标返回字典格式(键为字段名)
)
self.cursor = self.conn.cursor()
print("数据库连接成功!")
except OperationalError as e:
print(f"数据库连接失败:{e}")
raise # 抛出异常,由调用者处理
def close(self):
"""关闭游标和连接(避免资源泄漏)"""
try:
if self.cursor:
self.cursor.close()
if self.conn and self.conn.open:
self.conn.close()
print("数据库连接已关闭!")
except Exception as e:
print(f"关闭资源失败:{e}")
def execute_sql(self, sql, params=None):
"""
执行 SQL 语句(insert/update/delete)
:param sql: SQL 语句(使用 %s 作为占位符)
:param params: SQL 参数(元组/列表,如 (name, age))
:return: 受影响的行数
"""
try:
self.connect() # 建立连接
affected_rows = self.cursor.execute(sql, params) # 执行 SQL(params 避免 SQL 注入)
self.conn.commit() # 提交事务(增删改必须提交)
print(f"SQL 执行成功,受影响行数:{affected_rows}")
return affected_rows except (ProgrammingError, IntegrityError) as e:
self.conn.rollback() # 执行失败,回滚事务
print(f"SQL 执行失败:{e}")
raise
finally:
self.close() # 无论成功失败,都关闭资源
def query_sql(self, sql, params=None, fetch_all=True):
"""
执行查询 SQL(select)
:param sql: 查询 SQL 语句
:param params: SQL 参数
:param fetch_all: 是否查询所有结果(True 返回全部,False 返回单条)
:return: 查询结果(字典列表/单个字典)
"""
try:
self.connect()
self.cursor.execute(sql, params)
if fetch_all:
result = self.cursor.fetchall() # 获取所有结果
else:
result = self.cursor.fetchone() # 获取单条结果
print(f"查询成功,共 {len(result) if fetch_all else 1} 条数据" if result else "查询无结果")
return result except ProgrammingError as e:
print(f"查询失败:{e}")
raise
finally:
self.close()关键说明:
cursorclass=pymysql.cursors.DictCursor:设置游标返回格式为字典(如{'id': 1, 'name': '张三'}),比默认元组更直观。事务处理:
execute_sql中执行commit()(提交事务)和rollback()(回滚事务),避免数据不一致。占位符:使用
%s作为 SQL 占位符(注意:不是 Python 的格式化字符串%),通过params传递参数,防止 SQL 注入。资源关闭:
finally块中强制关闭游标和连接,避免资源泄漏。
三、基础增删改查(CRUD)操作实现
MySQLDB 工具类,我们可以简洁地实现 users 表的增删改查操作,无需重复编写连接和关闭逻辑。3.1 新增操作(Create)
users 表插入一条新用户数据。if __name__ == "__main__":
# 初始化数据库工具类(替换为你的 MySQL 账号密码)
db = MySQLDB(user='root', password='123456')
# 1. 新增用户
try:
sql = "INSERT INTO users (name, age, email) VALUES (%s, %s, %s)"
params = ("张三", 24, "zhangsan@example.com") # 对应 SQL 中的 %s 占位符
affected_rows = db.execute_sql(sql, params)
print(f"新增用户成功,受影响行数:{affected_rows}")
except Exception as e:
print(f"新增失败:{e}")注意:params必须是元组或列表,即使只有一个参数,也需写成(param,)(末尾加逗号),避免语法错误。
3.2 查询操作(Read)
fetch_all 参数控制。3.2.1 查询单条数据(按 ID 查询)
if __name__ == "__main__":
db = MySQLDB(user='root', password='123456')
# 2. 按 ID 查询单条用户
try:
sql = "SELECT id, name, age, email, create_time FROM users WHERE id = %s"
params = (1,) # 查询 ID=1 的用户
user = db.query_sql(sql, params, fetch_all=False) # fetch_all=False 返回单条
if user:
print("查询到的用户信息:")
print(f"ID:{user['id']}")
print(f"姓名:{user['name']}")
print(f"年龄:{user['age']}")
print(f"邮箱:{user['email']}")
print(f"注册时间:{user['create_time']}")
else:
print("未查询到该用户")
except Exception as e:
print(f"查询失败:{e}")3.2.2 查询多条数据(查询所有用户)
if __name__ == "__main__":
db = MySQLDB(user='root', password='123456')
# 3. 查询所有用户(按注册时间倒序)
try:
sql = "SELECT id, name, age, email FROM users ORDER BY create_time DESC"
users = db.query_sql(sql, fetch_all=True) # fetch_all=True 返回全部
if users:
print("所有用户列表:")
for idx, user in enumerate(users, 1):
print(f"序号 {idx}:ID={user['id']},姓名={user['name']},邮箱={user['email']}")
else:
print("暂无用户数据")
except Exception as e:
print(f"查询失败:{e}")3.3 修改操作(Update)
if __name__ == "__main__":
db = MySQLDB(user='root', password='123456')
# 4. 修改用户信息(修改 ID=1 的用户)
try:
sql = "UPDATE users SET name = %s, age = %s WHERE id = %s"
params = ("张三_更新", 25, 1) # 新姓名、新年龄、目标 ID
affected_rows = db.execute_sql(sql, params)
if affected_rows > 0:
print("用户信息修改成功!")
else:
print("修改失败:未找到用户或数据未变更")
except Exception as e:
print(f"修改失败:{e}")3.4 删除操作(Delete)
if __name__ == "__main__":
db = MySQLDB(user='root', password='123456')
# 5. 删除用户(删除 ID=1 的用户)
try:
sql = "DELETE FROM users WHERE id = %s"
params = (1,)
affected_rows = db.execute_sql(sql, params)
if affected_rows > 0:
print(f"删除成功!已删除 ID=1 的用户")
else:
print("删除失败:未找到该用户")
except Exception as e:
print(f"删除失败:{e}")四、关键注意事项与安全优化
4.1 防 SQL 注入(重中之重)
使用参数绑定:严禁直接拼接 SQL 字符串(如下错误示范),必须通过
params参数传递值,pymysql会自动转义特殊字符:python运行# 错误示范:直接拼接参数,存在 SQL 注入风险name = "张三' OR 1=1 --"sql = f"SELECT * FROM users WHERE name = '{name}'" # 恶意参数会导致查询所有用户过滤用户输入:对用户提交的参数(如表单、接口请求)进行合法性校验(如年龄必须是数字、邮箱格式正确),减少恶意参数传入。
4.2 常见问题排查
- 连接失败:
检查 MySQL 服务是否启动(可通过
net start mysql启动)。确认
host、port、user、password正确(尤其是 MySQL 8.0+ 需注意密码认证方式)。若报 “Access denied”,可能是用户无权限访问
python_db数据库,需授权:sqlGRANT ALL PRIVILEGES ON python_db.* TO 'root'@'localhost' IDENTIFIED BY '123456';FLUSH PRIVILEGES;
- 中文乱码:
数据库、表、字段的编码必须为
utf8mb4(已在创建时指定)。MySQLDB初始化时已设置charset='utf8mb4',确保连接编码一致。若查询结果中文乱码,检查 Python 脚本文件编码是否为 UTF-8(PyCharm 默认是)。
- 事务提交问题:
增删改操作必须执行
conn.commit(),否则数据不会写入数据库(工具类已封装)。执行失败时需
rollback()回滚事务,避免数据不一致(工具类已处理)。
4.3 生产环境优化建议
- 配置文件分离:将数据库连接参数(
host、user、password)放在独立配置文件(如config.py)中,避免硬编码:python运行# config.pyDB_CONFIG = { 'host': 'localhost', 'port': 3306, 'user': 'prod_user', # 生产环境使用低权限用户 'password': 'StrongPassword123!', # 复杂密码 'database': 'python_db', 'charset': 'utf8mb4'}使用时导入:python运行from config import DB_CONFIG db = MySQLDB(**DB_CONFIG) # 解包字典参数
- 错误处理优化:生产环境中避免直接打印异常信息(泄露敏感数据),应使用日志模块(如
logging)记录错误,向用户返回友好提示:python运行import logging# 配置日志logging.basicConfig( filename='db_error.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')try: db.execute_sql(sql, params)except Exception as e: logging.error(f"SQL 执行失败:{e}") # 记录日志 print("系统繁忙,请稍后再试~") # 友好提示 - 使用数据库连接池:高并发场景下,频繁创建 / 关闭数据库连接会消耗大量资源。推荐使用
DBUtils库的连接池,复用连接提升性能:bash运行pip install DBUtils # 安装连接池库
改造工具类(核心代码):python运行from DBUtils.PooledDB import PooledDBclass MySQLPoolDB: def __init__(self, **config): self.pool = PooledDB( creator=pymysql, # 数据库驱动 maxconnections=10, # 最大连接数 **config ) def connect(self): self.conn = self.pool.connection() # 从连接池获取连接 self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
- 限制数据库用户权限:生产环境中,Python 连接数据库的用户仅授予
SELECT、INSERT、UPDATE、DELETE必要权限,禁止DROP、ALTER等高危权限,降低风险。
五、扩展:其他数据库支持
PostgreSQL:使用
psycopg2库(用法与pymysql类似)。SQLite:Python 内置
sqlite3库(无需额外安装,适合轻量级应用)。Oracle:使用
cx_Oracle库。MongoDB(非关系型):使用
pymongo库。
六、总结
pymysql 操作 MySQL 数据库的核心流程,重点掌握:环境准备:安装
pymysql依赖,创建测试数据库和表。工具类封装:统一管理连接、SQL 执行、资源关闭,减少冗余代码。
CRUD 实现:使用参数绑定(
%s占位符)实现增删改查,避免 SQL 注入。安全与优化:分离配置文件、记录日志、使用连接池,适配生产环境。