【Python】基础语法入门(二十八)—— 数据库操作入门:用 SQLite 持久化数据

一、为什么用数据库

在前面几篇文章里,我们保存数据的方式大多是:写进文本文件、JSON 文件、CSV 文件。这些方式在小数据量、单机、只读场景下勉强够用,但一旦数据规模变大、查询变复杂、需要并发写入,文件存储的痛点就会暴露无遗。

1.1 文件存储的痛点

痛点 说明 举例
查询慢 想找某条记录,得把整个文件读进内存再遍历 10 万条用户数据里找 id=50000 的用户,要循环 5 万次
并发差 多个进程同时写同一个文件,容易数据错乱 两个请求同时 append,可能互相覆盖
无事务 写到一半程序崩了,数据就损坏了 转账:A 扣了钱,B 还没加,程序挂了
难以表达关系 用户、订单、商品之间的关联关系用 JSON 嵌套很难维护 查"某用户买过的所有商品"要手动 join 多个文件
无索引 没有索引机制,只能全表扫描 按手机号查用户,每次都要遍历全部记录

1.2 数据库能解决什么

数据库(Database)本质上是一个专门用来高效存储、查询、管理数据的软件系统。它提供:

  • 结构化存储:数据按表(Table)组织,有明确的字段类型。
  • 查询语言 SQL :用一行 SELECT ... WHERE ... 就能精准查找,不用自己写循环。
  • 索引:给某些列建索引,查询速度从 O(n) 降到接近 O(log n)。
  • 事务(Transaction):一组操作要么全部成功,要么全部回滚,保证数据一致性。
  • 并发控制:内置锁机制,多线程/多进程安全。
  • 关系建模:多表通过外键关联,支持 JOIN 查询。

一句话总结:当数据需要"持久化 + 查询 + 关系 + 并发"时,就该上数据库了。


二、数据库基础概念

在动手写代码前,先理清几个核心概念,否则后面 SQL 语法会看得云里雾里。

2.1 表(Table)、行(Row)、列(Column)

可以类比 Excel 表格:

  • 表 :相当于一个 Excel 工作表,比如 users 表存所有用户。

  • 列(字段) :表的"表头",定义每一列存什么类型的数据,如 id、name、age。

  • 行(记录) :表里的一条具体数据,比如 (1, '张三', 20) 表示 id 为 1、姓名张三、年龄 20 的一个用户。

    users 表
    +----+------+-----+
    | id | name | age | ← 列(字段)
    +----+------+-----+
    | 1 | 张三 | 20 | ← 行(记录)
    | 2 | 李四 | 25 |
    | 3 | 王五 | 30 |
    +----+------+-----+

2.2 主键(Primary Key)

主键是唯一标识一行记录的列,要求:

  • 值唯一(不能重复)。

  • 不能为 NULL。

  • 通常用自增整数(id),也可以用 UUID、身份证号等。

    CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT, -- 自增主键
    name TEXT NOT NULL
    );

2.3 SQL 语言简介

SQL(Structured Query Language,结构化查询语言)是操作关系型数据库的标准语言,主要分四类:

类别 作用 关键字
DDL(数据定义) 建表、改表结构 CREATE、ALTER、DROP
DML(数据操作) 增删改查 INSERT、SELECT、UPDATE、DELETE
DQL(数据查询) 查询 SELECT
DCL(数据控制) 权限管理 GRANT、REVOKE

日常开发最常用的是 DML,也就是常说的 CRUD(Create、Read、Update、Delete)。


三、SQLite:零配置的轻量数据库

3.1 什么是 SQLite

SQLite 是一个嵌入式、单文件、零配置 的关系型数据库。它不需要单独启动服务进程,整个数据库就是一个 .db 文件,直接用库 API 读写即可。

Python 标准库自带 sqlite3 模块,无需安装任何东西就能用。

3.2 SQLite 的特点

  • 轻量:核心代码几百 KB,整个数据库就一个文件。
  • 零配置:不用装服务端、不用配账号密码、不用建库。
  • 跨平台 :.db 文件可以在 Windows / Linux / macOS 之间直接拷贝使用。
  • 标准 SQL:支持大部分 SQL-92 标准,包括事务、外键、触发器、视图。
  • 并发:支持多读单写(多个连接可同时读,但写会加锁)。

3.3 适用场景

适合 不适合
桌面应用本地存储(浏览器历史、聊天记录) 高并发写入的 Web 服务(每秒上千写)
移动 App(Android/iOS 内置 SQLite) 多台服务器共享数据(需要 C/S 数据库)
小型网站、原型开发 海量数据(TB 级)
测试、教学 需要复杂权限管理
配置文件、缓存

3.4 vs MySQL / PostgreSQL

对比项 SQLite MySQL PostgreSQL
架构 嵌入式 C/S 服务端 C/S 服务端
安装 无需 需装服务 需装服务
并发写 单写锁 多线程写 多线程写
数据量 GB 级 TB 级 TB 级
适用 单机、轻量 中大型 Web 大型、复杂
Python 驱动 内置 sqlite3 pymysql psycopg2

经验:学习阶段、本地工具、小型项目首选 SQLite;上线 Web 服务再考虑 MySQL/PostgreSQL。


四、连接与建表

4.1 sqlite3.connect

sqlite3.connect(database) 打开(或创建)一个数据库文件,返回连接对象 Connection。

复制代码
import sqlite3

# 连接到本地文件(不存在会自动创建)
conn = sqlite3.connect('test.db')

# :memory: 表示在内存中创建临时数据库,程序结束即销毁,适合测试
# conn = sqlite3.connect(':memory:')

# 获取游标,用来执行 SQL
cursor = conn.cursor()

# 关闭
cursor.close()
conn.close()

运行结果:当前目录下会生成一个 test.db 文件。

4.2 row_factory = sqlite3.Row

默认查询返回的是元组 ,通过下标取值不直观。设置 row_factory = sqlite3.Row 后,返回的行对象可以像字典一样用列名取值,可读性大幅提升。

复制代码
import sqlite3

conn = sqlite3.connect('test.db')
conn.row_factory = sqlite3.Row   # 关键设置

cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")
cursor.execute("INSERT INTO users (id, name) VALUES (1, '张三')")
conn.commit()

cursor.execute("SELECT * FROM users")
row = cursor.fetchone()
print(row['name'])   # 张三(用列名取值,比 row[1] 直观)

conn.close()

运行结果:

复制代码
张三

4.3 CREATE TABLE 语法

复制代码
CREATE TABLE [IF NOT EXISTS] 表名 (
    列名1  数据类型  [约束],
    列名2  数据类型  [约束],
    ...
    [表级约束]
);

完整示例:

复制代码
import sqlite3

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

cursor.execute('''
CREATE TABLE IF NOT EXISTS products (
    id        INTEGER PRIMARY KEY AUTOINCREMENT,
    name      TEXT    NOT NULL,
    price     REAL    NOT NULL DEFAULT 0,
    stock     INTEGER NOT NULL DEFAULT 0,
    created   TEXT    DEFAULT (datetime('now','localtime'))
)
''')

conn.commit()
conn.close()
print("建表成功")

运行结果:

复制代码
建表成功

4.4 SQLite 数据类型

SQLite 是动态类型,存什么类型由值决定,但建表时仍建议声明类型。主要类型:

类型 含义 Python 对应
INTEGER 整数 int
REAL 浮点数 float
TEXT 字符串 str
BLOB 二进制(图片、文件) bytes
NUMERIC 数值(整数或浮点) int/float

常用约束:PRIMARY KEY(主键)、NOT NULL(非空)、UNIQUE(唯一)、DEFAULT(默认值)、AUTOINCREMENT(自增)。


五、增删改查 CRUD

下面用一个 students 表演示完整 CRUD。

5.1 INSERT:插入数据

单条插入:

复制代码
import sqlite3

conn = sqlite3.connect('school.db')
conn.row_factory = sqlite3.Row
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.execute(
    "INSERT INTO students (name, age, score) VALUES (?, ?, ?)",
    ('张三', 20, 88.5)
)
conn.commit()
print(f"新增记录 id = {cursor.lastrowid}")  # 获取自增 id

conn.close()

运行结果:

复制代码
新增记录 id = 1

批量插入(executemany):

复制代码
import sqlite3

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

students = [
    ('李四', 21, 92.0),
    ('王五', 22, 76.5),
    ('赵六', 20, 85.0),
    ('钱七', 23, 60.0),
    ('孙八', 21, 95.5),
]

# executemany 一次插入多条,比循环 execute 高效得多
cursor.executemany(
    "INSERT INTO students (name, age, score) VALUES (?, ?, ?)",
    students
)
conn.commit()
print(f"批量插入 {len(students)} 条")

conn.close()

运行结果:

复制代码
批量插入 5 条

5.2 SELECT:查询数据

复制代码
import sqlite3

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

# 查询全部
cursor.execute("SELECT * FROM students")
rows = cursor.fetchall()
print("全部学生:")
for r in rows:
    print(dict(r))   # sqlite3.Row 可转 dict

# 条件查询:分数 >= 80 的学生
cursor.execute("SELECT name, score FROM students WHERE score >= ? ORDER BY score DESC", (80,))
print("\n80分以上(降序):")
for r in cursor.fetchall():
    print(f"{r['name']}: {r['score']}")

# 分页:第 2 页,每页 2 条(LIMIT 数量 OFFSET 偏移)
page = 2
size = 2
cursor.execute("SELECT * FROM students LIMIT ? OFFSET ?", (size, (page-1)*size))
print(f"\n第{page}页:")
for r in cursor.fetchall():
    print(dict(r))

conn.close()

运行结果:

复制代码
全部学生:
{'id': 1, 'name': '张三', 'age': 20, 'score': 88.5}
{'id': 2, 'name': '李四', 'age': 21, 'score': 92.0}
{'id': 3, 'name': '王五', 'age': 22, 'score': 76.5}
{'id': 4, 'name': '赵六', 'age': 20, 'score': 85.0}
{'id': 5, 'name': '钱七', 'age': 23, 'score': 60.0}
{'id': 6, 'name': '孙八', 'age': 21, 'score': 95.5}

80分以上(降序):
孙八: 95.5
李四: 92.0
张三: 88.5
赵六: 85.0

第2页:
{'id': 3, 'name': '王五', 'age': 22, 'score': 76.5}
{'id': 4, 'name': '赵六', 'age': 20, 'score': 85.0}

三个取值方法:

  • fetchone():取一条(返回 Row 或 None)。
  • fetchall():取全部(返回列表)。
  • fetchmany(n):取 n 条。

5.3 UPDATE:更新数据

复制代码
import sqlite3

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

# 把钱七的分数改成 70
cursor.execute("UPDATE students SET score = ? WHERE name = ?", (70.0, '钱七'))
conn.commit()
print(f"更新了 {cursor.rowcount} 行")  # rowcount 表示受影响行数

# 验证
cursor.execute("SELECT name, score FROM students WHERE name = '钱七'")
print(dict(cursor.fetchone()))

conn.close()

运行结果:

复制代码
更新了 1 行
{'name': '钱七', 'score': 70.0}

5.4 DELETE:删除数据

复制代码
import sqlite3

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

# 删除 score < 70 的学生
cursor.execute("DELETE FROM students WHERE score < ?", (70,))
conn.commit()
print(f"删除了 {cursor.rowcount} 行")

cursor.execute("SELECT COUNT(*) FROM students")
print(f"剩余 {cursor.fetchone()[0]} 条")

conn.close()

运行结果:

复制代码
删除了 0 行
剩余 6 条

5.5 参数化查询防注入(重点)

永远不要用字符串拼接构造 SQL! 否则会有 SQL 注入风险。

错误示范(危险):

复制代码
name = input("请输入姓名:")
# 危险!用户输入 "张三' OR '1'='1" 就能查出所有数据
cursor.execute(f"SELECT * FROM students WHERE name = '{name}'")

正确做法:用 ? 占位符,参数以元组传入,sqlite3 会自动转义:

复制代码
name = input("请输入姓名:")
cursor.execute("SELECT * FROM students WHERE name = ?", (name,))
# 即使输入 "张三' OR '1'='1" 也只会被当作普通字符串,查不到数据

这是数据库操作最重要的安全原则 ,务必养成习惯:凡是用户输入拼进 SQL 的地方,一律参数化。


六、事务与 commit

6.1 什么是事务

事务(Transaction)是一组要么全部成功、要么全部回滚的操作单元,满足 ACID:

  • Atomicity 原子性:不可分割,全成或全败。
  • Consistency 一致性:执行前后数据保持一致。
  • Isolation 隔离性:并发事务互不干扰。
  • Durability 持久性:提交后永久保存。

典型场景:银行转账,A 扣 100,B 加 100,必须同时成功或同时失败。

6.2 commit 与 rollback

复制代码
import sqlite3

conn = sqlite3.connect('bank.db')
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS account (id INTEGER PRIMARY KEY, name TEXT, balance REAL)")
cursor.execute("INSERT OR REPLACE INTO account VALUES (1, 'A', 1000)")
cursor.execute("INSERT OR REPLACE INTO account VALUES (2, 'B', 1000)")
conn.commit()

def transfer(from_id, to_id, amount):
    try:
        cursor.execute("UPDATE account SET balance = balance - ? WHERE id = ?", (amount, from_id))
        cursor.execute("UPDATE account SET balance = balance + ? WHERE id = ?", (amount, to_id))
        conn.commit()   # 两条都成功才提交
        print(f"转账 {amount} 成功")
    except Exception as e:
        conn.rollback()  # 出错就回滚,恢复原状
        print(f"转账失败,已回滚:{e}")

transfer(1, 2, 200)

cursor.execute("SELECT * FROM account")
print([dict(r) for r in cursor.fetchall()])

conn.close()

运行结果:

复制代码
转账 200 成功
[{'id': 1, 'name': 'A', 'balance': 800.0}, {'id': 2, 'name': 'B', 'balance': 1200.0}]

说明:sqlite3 默认开启自动事务,执行写操作后必须 commit() 才真正落盘;出错时 rollback() 撤销未提交的改动。


七、用上下文管理器封装数据库操作

手动 close 容易遗漏,用 with 语句可以自动管理连接。sqlite3.Connection 本身就支持上下文管理器(自动 commit / rollback):

复制代码
import sqlite3

class DB:
    """简易数据库封装"""
    def __init__(self, path):
        self.conn = sqlite3.connect(path)
        self.conn.row_factory = sqlite3.Row

    def execute(self, sql, params=()):
        with self.conn:              # 自动 commit,异常自动 rollback
            return self.conn.execute(sql, params).fetchall()

    def execute_one(self, sql, params=()):
        with self.conn:
            return self.conn.execute(sql, params).fetchone()

    def close(self):
        self.conn.close()


db = DB('demo.db')
db.execute("CREATE TABLE IF NOT EXISTS t (id INTEGER PRIMARY KEY, v TEXT)")
db.execute("INSERT INTO t (v) VALUES (?)", ('hello',))
rows = db.execute("SELECT * FROM t")
for r in rows:
    print(dict(r))
db.close()

运行结果:

复制代码
{'id': 1, 'v': 'hello'}

优势:异常时自动回滚,代码更安全;不用手动写 try/finally/close。


八、多表关联查询

实际业务中数据是分多张表存的,通过外键 关联,用 JOIN 查询。

8.1 建两张表

复制代码
import sqlite3

conn = sqlite3.connect('blog.db')
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA foreign_keys = ON")   # 开启外键约束(SQLite 默认关闭)
cursor = conn.cursor()

cursor.execute('''
CREATE TABLE IF NOT EXISTS articles (
    id    INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    author TEXT
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS comments (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    content    TEXT NOT NULL,
    article_id INTEGER,
    FOREIGN KEY (article_id) REFERENCES articles(id)
)
''')

cursor.execute("INSERT INTO articles (title, author) VALUES (?, ?)", ('Python入门', '陈'))
cursor.execute("INSERT INTO articles (title, author) VALUES (?, ?)", ('数据库进阶', '李'))
cursor.execute("INSERT INTO comments (content, article_id) VALUES (?, ?)", ('写得真好', 1))
cursor.execute("INSERT INTO comments (content, article_id) VALUES (?, ?)", ('学到了', 1))
cursor.execute("INSERT INTO comments (content, article_id) VALUES (?, ?)", ('求更新', 2))
conn.commit()

8.2 INNER JOIN 查询

复制代码
# 查每篇文章及其评论
cursor.execute('''
SELECT a.title, a.author, c.content
FROM articles a
INNER JOIN comments c ON a.id = c.article_id
ORDER BY a.id
''')
for r in cursor.fetchall():
    print(f"《{r['title']}》({r['author']}) - 评论:{r['content']}")

conn.close()

运行结果:

复制代码
《Python入门》(陈) - 评论:写得真好
《Python入门》(陈) - 评论:学到了
《数据库进阶》(李) - 评论:求更新

INNER JOIN 只返回两表都匹配的行;还有 LEFT JOIN(左表全保留)、RIGHT JOIN、FULL JOIN。


九、进阶:SQLAlchemy ORM

直接写 SQL 虽然灵活,但字符串拼接不直观、表结构变更难维护。**ORM(对象关系映射)**把表映射成 Python 类,把行映射成对象,操作数据库就像操作对象一样。

9.1 安装

复制代码
pip install sqlalchemy

9.2 定义模型

复制代码
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id    = Column(Integer, primary_key=True)
    name  = Column(String, nullable=False)
    posts = relationship('Post', backref='author')   # 一对多关系

class Post(Base):
    __tablename__ = 'posts'
    id      = Column(Integer, primary_key=True)
    title   = Column(String)
    user_id = Column(Integer, ForeignKey('users.id'))

engine = create_engine('sqlite:///orm.db', echo=False)
Base.metadata.create_all(engine)          # 自动建表
Session = sessionmaker(bind=engine)
session = Session()

9.3 增删改查

复制代码
# 增
u = User(name='张三')
session.add(u)
session.commit()

p = Post(title='第一篇', author=u)   # 通过关系直接关联
session.add(p)
session.commit()

# 查
user = session.query(User).filter_by(name='张三').first()
print(user.name, [post.title for post in user.posts])

# 改
user.name = '张三丰'
session.commit()

# 删
session.delete(p)
session.commit()

session.close()

运行结果:

复制代码
张三 ['第一篇']

ORM 让代码更面向对象、更易维护,但会有一点点性能损耗。小型项目可直写 SQL,中大型项目推荐 ORM。


十、实战案例:用 SQLite 实现通讯录管理

把前面学的串起来,写一个完整的命令行通讯录程序。

复制代码
import sqlite3

class ContactBook:
    def __init__(self, db_path='contacts.db'):
        self.conn = sqlite3.connect(db_path)
        self.conn.row_factory = sqlite3.Row
        self._init_db()

    def _init_db(self):
        self.conn.execute('''
        CREATE TABLE IF NOT EXISTS contacts (
            id    INTEGER PRIMARY KEY AUTOINCREMENT,
            name  TEXT NOT NULL,
            phone TEXT NOT NULL,
            email TEXT,
            group_name TEXT DEFAULT '默认'
        )
        ''')
        self.conn.commit()

    def add(self, name, phone, email=None, group_name='默认'):
        self.conn.execute(
            "INSERT INTO contacts (name, phone, email, group_name) VALUES (?,?,?,?)",
            (name, phone, email, group_name)
        )
        self.conn.commit()
        print(f"已添加:{name}")

    def list_all(self):
        rows = self.conn.execute("SELECT * FROM contacts ORDER BY id").fetchall()
        for r in rows:
            print(f"[{r['id']}] {r['name']} | {r['phone']} | {r['email']} | {r['group_name']}")

    def search(self, keyword):
        like = f"%{keyword}%"
        rows = self.conn.execute(
            "SELECT * FROM contacts WHERE name LIKE ? OR phone LIKE ?",
            (like, like)
        ).fetchall()
        print(f"搜索 '{keyword}' 结果:")
        for r in rows:
            print(f"  {r['name']} - {r['phone']}")

    def update(self, cid, name=None, phone=None):
        if name:
            self.conn.execute("UPDATE contacts SET name=? WHERE id=?", (name, cid))
        if phone:
            self.conn.execute("UPDATE contacts SET phone=? WHERE id=?", (phone, cid))
        self.conn.commit()
        print(f"已更新 id={cid}")

    def delete(self, cid):
        self.conn.execute("DELETE FROM contacts WHERE id=?", (cid,))
        self.conn.commit()
        print(f"已删除 id={cid}")

    def close(self):
        self.conn.close()


# 使用示例
if __name__ == '__main__':
    cb = ContactBook()
    cb.add('张三', '13800138000', 'zhangsan@qq.com', '同事')
    cb.add('李四', '13900139000', 'lisi@qq.com', '家人')
    cb.add('王五', '13700137000', group_name='朋友')
    print("--- 通讯录 ---")
    cb.list_all()
    cb.search('张')
    cb.update(1, phone='13800000000')
    cb.delete(2)
    print("--- 更新后 ---")
    cb.list_all()
    cb.close()

运行结果:

复制代码
已添加:张三
已添加:李四
已添加:王五
--- 通讯录 ---
[1] 张三 | 13800138000 | zhangsan@qq.com | 同事
[2] 李四 | 13900139000 | lisi@qq.com | 家人
[3] 王五 | 13700137000 | None | 朋友
搜索 '张' 结果:
  张三 - 13800138000
已更新 id=1
已删除 id=2
--- 更新后 ---
[1] 张三 | 13800000000 | zhangsan@qq.com | 同事
[3] 王五 | 13700137000 | None | 朋友

十一、索引与查询优化

当表数据量变大(上万、几十万行),没有索引的查询会越来越慢,因为数据库只能全表扫描------一行行遍历比对。给常作为查询条件的列建索引,可以大幅提速。

11.1 创建索引

复制代码
import sqlite3

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

# 给 name 列建普通索引
cursor.execute("CREATE INDEX IF NOT EXISTS idx_name ON students(name)")

# 给 score 列建索引(常用于排序、范围查询)
cursor.execute("CREATE INDEX IF NOT EXISTS idx_score ON students(score)")

conn.commit()
print("索引创建成功")
conn.close()

运行结果:

复制代码
索引创建成功

11.2 查看查询计划

用 EXPLAIN QUERY PLAN 可以看数据库是否走索引:

复制代码
cursor.execute("EXPLAIN QUERY PLAN SELECT * FROM students WHERE name = '张三'")
print(cursor.fetchone())

如果输出里出现 USING INDEX idx_name,说明走了索引;如果只有 SCAN TABLE,说明全表扫描,需要加索引。

11.3 索引的代价

索引不是越多越好:

  • 占空间:每个索引本身也是一棵树,要额外存储。
  • 拖慢写入:每次 INSERT/UPDATE/DELETE 都要同步更新索引。
  • 只加在常查询的列:很少查的列没必要建索引。

经验:主键自带索引;外键、常 WHERE / ORDER BY / JOIN 的列建议加索引。


十二、最佳实践

  1. 始终用参数化查询 (? 占位符),杜绝 SQL 注入。
  2. 用 with conn: 或上下文管理器管理事务,自动 commit/rollback。
  3. 设置 row_factory = sqlite3.Row,用列名取值,代码可读性更高。
  4. 开启外键约束 :PRAGMA foreign_keys = ON(SQLite 默认关闭)。
  5. 批量插入用 executemany ,比循环 execute 快一个数量级。
  6. 给常查询的列建索引 :CREATE INDEX idx_name ON students(name)。
  7. 写操作及时 commit(),避免连接异常关闭丢数据。
  8. 多线程下每个线程用独立连接,SQLite 不支持跨线程共享连接。
  9. 生产环境定期备份 .db 文件 ,或用 VACUUM INTO 导出。
  10. 数据量大、并发高时考虑换 MySQL/PostgreSQL。

十三、小结

本文从"为什么用数据库"讲起,介绍了表、行、主键、SQL 等基础概念,对比了 SQLite 与 MySQL/PostgreSQL 的差异;然后用 Python 内置的 sqlite3 模块完整演示了连接、建表、增删改查、参数化防注入、事务、上下文管理器、多表 JOIN ;接着进阶了解了 SQLAlchemy ORM 的用法;最后用一个通讯录管理实战案例把所有知识点串起来。

核心要点回顾:

  • sqlite3 是 Python 标准库,零安装,适合学习和小型项目。
  • CRUD 四大操作 :INSERT / SELECT / UPDATE / DELETE。
  • 参数化查询是防 SQL 注入的底线。
  • 事务 保证数据一致性,commit 提交、rollback 回滚。
  • ORM 让操作数据库像操作对象,适合中大型项目。

下一篇我们将学习 正则表达式------文本处理的瑞士军刀,敬请期待!


作者:全栈陈序员 系列:Python 基础语法入门 专栏:Python 从零到一 如果对你有帮助,欢迎点赞、收藏、关注 ❤️

相关推荐
m0_639906941 小时前
MySQL 索引底层原理|B + 树、聚簇索引、非聚簇索引、覆盖索引
数据库·mysql
马剑威(威哥爱编程)1 小时前
【HarmonyOS 7 小艺智能体实战】09:上架前先把“关”过一遍——合规自查与素材准备
人工智能·华为·harmonyos
moonsims1 小时前
AiBrainBox-UGV 通 · 感 · 算 · 控一体化地面机器人边缘智能平台 规格书
大数据·网络·人工智能·无人机
m0_674571971 小时前
手写能跑的AI agent循环 (纯 Python,无框架)原理+示例讲解
人工智能
jnrjian1 小时前
Oracle RAC crs stop时force 关闭DB,另一个node会立即进行recover
数据库·oracle
次次皮1 小时前
Oracle判空踩坑(对比mysql)
数据库·mysql·oracle
大力财经1 小时前
山东山大华天软件与阿里云达成合作,旗下产品CrownCAD已接入Qoder
人工智能·阿里云·云计算
徐小夕@趣谈前端1 小时前
老旧Word格式全支持!JitWord协同AI文档4.1,让Word文档无缝迁移到Web端
前端·人工智能·word
严谨的麻辣烫1 小时前
固定 IP 稳定性如何检测?使用 Python 实现网络连通性与耗时监控
开发语言·数据库·php