Python数据库编程入门:sqlite3 基础、增删改查与参数化查询

前面的几篇文章里,我们已经学会了处理文件、调用网络接口,也接触了常见第三方库。

但真实系统中的数据,很多时候并不是保存在:

  • 普通文本文件;
  • CSV 表格;
  • JSON 文件。

而是保存在数据库里。

所以当你继续往工程方向走时,一个几乎绕不过去的问题就是:

Python 程序如何和数据库交互?

本文不会一开始就把内容拉到复杂的 MySQL、PostgreSQL 或 ORM 框架,而是先从 Python 标准库自带的 sqlite3 开始。

原因很简单:

  • 它零安装、开箱即用;
  • 足够覆盖数据库编程的核心思路;
  • 特别适合初学者理解 SQL 和 Python 之间的协作方式。

本文会围绕下面几个主题展开:

  • 什么是 SQLite;
  • 如何连接数据库;
  • 如何建表;
  • 如何做增删改查;
  • 为什么要使用参数化查询;
  • 如何处理事务和异常。

理解这一章之后,你就能写出基本的数据存储程序,也能为后续学习 MySQL、ORM 和 Web 开发打下很好的基础。

一、为什么要学数据库编程?

文件当然可以保存数据,但它有局限:

  • 查找和筛选不方便;
  • 多条结构化记录不好管理;
  • 更新某一部分数据比较麻烦;
  • 并不适合复杂查询。

而数据库更擅长解决这些问题:

  • 存储结构化数据;
  • 支持条件查询;
  • 支持排序、聚合、统计;
  • 更适合长期持久化。

例如下面这些场景:

  • 用户信息;
  • 订单数据;
  • 学生成绩;
  • 日志记录;
  • 配置和业务状态。

这些都很适合放在数据库中。


二、为什么先学 sqlite3

SQLite 是一个轻量级数据库,它和很多传统数据库最大的区别之一是:

它通常不需要单独安装服务进程,一个数据库往往就是一个本地文件。

这非常适合初学阶段。

Python 标准库自带 sqlite3 模块,所以你几乎可以直接开始写。

2.1 适合初学者的原因
  • 不需要额外部署数据库服务;
  • 代码可以直接跑起来;
  • 足够学习 SQL 和数据库交互的基础逻辑;
  • 非常适合本地工具、小项目、原型验证。
2.2 先理解几个基础术语
  • 数据库(database):存数据的整体容器;
  • 表(table):数据库中的数据表;
  • 行(row):一条记录;
  • 列(column):字段;
  • SQL:用来操作关系型数据库的语言。

三、连接数据库与创建表

3.1 连接数据库
1
2
3
4
5
import sqlite3

conn = sqlite3.connect('students.db')
print('连接成功')
conn.close()

这里:

  • 如果 students.db 不存在,通常会自动创建;
  • conn 表示数据库连接对象。
3.2 创建游标对象

在执行 SQL 语句之前,通常需要先创建游标:

1
2
3
4
5
6
import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()
print(cursor)
conn.close()

可以先简单理解为:

游标是你和数据库“发命令、拿结果”的接口之一。

3.3 创建一张表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

cursor.execute('''
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
score REAL
)
''')

conn.commit()
conn.close()

这里做了几件事:

  • 建立连接;
  • 创建游标;
  • 执行建表 SQL;
  • commit() 提交更改;
  • 关闭连接。
3.4 为什么 commit() 很重要?

数据库中的修改操作通常不是“写了就立刻永久生效”,而是先处于当前事务中。

只有提交后,修改才真正保存下来。

所以对初学者来说,一个很重要的习惯是:

  • 查询不一定需要 commit()
  • 插入、更新、删除通常记得 commit()

四、插入数据:CREATE 之后就是 INSERT

4.1 插入一条记录
1
2
3
4
5
6
7
8
9
10
11
12
import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

cursor.execute('''
INSERT INTO students (name, age, score)
VALUES (?, ?, ?)
''', ('Alice', 20, 95.5))

conn.commit()
conn.close()

这里最值得注意的是:

1
VALUES (?, ?, ?)

这是参数化查询写法,后面还会重点讲。

4.2 插入多条记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sqlite3

students = [
('Bob', 21, 88),
('Cindy', 19, 91),
('David', 22, 76),
]

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

cursor.executemany('''
INSERT INTO students (name, age, score)
VALUES (?, ?, ?)
''', students)

conn.commit()
conn.close()

如果是批量插入,executemany() 通常更方便。


五、查询数据:SELECT 是最常见的操作之一

5.1 查询全部记录
1
2
3
4
5
6
7
8
9
10
11
12
import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

cursor.execute('SELECT id, name, age, score FROM students')
rows = cursor.fetchall()

for row in rows:
print(row)

conn.close()

这里:

  • fetchall() 会取出所有结果;
  • 每一条记录通常是一个元组。
5.2 查询一条记录
1
2
3
cursor.execute('SELECT name, score FROM students WHERE id = ?', (1,))
row = cursor.fetchone()
print(row)

注意这里 (1,) 末尾有个逗号,因为它是单元素元组。

5.3 带条件查询
1
2
3
4
cursor.execute('SELECT name, score FROM students WHERE score >= ?', (90,))
rows = cursor.fetchall()
for row in rows:
print(row)
5.4 排序查询
1
2
3
4
cursor.execute('SELECT name, score FROM students ORDER BY score DESC')
rows = cursor.fetchall()
for row in rows:
print(row)
5.5 限制返回条数
1
2
cursor.execute('SELECT name, score FROM students ORDER BY score DESC LIMIT 3')
print(cursor.fetchall())

这些已经能覆盖很多基础查询场景。


六、更新与删除:改数据和删数据

6.1 更新数据
1
2
3
4
5
6
7
8
9
10
11
12
13
import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

cursor.execute('''
UPDATE students
SET score = ?
WHERE name = ?
''', (98, 'Alice'))

conn.commit()
conn.close()
6.2 删除数据
1
2
3
4
5
6
7
8
9
import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

cursor.execute('DELETE FROM students WHERE name = ?', ('David',))

conn.commit()
conn.close()
6.3 删除要特别小心

尤其是:

1
DELETE FROM students

如果不加 WHERE,通常会删除整张表的所有记录。

所以写删除语句时要非常谨慎。


七、参数化查询:为什么它这么重要?

这是数据库编程里非常关键的一部分。

很多初学者可能会下意识写出这种代码:

1
2
3
name = 'Alice'
sql = f"SELECT * FROM students WHERE name = '{name}'"
cursor.execute(sql)

这看起来好像能用,但它有明显问题:

  • 容易出错;
  • 可读性不稳定;
  • 最重要的是:可能带来 SQL 注入风险。

更好的写法是参数化查询:

1
cursor.execute('SELECT * FROM students WHERE name = ?', ('Alice',))
7.1 参数化查询的好处
  1. 更安全:避免拼接 SQL 带来的风险;
  2. 更清晰:SQL 结构和数据分离;
  3. 更容易维护:变量不用自己处理引号转义。
7.2 一个错误示例和正确示例对比

错误写法:

1
2
name = "Alice' OR 1=1 --"
sql = f"SELECT * FROM students WHERE name = '{name}'"

正确写法:

1
2
name = "Alice' OR 1=1 --"
cursor.execute('SELECT * FROM students WHERE name = ?', (name,))

初学阶段请尽量形成一个硬习惯:

只要 SQL 里有外部变量,就优先参数化,不自己拼接。


八、事务、提交与回滚

数据库修改通常涉及事务。

你可以把事务简单理解为:

一组数据库操作,要么一起成功,要么一起失败。

8.1 正常提交
1
conn.commit()
8.2 出错回滚
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sqlite3

conn = sqlite3.connect('students.db')
cursor = conn.cursor()

try:
cursor.execute('INSERT INTO students (name, age, score) VALUES (?, ?, ?)', ('Eve', 20, 85))
cursor.execute('INSERT INTO students (name, age, score) VALUES (?, ?, ?)', ('Frank', 21, 90))
conn.commit()
except Exception as e:
conn.rollback()
print('操作失败,已回滚:', e)
finally:
conn.close()

这说明一个很重要的思路:

  • 多个相关修改最好放在同一个事务里;
  • 一旦失败,可以统一回滚。

九、让查询结果更好用:row_factory

默认情况下,查询结果是元组,例如:

1
(1, 'Alice', 20, 95.5)

有时你会希望像字典一样按列名访问。

1
2
3
4
5
6
7
8
9
10
11
12
import sqlite3

conn = sqlite3.connect('students.db')
conn.row_factory = sqlite3.Row
cursor = conn.cursor()

cursor.execute('SELECT id, name, age, score FROM students')
row = cursor.fetchone()
print(row['name'])
print(row['score'])

conn.close()

这在结果字段较多时非常实用。


十、一个综合示例:学生成绩管理的基本流程

下面写一个稍微完整一点的示例,把建表、插入、查询串起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import sqlite3

conn = sqlite3.connect('school.db')
cursor = conn.cursor()

cursor.execute('''
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
score REAL
)
''')

cursor.executemany('''
INSERT INTO students (name, age, score)
VALUES (?, ?, ?)
''', [
('Alice', 20, 95),
('Bob', 21, 88),
('Cindy', 19, 59),
('David', 22, 76),
])

conn.commit()

cursor.execute('''
SELECT name, score
FROM students
WHERE score >= ?
ORDER BY score DESC
''', (60,))

for row in cursor.fetchall():
print(row)

conn.close()

这个例子体现了数据库编程的基础闭环:

  • 建表;
  • 存数据;
  • 查数据;
  • 条件过滤;
  • 排序输出。

十一、学习数据库编程时的常见误区

11.1 不要把 SQL 和 Python 逻辑混在一起乱拼

最好让:

  • SQL 负责数据查询和数据变更;
  • Python 负责业务流程和结果处理。
11.2 不要忘记关闭连接

哪怕是小程序,也建议养成好习惯:

  • 用完连接及时关闭;
  • 或者至少在 finally 中关闭。
11.3 不要忽视参数化查询

这是数据库安全和可维护性的基础之一。

11.4 不要一开始就急着跳 ORM

ORM 很方便,但如果你完全不懂:

  • 表是什么;
  • SQL 在做什么;
  • 事务和查询怎么工作;

那么后面遇到 ORM 问题会很难排查。

所以先学 sqlite3 和基础 SQL,是非常值得的。


十二、学习建议:这一章优先掌握什么?

12.1 先掌握 CRUD

也就是:

  • CREATE
  • INSERT
  • SELECT
  • UPDATE
  • DELETE

这是数据库交互最核心的 5 类操作。

12.2 先把参数化查询养成习惯

这件事越早形成越好。

12.3 先把事务和 commit() 理解清楚

不然后面很容易出现“为什么我明明执行了 SQL,数据却没保存”的困惑。

12.4 后续再扩展到 MySQL / PostgreSQL / ORM

当你把这篇吃透后,再学:

  • MySQL 驱动;
  • SQLAlchemy;
  • Django ORM;
  • FastAPI + 数据库;

都会顺畅很多。


十三、总结

这一篇我们系统梳理了 Python 中“数据库编程入门”的核心内容:

  1. 为什么需要数据库:理解数据库比普通文件更适合结构化数据管理;
  2. sqlite3 基础:学会连接数据库、创建游标和建表;
  3. 插入数据:掌握单条插入和批量插入;
  4. 查询数据:掌握条件查询、排序和限制条数;
  5. 更新与删除:理解修改数据和删除数据的基本方式;
  6. 参数化查询:理解安全性和可维护性的重要性;
  7. 事务处理:掌握 commit()rollback() 的意义;
  8. 结果处理优化:学会使用 sqlite3.Row 提升可读性。

如果说前面的几篇在处理文件和网络数据,那么这一篇更像是在回答:

当数据需要长期保存、结构化管理,并支持条件查询时,Python 应该如何优雅地和数据库协作?

掌握这一篇之后,你就已经具备了最基础的数据库编程能力。

继续往后写时,比较自然的衔接方向通常有两个:

  • Python并发编程与异步入门:进一步处理 I/O 任务和高并发请求;
  • Python Web 开发入门:把数据库、网络请求和服务端结合起来。

参考资料