首页 / SQLite 入门教程 / Python 接口入门(sqlite3)

SQLite 入门教程

Python 接口入门(sqlite3)

本教程共 50 篇 · 第 47 篇 · 更新于 2026-07-31

sqlitePythonsqlite3标准库

47. Python 接口入门(sqlite3)

本节目标:学完本章你能用 Python 自带的 sqlite3 标准库,完成”连接数据库 → 建表 → 插入 → 查询 → 更新 → 删除”的最小闭环,并理解事务与参数化在 Python 中的写法。

SQLite 最大的魅力之一,是它不需要你启动任何数据库服务。它是 serverless(无服务进程)、零配置的:数据库就是一个磁盘文件,你的程序通过库函数直接读写它。在 Python 世界里,这份便利更进一步——从 Python 2.5 起,标准库就内置了 sqlite3 模块,你不用额外安装任何东西就能直接 import sqlite3。这也呼应了第 46 章的要点:既然没有服务器帮你兜底,连接、安全和事务都得自己代码负责。

本章只演示 sqlite3 标准库的最小读写,不引入任何 Web 框架、不使用 SQLAlchemy 等 ORM——先把手感练对,将来接框架才不会被层层封装绕晕。

一、连上数据库

sqlite3.connect() 接受一个文件名;如果文件不存在,它会自动创建。你也可以用 ":memory:" 在内存里建一个临时库(程序退出就没了)。因为我们反复强调”数据库即文件”,所以这里用真实的文件更直观:

import sqlite3

conn = sqlite3.connect("demo.db")   # 文件不存在就新建,存在就打开
print("连接成功")

连接对象 conn 代表一次数据库会话。多数操作要先拿到一个”游标(cursor)“,再用它执行 SQL:

c = conn.cursor()

二、建表(用全局统一示例表)

我们用教程贯穿的 users 表:

c.execute("""
CREATE TABLE IF NOT EXISTS users (
  id INTEGER PRIMARY KEY,
  name TEXT,
  age INTEGER,
  email TEXT
)
""")
conn.commit()   # DDL 也建议 commit,确保落盘

这里 id INTEGER PRIMARY KEY 又一次体现 SQLite 的特色:它隐式自增,是底层 rowid 的别名,你插入时不写 id,SQLite 会自动填上比当前最大值大 1 的值。不需要AUTOINCREMENT 关键字——那是可选的、仅在”永不重用已删除的 rowid”时才考虑,还会带来 sqlite_sequence 表与额外开销。日常自增主键,写 INTEGER PRIMARY KEY 就够了。

三、插入:参数化是铁律

还记得第 46 章吗?插入值必须走参数,绝不拼字符串:

c.execute(
    "INSERT INTO users (name, age, email) VALUES (?, ?, ?)",
    ("小明", 19, "ming@example.com")
)
conn.commit()

多个值用 executemany 一次插入:

rows = [
    ("小红", 20, "hong@example.com"),
    ("小刚", 22, "gang@example.com"),
]
c.executemany("INSERT INTO users (name, age, email) VALUES (?, ?, ?)", rows)
conn.commit()

实用技巧

如果你嫌按位置对 ? 容易数错,可以用命名占位符 :name 配字典:

c.execute(
    "INSERT INTO users (name, age, email) VALUES (:n, :a, :e)",
    {"n": "小美", "a": 21, "e": "mei@example.com"}
)
conn.commit()

四、查询:读取结果

execute 之后用 fetchall() / fetchone() / fetchmany(n) 取结果,每行是一个元组:

for row in c.execute("SELECT id, name, age FROM users ORDER BY age"):
    print(row)
# (19, '小明', 19)  (21, '小美', 21)  ...

想要”按列名取”而不是”按下标取”,把 row_factory 设成 sqlite3.Row

conn.row_factory = sqlite3.Row
for row in conn.execute("SELECT * FROM users"):
    print(row["name"], row["email"])   # 像字典一样按列名访问

带条件查询同样参数化:

c.execute("SELECT * FROM users WHERE age > ?", (20,))
print(c.fetchall())

如果结果很多、不想一次性全读进内存,用 fetchmany(n) 分批取:

c.execute("SELECT * FROM users ORDER BY id")
while True:
    batch = c.fetchmany(100)   # 每次取 100 行
    if not batch:
        break
    for row in batch:
        print(row)

四之二、把 users 和 orders 一起用(外键要点)

教程贯穿还有一张 orders 表,它用 user_id 指向 users.id

CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  user_id INTEGER,
  amount REAL,
  created_at TEXT
);

如果你希望”删掉一个用户时,数据库拦住尚有订单的用户”,就要开外键约束——但切记 SQLite 默认不强制外键,必须显式开启,且每次新连接都要开

conn = sqlite3.connect("demo.db")
conn.execute("PRAGMA foreign_keys = ON")   # 关键!默认 OFF
c = conn.cursor()
c.execute("""CREATE TABLE IF NOT EXISTS orders (
  id INTEGER PRIMARY KEY, user_id INTEGER,
  amount REAL, created_at TEXT,
  FOREIGN KEY(user_id) REFERENCES users(id)
)""")
conn.commit()

漏掉 PRAGMA foreign_keys = ON,参照完整性就形同虚设,这是新手最常踩的坑之一。

四之三、Python 值与存储类的对应关系

SQLite 是动态类型 + 类型亲和性(Type Affinity):列声明只是”亲和性建议”,实际存什么由值决定。sqlite3 在写入时会按下面的规则把 Python 值映射成 5 种**存储类(Storage Class)**之一——NULL / INTEGER / REAL / TEXT / BLOB:

  • Python int → INTEGER(注意:超过 2^63 的大整数会被转成 REAL,可能丢精度)
  • Python float → REAL
  • Python str → TEXT
  • Python bytes → BLOB
  • Python None → NULL

正因如此,没有 DATE/TIME 这类日期类型:日期请用 ISO8601 字符串(TEXT)、unix 秒(INTEGER)或儒略日(REAL)存,读取后自己解析。理解这层映射,你就不会被”为什么 TEXT 列里能存进数字”这类动态类型现象困惑。

五、更新与删除

c.execute("UPDATE users SET age = ? WHERE name = ?", (23, "小刚"))
conn.commit()

c.execute("DELETE FROM users WHERE name = ?", ("小美",))
conn.commit()

常见坑

第一,UPDATE/DELETE 一定要带 WHERE,否则会改/删整张表。第二,conn.close() 不会自动 commit()——如果忘了 commit() 就直接 close(),你的改动会全部丢失。第三,涉及 orders 等带外键的表时,SQLite 默认不强制外键,需先执行 PRAGMA foreign_keys = ON(每次连接都要开),否则参照完整性形同虚设。

六、事务:用 with 更省心

sqlite3 的连接对象本身可以当”上下文管理器”用。with conn: 会把块内操作包成一个事务,正常结束自动 commit(),抛异常自动 rollback(),再也不怕漏写 commit

with conn:
    conn.execute("INSERT INTO users (name, age, email) VALUES (?, ?, ?)",
                 ("阿强", 25, "qiang@example.com"))
# 离开 with 块即自动提交

六之二、异常处理:出错要回滚

真实程序里写入可能失败(比如违反了 NOT NULL、UNIQUE 约束)。用 try/except 包住写操作,出错时 rollback() 把没提交的部分撤掉,避免半截数据残留在库里:

try:
    with conn:
        conn.execute(
            "INSERT INTO users (name, age, email) VALUES (?, ?, ?)",
            ("小雷", 28, "lei@example.com")
        )
except sqlite3.Error as e:
    print("写入失败,已回滚:", e)

因为 with conn: 在异常时会自动 rollback(),这里即使不手动调也安全;手动 try/except 主要是为了”捕获错误、给出友好提示”,而不是让程序崩掉。

另外提醒:INTEGER PRIMARY KEY 之所以能隐式自增,是因为它直接映射到底层 rowid——你插入时不给 id,SQLite 自动填”当前最大 rowid + 1”。如果想用文本做主键(比如 UUID),就写 id TEXT PRIMARY KEY,此时它不会自增,需要你自己保证唯一。

六之三、顺手的小工具方法

sqlite3 还提供几个省心的小帮手。想看表里有哪些列,不必切回 CLI,用 PRAGMA table_info 即可:

for col in conn.execute("PRAGMA table_info(users)"):
    print(col[1], col[2])   # 列名、声明类型

想知道”本次连接以来总共改了多少行”,用 conn.total_changes

conn.execute("UPDATE users SET age = age + 1")
print("累计影响行数:", conn.total_changes)

这些在调试或写小工具时很顺手,但记住它们只是语法糖,底层仍然是你已经学过的 SQL 与事务逻辑。

七、收尾与类比小结

用完记得关连接,释放文件锁:

conn.close()

sqlite3 模块类比成一个”文件读写助手”:你 connect 打开文件,execute 往里写指令,commit 真正落盘,close 合上文件。它和文本文件不同的是——底层是 B 树组织的页式存储(第 49 章会讲),所以即使是几百万行的查询也很快。你今天写的每一行 Python,最终都被这个助手翻译成对那个 .db 文件的精准读写,没有中间商、没有网络往返,这正是嵌入式数据库”又快又简单”的根本原因。

重点提示

sqlite3 是 Python 官方标准库,API 遵循 DB-API 2.0 规范,所以你今天学的 connect / cursor / execute / commit / rollback 套路,迁移到 MySQL(用 pymysql)、PostgreSQL(用 psycopg)时也高度相似,只是换了个驱动模块。但参数化 ? 占位和”显式 commit”这两条习惯,在哪里都成立。

最后提醒:本教程定位是”纯知识点干货,不含实战项目”,所以本章只演示标准库最小读写。等你在真实项目里用 Flask/Django/FastAPI 时,它们底层调用的依然是这一套 sqlite3 能力,理解原理比记框架 API 更重要。