{T}

Python 关系型数据库操作完全指南

概述

Python 通过统一的 DB-API 2.0 接口操作各类关系型数据库。本专题聚焦两大主流方案:

方案架构适用场景
第一部分 · SQLite嵌入式,单文件本地存储、桌面/移动应用、测试与原型、低并发 Web
第二部分 · MySQL客户端-服务器高并发 Web、生产级业务系统、海量数据

二者都遵循 PEP 249 — DB-API 2.0 标准,均使用参数化查询防止 SQL 注入,都支持事务管理。

第一部分:SQLite(嵌入式)


title: Python 操作 SQLite 完全指南 description: 深入掌握 Python 内置 sqlite3 模块,涵盖连接管理、游标操作、参数化查询、事务控制、行工厂、上下文管理器与生产级最佳实践。 version: 1.0 author: 文档维护组 created: 2026-06-05 updated: 2026-06-05 status: 正式

概述

SQLite 是一种 嵌入式关系型数据库引擎,它的数据库就是一个独立的 .db 磁盘文件。与 MySQL、PostgreSQL 等服务端数据库不同,SQLite 不需要独立的服务器进程——应用程序直接读写数据库文件,所有操作都在调用进程内完成。

图表渲染中…

适用场景:

场景说明
本地存储桌面应用、移动 App、浏览器(Chrome/Safari 内部大量使用 SQLite)
嵌入式设备IoT 设备、路由器配置文件存储
测试与原型无需搭建数据库服务器,快速验证 SQL 逻辑
轻量 Web 应用单用户或低并发场景(如个人博客、小型工具站)
数据分析中间存储将中间计算结果持久化,避免重复计算

不适用场景:

场景替代方案
高并发写入MySQL、PostgreSQL
海量数据(TB 级)PostgreSQL、分布式数据库
需要网络访问几乎所有服务端数据库
DB-API 2.0 规范

Python 定义了 PEP 249 — Python Database API Specification v2.0,所有 Python 数据库驱动(sqlite3、mysql-connector-python、PyMySQL、psycopg2 等)都遵循相同的接口约定:

图表渲染中…

核心对象模型:

对象职责获取方式
Connection管理数据库连接、事务控制sqlite3.connect(db_path)
Cursor执行 SQL 语句、遍历结果集conn.cursor()
Row结果集的单行表示cursor.fetchone() / fetchall()

标准操作流程:

图表渲染中…

连接管理

基础连接
python
import sqlite3

# 若文件不存在,自动创建
conn = sqlite3.connect('data.db')

connect() 的完整签名:

code
sqlite3.connect(
    database,           # 数据库文件路径,或 ':memory:' 创建内存数据库
    timeout=5.0,        # 数据库被锁时等待的时间(秒)
    detect_types=0,     # 是否自动检测列类型
    isolation_level='', # 事务隔离级别(见下文)
    check_same_thread=True,  # 是否检查线程安全
    uri=False,          # 是否将 database 参数解释为 URI
)
使用上下文管理器

Python 3.x 的 sqlite3.Connection 支持 with 语句,退出时自动提交或回滚

python
import sqlite3

# 推荐写法:自动管理事务
with sqlite3.connect('data.db') as conn:
    cursor = conn.cursor()
    cursor.execute('CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT)')
    cursor.execute('INSERT INTO users(name) VALUES(?)', ('Alice',))
    # with 块结束时自动 commit;若抛异常则自动 rollback

注意: with conn 只管理事务(提交/回滚),不会自动关闭连接。连接在 conn.close() 或离开 with 块后仍存活,直到被 GC 回收或显式关闭。生产环境中建议手动 conn.close()

使用 with 管理 Cursor
python
with sqlite3.connect('data.db') as conn:
    with conn:
        # 双层 with:
        #   conn.__exit__  → commit/rollback
        #   conn.cursor().__exit__ → close cursor
        conn.execute('CREATE TABLE IF NOT EXISTS log(ts TEXT, msg TEXT)')
        conn.execute('INSERT INTO log VALUES(datetime("now"), ?)', ('service started',))

快捷方式: Connection.execute()cursor().execute() 的简写,会自动创建并关闭游标。

核心操作

CRUD 全流程
图表渲染中…

####### 建表与插入

python
import sqlite3

with sqlite3.connect('school.db') as conn:
    conn.execute('''
        CREATE TABLE IF NOT EXISTS student (
            id    INTEGER PRIMARY KEY AUTOINCREMENT,
            name  TEXT    NOT NULL,
            age   INTEGER CHECK(age > 0 AND age < 150),
            score REAL    DEFAULT 0.0
        )
    ''')

    # 单行插入
    conn.execute(
        'INSERT INTO student(name, age, score) VALUES(?, ?, ?)',
        ('张三', 20, 88.5)
    )

    # 多行批量插入
    students = [
        ('李四', 22, 91.0),
        ('王五', 19, 76.5),
        ('赵六', 21, 85.0),
    ]
    conn.executemany('INSERT INTO student(name, age, score) VALUES(?, ?, ?)', students)

    print(f'插入了 {conn.execute("SELECT COUNT(*) FROM student").fetchone()[0]} 条记录')

####### 查询

python
import sqlite3

with sqlite3.connect('school.db') as conn:
    # 在连接上启用 Row 工厂,使结果可以通过列名访问
    conn.row_factory = sqlite3.Row

    # 条件查询
    rows = conn.execute(
        'SELECT id, name, age, score FROM student WHERE score >= ? ORDER BY score DESC',
        (80,)
    ).fetchall()

    for row in rows:
        # 使用列名访问,比按索引更可读
        print(f'{row["id"]:>3} | {row["name"]:<6} | {row["age"]:>3} | {row["score"]:>5}')

row_factory 的常见配置:

工厂效果示例
默认 tuple按位置索引row[0]
sqlite3.Row按列名或索引访问row['name']row[0]
自定义 dictlambda c, r: dict(zip([col[0] for col in c.description], r))row['name']
namedtuple按属性访问row.name

####### 更新与删除

python
with sqlite3.connect('school.db') as conn:
    # 更新
    affected = conn.execute(
        'UPDATE student SET score = score + 5 WHERE score < 80'
    ).rowcount
    print(f'更新了 {affected} 行')

    # 删除
    affected = conn.execute(
        'DELETE FROM student WHERE id = ?', (3,)
    ).rowcount
    print(f'删除了 {affected} 行')
结果集获取方法
方法返回值适用场景
fetchone()单行或 None按需逐行读取,内存友好
fetchmany(size)最多 size 行列表分批处理大数据集
fetchall()所有行列表小数据集一次性加载
迭代 Cursor生成器逐行产出推荐 — 内存最优
python
# 推荐:迭代 Cursor 逐行处理,不一次性加载到内存
with sqlite3.connect('large.db') as conn:
    for row in conn.execute('SELECT * FROM huge_table'):
        process(row)

SQL 注入与参数化查询

问题:字符串拼接的危险
python
# ❌ 危险:字符串拼接,攻击者输入 ' OR 1=1 --' 即可绕过认证
user_input = "admin' OR '1'='1"
sql = f"SELECT * FROM users WHERE name='{user_input}'"
# 实际执行的 SQL:
# SELECT * FROM users WHERE name='admin' OR '1'='1'
# 返回所有用户!
python
# ✅ 安全:参数化查询,数据库驱动自动转义
cursor.execute('SELECT * FROM users WHERE name=?', (user_input,))
SQLite 参数占位符
风格占位符示例
问号风格 (qmark)?execute('... WHERE a=? AND b=?', (1, 2))
命名风格 (named):nameexecute('... WHERE a=:a AND b=:b', {'a':1,'b':2})
数字风格 (numeric):1execute('... WHERE a=:1 AND b=:2', (1, 2))

SQLite 原生支持 qmark、named、numeric 三种风格;MySQL 使用 %s;PostgreSQL 使用 %s$1, $2。参数化查询的关键原则是:绝不用字符串拼接构造 SQL

事务管理

SQLite 的隐式事务
图表渲染中…

SQLite 默认开启隐式事务:任何 INSERT/UPDATE/DELETE 语句都会自动启动一个事务,直到显式 commit()rollback()

显式事务控制
python
import sqlite3

with sqlite3.connect('bank.db') as conn:
    # 转账操作:必须同时成功或同时失败

    # 方案一:双层 with 自动管理
    with conn:
        conn.execute('UPDATE accounts SET balance = balance - 100 WHERE id = ?', (1,))
        conn.execute('UPDATE accounts SET balance = balance + 100 WHERE id = ?', (2,))
        # 离开 with conn 块:自动 commit
        # 若任何一步抛异常:自动 rollback

    # 方案二:手动控制
    try:
        conn.execute('UPDATE accounts SET balance = balance - 100 WHERE id = ?', (1,))
        # 模拟中间出错
        # raise ValueError('模拟失败')
        conn.execute('UPDATE accounts SET balance = balance + 100 WHERE id = ?', (2,))
        conn.commit()
        print('转账成功')
    except Exception as e:
        conn.rollback()
        print(f'转账失败,已回滚: {e}')
隔离级别
参数值含义
isolation_level=None自动提交模式(每条语句自动 commit)
isolation_level=''标准模式(隐式事务 + 显式 commit)
isolation_level='DEFERRED'延迟事务(SQLite 默认,加锁延迟到实际写入时)
isolation_level='IMMEDIATE'立即事务(连接时立即获取写锁)
isolation_level='EXCLUSIVE'排他事务(连接时独占整个数据库)
python
# 自动提交模式:每条语句立即生效
conn = sqlite3.connect('data.db', isolation_level=None)

# WAL 模式 + 立即事务:高并发场景推荐
conn = sqlite3.connect('data.db', isolation_level='IMMEDIATE')
conn.execute('PRAGMA journal_mode=WAL')  # 启用 Write-Ahead Logging

高级特性

回滚数据保留
python
import sqlite3

with sqlite3.connect('analytics.db') as conn:
    # 开启 rollback 日志(可选)
    conn.execute('PRAGMA journal_mode=WAL')

    # 通过隔离 savepoint 保留部分回滚数据
    conn.execute("SAVEPOINT before_batch")
    try:
        conn.execute("INSERT INTO events VALUES(?, ?)", ("page_view", 100))
        conn.execute("INSERT INTO events VALUES(?, ?)", ("click", 50))
        # ...
        conn.execute("RELEASE before_batch")  # 确认保存点
    except Exception:
        conn.execute("ROLLBACK TO before_batch")  # 只回滚到保存点
        conn.execute("INSERT INTO error_log VALUES(datetime('now'), 'batch_failed')")
自定义函数注册

SQLite 允许将 Python 函数注册为 SQL 函数:

python
import sqlite3
import hashlib

def md5(text):
    return hashlib.md5(text.encode()).hexdigest()

with sqlite3.connect(':memory:') as conn:
    conn.create_function('md5', 1, md5)
    result = conn.execute("SELECT md5('hello')").fetchone()[0]
    print(result)  # 5d41402abc4b2a76b9719d911017c592
聚合函数
python
class Median:
    """自定义聚合函数:计算中位数"""
    def __init__(self):
        self.values = []

    def step(self, value):
        if value is not None:
            self.values.append(value)

    def finalize(self):
        if not self.values:
            return None
        self.values.sort()
        n = len(self.values)
        mid = n // 2
        return self.values[mid] if n % 2 else (self.values[mid-1] + self.values[mid]) / 2

with sqlite3.connect(':memory:') as conn:
    conn.create_aggregate('median', 1, Median)
    conn.execute('CREATE TABLE scores(score REAL)')
    conn.executemany('INSERT INTO scores VALUES(?)', [(85,), (90,), (78,), (92,), (88,)])
    median = conn.execute('SELECT median(score) FROM scores').fetchone()[0]
    print(f'中位数: {median}')  # 88.0
备份与恢复
python
import sqlite3

def backup_database(source_path, dest_path):
    """在线备份 SQLite 数据库"""
    source = sqlite3.connect(source_path)
    dest = sqlite3.connect(dest_path)
    with dest:
        source.backup(dest)
    source.close()
    dest.close()

backup_database('production.db', 'production_backup.db')

性能优化要点

优化项操作收益
WAL 模式PRAGMA journal_mode=WAL读写并发不阻塞
批量插入executemany() + 单事务10x-100x 写入提速
同步模式PRAGMA synchronous=NORMAL写入提速 2x(少量安全保障降低)
内存缓存PRAGMA cache_size=-80008MB 缓存(负值表示 KB)
临时存储PRAGMA temp_store=MEMORY临时表/排序在内存中进行
索引为高频查询列建索引查询提速 10x-1000x

批量插入最佳实践:

python
import sqlite3

# ❌ 慢:逐条插入,每条一个事务
# 1万条数据需要约 60 秒

# ✅ 快:批量 + 单事务
with sqlite3.connect('data.db') as conn:
    conn.execute('PRAGMA synchronous=NORMAL')
    conn.execute('PRAGMA journal_mode=WAL')
    conn.execute('CREATE TABLE IF NOT EXISTS records(id INTEGER, data TEXT)')

    batch_size = 1000
    data = [(i, f'data-{i}') for i in range(10000)]

    with conn:  # 整个批量在一个事务中
        conn.executemany('INSERT INTO records VALUES(?, ?)', data)
    # 1万条数据约 0.5 秒 — 120x 提速

常见陷阱

陷阱说明正确做法
忘记 commit数据不保存,程序退出后丢失使用 with conn: 自动提交
不关连接连接泄漏,文件锁不释放使用 with connect(...) as conn:
字符串拼接 SQLSQL 注入风险始终使用 ? 参数化查询
多线程共享连接SQLite 默认 check_same_thread=True每个线程独立连接或使用连接池
不处理 busy 超时高并发时频繁 database is locked设置 timeout 参数 + WAL 模式
不建索引全表扫描,大数据量极慢为 WHERE / JOIN / ORDER BY 列建索引
把 SQLite 当 MySQL 用并发写入性能差高并发写入场景用服务端数据库

术语表

术语定义
DB-API 2.0Python 数据库接口规范,定义 Connection/Cursor/execute 等标准 API
Connection数据库连接对象,管理事务与连接生命周期
Cursor游标对象,执行 SQL 并遍历结果集
Parameterized Query参数化查询,用占位符代替字符串拼接,防止 SQL 注入
WALWrite-Ahead Logging,先写日志再写数据文件,提升并发读写性能
row_factory行工厂函数,控制查询返回行的数据类型
savepoint事务内部的子保存点,支持部分回滚

延伸阅读

第二部分:MySQL(服务端)


title: Python 操作 MySQL 完全指南 description: 深入掌握 Python 操作 MySQL,涵盖 mysql-connector-python、PyMySQL、SQLAlchemy ORM、连接池、SQL 注入防护、事务管理与生产级最佳实践。 version: 1.0 author: 文档维护组 created: 2026-06-05 updated: 2026-06-05 status: 正式

概述

MySQL 是业界最流行的开源关系型数据库之一,采用 客户端-服务器架构。与 SQLite 不同,MySQL 服务器以独立进程运行,通过网络对外提供服务,Python 程序通过数据库驱动(Driver)与之通信。

图表渲染中…
Python MySQL 驱动生态
驱动特点适用场景
mysql-connector-pythonMySQL 官方纯 Python 实现快速原型、无 C 依赖环境
PyMySQL纯 Python 实现,API 简洁通用场景、性能测试造数据
mysqlclientC 扩展,性能最优高并发生产环境
aiomysql基于 PyMySQL 的异步驱动asyncio 异步项目
图表渲染中…

mysql-connector-python

MySQL 官方提供的纯 Python 驱动,安装简单:

bash
pip install mysql-connector-python
基本 CRUD 操作
图表渲染中…

####### 建表与插入

python
import mysql.connector

conn = mysql.connector.connect(
    user='root',
    password='root1234',
    database='test'
)
cursor = conn.cursor()

# 创建表
cursor.execute('''
    CREATE TABLE user (
        id   VARCHAR(20) PRIMARY KEY,
        name VARCHAR(20) NOT NULL
    )
''')

# 插入单行 — MySQL 占位符为 %s
cursor.execute(
    'INSERT INTO user (id, name) VALUES (%s, %s)',
    ['1', 'Michael']
)
print(f'影响行数: {cursor.rowcount}')

conn.commit()
cursor.close()
conn.close()

####### 查询

python
import mysql.connector

conn = mysql.connector.connect(
    user='root', password='root1234', database='test'
)
cursor = conn.cursor()

cursor.execute('SELECT * FROM user WHERE id = %s', ('1',))
values = cursor.fetchall()
print(values)  # [('1', 'Michael')]

cursor.close()
conn.close()

PyMySQL

PyMySQL 是社区最广泛使用的纯 Python MySQL 驱动,API 与 mysql-connector-python 高度兼容。

bash
pip install pymysql
CRUD 完整示例

####### 插入数据

python
import pymysql

conn = pymysql.connect(
    host="localhost",
    port=3306,
    user="root",
    password="root1234",
    database="vega"
)
cursor = conn.cursor()

sql = "INSERT INTO t_user(username, password, email, role_id) VALUES(%s, %s, %s, %s)"

# 单行插入
cursor.execute(sql, ("user1", "pwd_hash", "user1@qq.com", 1))

# 批量插入 — 性能远优于逐条 execute
data = [
    ("user2", "pwd_hash_2", "user2@qq.com", 1),
    ("user3", "pwd_hash_3", "user3@qq.com", 1),
]
cursor.executemany(sql, data)

conn.commit()
cursor.close()
conn.close()

####### 更新数据

python
import pymysql

conn = pymysql.connect(
    host="localhost", port=3306,
    user="root", password="root1234", database="vega"
)
cursor = conn.cursor()

sql = 'UPDATE t_user SET username = %s WHERE id = %s'

# 单行更新
cursor.execute(sql, ('new_name', 23))

# 批量更新(一次性提交多组参数)
cursor.executemany(sql, [('name_22', 22), ('name_21', 21)])

conn.commit()
cursor.close()
conn.close()

####### 删除数据

python
sql = 'DELETE FROM t_user WHERE id = %s'

# 单行删除
cursor.execute(sql, (3,))

# 批量删除
cursor.executemany(sql, [(4,), (5,)])

conn.commit()

####### 查询数据 — 三种获取方式

python
cursor.execute("SELECT * FROM t_user")

# 方式一:逐行获取
row = cursor.fetchone()
print(row)

# 方式二:分批获取(适合大数据集,控制内存)
rows = cursor.fetchmany(5)
for row in rows:
    print(row)

# 方式三:全部获取(适合小数据集)
results = cursor.fetchall()
for row in results:
    print(row)

注意: fetchone() / fetchmany() / fetchall() 共享同一个结果集游标。先调用 fetchone() 再调用 fetchall() 时,fetchall() 只返回剩余未读取的行。

SQL 注入与参数化查询

攻击原理
图表渲染中…
攻击演示
python
import pymysql

conn = pymysql.connect(
    host="localhost", port=3306,
    user="root", password="root1234", database="vega"
)
cursor = conn.cursor()

username = "1 OR 1=1"
password = "1 OR 1=1"

# ❌ 危险:字符串拼接
sql_bad = (
    "SELECT COUNT(*) FROM t_user WHERE username=" + username +
    " AND AES_DECRYPT(UNHEX(password),'HelloWorld')=" + password
)
cursor.execute(sql_bad)
print(f'SQL注入成功,返回: {cursor.fetchone()[0]}')  # 绕过认证

# ✅ 安全:参数化查询
sql_safe = (
    "SELECT COUNT(*) FROM t_user WHERE username=%s "
    "AND AES_DECRYPT(UNHEX(password),'HelloWorld')=%s"
)
cursor.execute(sql_safe, (username, password))
print(f'参数化查询,返回: {cursor.fetchone()[0]}')  # 0 — 注入失败

cursor.close()
conn.close()
参数化查询原则
原则说明
永远不拼接 SQL任何用户输入都不应直接拼入 SQL 字符串
使用占位符MySQL 驱动使用 %s,SQLite 使用 ?
参数独立传递将参数作为 execute() 的第二个参数传入
动态表名/列名使用白名单校验,不能用占位符(占位符仅适用于值)
python
# ✅ 动态列名:白名单校验
ALLOWED_COLUMNS = {'id', 'name', 'email', 'created_at'}
ORDER_COLUMNS = {'id', 'name'}

def safe_query(table, columns, order_by):
    if table not in ALLOWED_TABLES:
        raise ValueError(f'非法的表名: {table}')
    selected = [c for c in columns if c in ALLOWED_COLUMNS]
    if order_by not in ORDER_COLUMNS:
        raise ValueError(f'非法的排序列: {order_by}')
    sql = f"SELECT {', '.join(selected)} FROM {table} ORDER BY {order_by}"
    cursor.execute(sql)

事务管理

图表渲染中…
python
import pymysql

conn = pymysql.connect(
    host="localhost", port=3306,
    user="root", password="root1234", database="vega"
)

try:
    cursor = conn.cursor()
    conn.begin()  # 显式开启事务

    cursor.execute("INSERT INTO t_type(type) VALUES(%s)", ('test1',))
    # 更多操作...

    conn.commit()
    print('事务提交成功')
except Exception as e:
    print(f'发生错误,回滚: {e}')
    conn.rollback()
finally:
    cursor.close()
    conn.close()
事务 ACID 特性
特性含义MySQL InnoDB 实现
原子性 (Atomicity)事务要么全部成功,要么全部失败rollback 回滚 + undo log
一致性 (Consistency)事务前后数据满足所有约束外键、CHECK、触发器
隔离性 (Isolation)并发事务互不干扰MVCC + 行级锁
持久性 (Durability)提交后数据永久保存redo log + binlog 双写

连接池

数据库连接是昂贵的资源——TCP 三次握手、MySQL 认证握手、线程分配。频繁创建和销毁连接会显著降低性能。连接池通过 预创建 + 复用 机制解决此问题。

图表渲染中…
bash
pip install dbutils
python
import pymysql
from dbutils.pooled_db import PooledDB


class MySQLPool:
    """线程安全的 MySQL 连接池"""

    def __init__(self, host, port, user, password, database,
                 mincached=5, maxcached=20, maxconnections=50):
        self._pool = PooledDB(
            creator=pymysql,
            maxconnections=maxconnections,  # 最大连接数上限
            mincached=mincached,            # 初始化时创建的闲置连接
            maxcached=maxcached,            # 最大闲置连接数
            blocking=True,                  # 连接耗尽时阻塞等待
            host=host,
            port=port,
            user=user,
            password=password,
            database=database,
            charset='utf8mb4'
        )

    def get_conn(self):
        """从池中获取一个连接"""
        return self._pool.connection()


# 全局单例
mysql_pool = MySQLPool(
    host="localhost", port=3306,
    user="root", password="root1234",
    database="vega"
)

# 使用连接池
conn = mysql_pool.get_conn()
try:
    with conn.cursor() as cursor:
        cursor.execute('SELECT * FROM t_user')
        for row in cursor.fetchall():
            print(row)
finally:
    conn.close()  # 归还到池中,而非真正关闭 TCP 连接
连接池关键参数
参数含义建议值
maxconnections最大连接数上限根据 MySQL max_connections 设置,通常 20-50
mincached池初始化时创建的闲置连接5-10
maxcached最大闲置连接数与 maxconnections 一致
blocking连接耗尽时是否等待True(生产推荐)
ping借出前检测连接有效性1(每次 ping,生产推荐)

SQLAlchemy ORM

ORM (Object-Relational Mapping) 将数据库表映射为 Python 类,将 SQL 操作转化为面向对象的 API 调用。

图表渲染中…
bash
pip install sqlalchemy
声明式映射
python
from sqlalchemy import Column, String, ForeignKey, create_engine
from sqlalchemy.orm import sessionmaker, relationship, declarative_base

Base = declarative_base()


class User(Base):
    __tablename__ = 'user'

    id   = Column(String(20), primary_key=True)
    name = Column(String(20))

    # 一对多关系:一个 User 拥有多个 Book
    books = relationship('Book', back_populates='owner')


class Book(Base):
    __tablename__ = 'book'

    id      = Column(String(20), primary_key=True)
    name    = Column(String(20))
    user_id = Column(String(20), ForeignKey('user.id'))

    owner = relationship('User', back_populates='books')
CRUD 操作
python
# 初始化引擎与会话
engine = create_engine(
    'mysql+mysqlconnector://root:root1234@localhost:3306/test',
    echo=False,          # 不打印 SQL 日志
    pool_size=10,        # 内置连接池大小
    max_overflow=20,     # 超出 pool_size 的最大额外连接
)
Session = sessionmaker(bind=engine)
session = Session()

# --- Create ---
new_user = User(id='5', name='Bob')
session.add(new_user)
session.commit()

# --- Read ---
user = session.query(User).filter(User.id == '5').one()
print(f'type: {type(user)}, name: {user.name}')

# 条件查询 + 排序 + 分页
users = (
    session.query(User)
    .filter(User.name.like('B%'))
    .order_by(User.name)
    .limit(10)
    .all()
)

# --- Update ---
user.name = 'Bob Updated'
session.commit()  # ORM 自动跟踪脏数据

# --- Delete ---
session.delete(user)
session.commit()

session.close()
关系查询
python
# 查询 User 时自动加载其所有 Book
user = session.query(User).filter(User.id == '5').one()
for book in user.books:
    print(f'{user.name} 拥有书籍: {book.name}')

# 从 Book 反向查询 Owner
book = session.query(Book).first()
print(f'《{book.name}》的所有者是: {book.owner.name}')
Engine URL 格式
code
dialect+driver://username:password@host:port/database
驱动URL 前缀
mysql-connector-pythonmysql+mysqlconnector://
PyMySQLmysql+pymysql://
PostgreSQLpostgresql+psycopg2://
SQLitesqlite:///path/to/db

实战案例:数据库导出到 Excel

bash
pip install openpyxl pymysql
python
import openpyxl
import pymysql

workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = '员工基本信息'
sheet.append(('工号', '姓名', '职位', '月薪', '补贴', '部门'))

conn = pymysql.connect(
    host='127.0.0.1', port=3306,
    user='guest', password='Guest.618',
    database='hrs', charset='utf8mb4'
)

try:
    with conn.cursor() as cursor:
        cursor.execute(
            'SELECT eno, ename, job, sal, COALESCE(comm, 0), dname '
            'FROM tb_emp NATURAL JOIN tb_dept'
        )
        for row in cursor:
            sheet.append(row)
    workbook.save('hrs.xlsx')
    print('导出成功: hrs.xlsx')
except pymysql.MySQLError as err:
    print(f'数据库错误: {err}')
finally:
    conn.close()

驱动选择决策树

图表渲染中…

常见陷阱

陷阱说明正确做法
忘记 commitInnoDB 默认不自动提交显式 commit() 或设置 autocommit=True
不关连接连接泄漏导致 "Too many connections"try/finally 或 with 语句
字符串拼接 SQLSQL 注入漏洞始终使用 %s 参数化查询
逐条插入大量数据每条一个事务,极慢executemany() + 单事务
不设字符集中文乱码(默认 latin1)charset='utf8mb4'
连接不设超时断连后长时间等待connect_timeout + read_timeout
硬编码密码安全风险环境变量或配置文件

术语表

术语定义
ORMObject-Relational Mapping,对象-关系映射
SessionSQLAlchemy 中的工作单元,管理对象持久化
EngineSQLAlchemy 中的数据库连接核心
Connection Pool连接池,预创建并复用数据库连接
Parameterized Query参数化查询,用占位符代替字符串拼接
ACID事务四大特性:原子性、一致性、隔离性、持久性
MVCCMulti-Version Concurrency Control,多版本并发控制

延伸阅读

版本差异(标准库 → Python 3.14)

模块/特性本文编写时Python 3.14 变化
datetimeutcnow() / utcfromtimestamp()3.12 起弃用,改用 datetime.now(tz=datetime.UTC) / fromtimestamp(ts, tz=datetime.UTC)(aware 对象)
asyncio基础 API3.14 新增内省能力(asyncio.Task/Future 状态查询);3.11 起推荐 TaskGroup + asyncio.timeout()
typing旧式 List/Dict3.9+ 内置泛型;3.10+ 联合类型 X | Y;3.12 type 语句;3.14 PEP 649 延迟注解
importlibimp 模块imp 于 3.12 移除,统一使用 importlib
压缩zlib/gzip/bz2/lzma3.14 新增 zstandard 标准库支持(PEP 784)
pathlib基础路径操作3.12+ 持续增强(Path.walk() 等),3.13 支持 is_relative_to()
往事清理3.13 移除 cgitelnetlibcryptaudioop 等已废弃模块

本文讲解的模块核心 API 与使用模式在 3.14 中保持稳定;注意上述弃用/移除项,升级时优先用标准库推荐的替代方案。