Lab 第4章 · 数据库上机实操(SQLite 版)
Hands-on Lab

Python 操作数据库 上机实操

使用 Python 内置的 SQLite 数据库完成实验。无需安装任何软件,只要电脑有 Python 就能直接运行。
请将每个脚本的运行结果截图保存,整理到实验报告模板中提交。

2
实验数量
3
脚本文件
45 min
建议时长
3 张截图
提交内容

🔧 实验前准备

本实验使用 SQLite 数据库。SQLite 是 Python 自带的轻量级数据库,import sqlite3 即可使用,不需要安装 MySQL、不需要启动任何服务、不需要输入密码

只要你的电脑装了 Python(任何版本都行),就可以直接运行以下所有代码。
SQLite 和 MySQL 的操作思路完全一致,都是「连接 → 游标 → 执行SQL → 提交 → 关闭」。主要区别见下表:
对比项MySQL(PyMySQL)SQLite(sqlite3)
导入模块import pymysqlimport sqlite3(内置)
连接方式pymysql.connect(host, user, password, database)sqlite3.connect('文件名.db')
参数占位符%s?
是否需要安装需要安装 MySQL 服务 + pip install pymysqlPython 自带,什么都不用装
数据存储MySQL 服务器中一个本地 .db 文件
实验一
🐍

Python 操作 SQLite — 建表、插入与查询 使用 Python 内置 sqlite3 模块完成从创建数据库、创建表到插入数据、多种方式查询数据的完整流程

掌握 Python 操作数据库的核心流程:建立连接 → 获取游标 → 执行 SQL → 提交事务 → 获取查询结果 → 关闭连接。这个流程不论是 SQLite 还是 MySQL 都完全一样。

脚本一:建表 + 插入数据(lab4_create.py)

新建文件 lab4_create.py,将下面的完整代码复制进去运行。运行后会在同目录下自动生成一个 student.db 数据库文件。

Python — lab4_create.py
# =======================================================
# 脚本一:创建数据库、数据表,并插入学生记录
# =======================================================
# sqlite3 是 Python 自带的模块,无需安装,直接导入即可
import sqlite3

# ----------------------------------------------------------
# 第1步:连接数据库
# ----------------------------------------------------------
# sqlite3.connect() 会连接到指定的数据库文件
# 如果文件不存在,会自动创建一个新的数据库文件
# 这里会在当前目录下生成 student.db 文件
# (对比 MySQL:pymysql.connect(host=..., user=..., password=..., database=...))
conn = sqlite3.connect('student.db')

# ----------------------------------------------------------
# 第2步:获取游标对象
# ----------------------------------------------------------
# 游标(cursor)是用来执行 SQL 语句的工具
# 你可以把它想象成数据库上的一根"手指",指哪查哪
cursor = conn.cursor()

print("✅ 已连接数据库 student.db")

# ----------------------------------------------------------
# 第3步:创建学生信息表 tb_students
# ----------------------------------------------------------
# CREATE TABLE IF NOT EXISTS:如果表不存在就创建,已存在就跳过
# 字段说明:
#   id          - 整数,主键(每行的唯一标识),自增长
#   name        - 文本,学生姓名,不允许为空
#   age         - 整数,学生年龄,默认值 18
#   major       - 文本,所学专业,默认值 '未分配'
#   enroll_date - 文本,入学日期(SQLite 没有 DATE 类型,用 TEXT 存储日期字符串)
create_sql = """
CREATE TABLE IF NOT EXISTS tb_students (
    id          INTEGER   PRIMARY KEY AUTOINCREMENT,
    name        TEXT      NOT NULL,
    age         INTEGER   DEFAULT 18,
    major       TEXT      DEFAULT '未分配',
    enroll_date TEXT
)
"""
cursor.execute(create_sql)
print("✅ 数据表 tb_students 已创建")

# ----------------------------------------------------------
# 第4步:插入 5 条学生记录
# ----------------------------------------------------------
# 使用参数化查询:用 ? 作为占位符(MySQL 中用 %s)
# 这样可以防止 SQL 注入攻击,也更安全
insert_sql = "INSERT INTO tb_students (name, age, major, enroll_date) VALUES (?, ?, ?, ?)"

# 准备数据:一个列表,每个元素是一个元组,对应一行记录
students = [
    ('张三', 20, '计算机科学', '2024-09-01'),
    ('李四', 19, '软件工程',   '2024-09-01'),
    ('王五', 21, '计算机科学', '2023-09-01'),
    ('赵六', 20, '数据科学',   '2024-09-01'),
    ('孙七', 22, '人工智能',   '2023-09-01'),
]

# executemany() 批量插入:一次性将列表中的所有数据插入表中
# 比用 for 循环逐条 execute() 效率更高
cursor.executemany(insert_sql, students)

# ----------------------------------------------------------
# 第5步:提交事务
# ----------------------------------------------------------
# 增删改操作必须调用 commit(),否则数据不会真正保存到文件中
# 只有查询(SELECT)不需要 commit()
conn.commit()
print(f"✅ 成功插入 {len(students)} 条学生记录")

# ----------------------------------------------------------
# 第6步:查询并展示所有数据,验证插入是否成功
# ----------------------------------------------------------
# execute() 执行 SELECT 查询后,用 fetchall() 获取全部结果
# 返回的结果是一个列表,每个元素是一个元组(代表一行数据)
cursor.execute("SELECT * FROM tb_students")
results = cursor.fetchall()

print("\n📋 当前 tb_students 表中的所有记录:")
print("-" * 60)
print(f"{'学号':<6}{'姓名':<8}{'年龄':<6}{'专业':<12}{'入学日期'}")
print("-" * 60)
for row in results:
    # row 是元组,row[0]=id, row[1]=name, row[2]=age, row[3]=major, row[4]=enroll_date
    print(f"{row[0]:<6}{row[1]:<8}{row[2]:<6}{row[3]:<12}{row[4]}")

print(f"\n共 {len(results)} 条记录")

# ----------------------------------------------------------
# 第7步:关闭游标和连接,释放资源
# ----------------------------------------------------------
# 用完数据库后一定要关闭,就像用完水龙头要关上一样
cursor.close()
conn.close()
print("🔒 数据库连接已关闭")
运行后会发生什么?
脚本运行结束后,你会在 lab4_create.py 同目录下看到一个新文件 student.db,这就是你的数据库。SQLite 的数据库就是一个普通文件,可以拷贝、删除、发送给别人。

脚本二:多种查询方式(lab4_query.py)

新建文件 lab4_query.py(放在和脚本一相同的目录下),复制以下代码。

Python — lab4_query.py
# =======================================================
# 脚本二:多种查询方式演示
# =======================================================
import sqlite3

# 连接到已有的数据库文件(脚本一创建的 student.db)
conn = sqlite3.connect('student.db')
cursor = conn.cursor()

# ----------------------------------------------------------
# 查询1:条件查询 —— WHERE 过滤年龄 >= 20 的学生
# ----------------------------------------------------------
# WHERE 子句用来设置查询条件,只返回满足条件的行
# 注意 SQLite 的占位符是 ?(不是 MySQL 的 %s)
print("🔍 查询1:年龄 >= 20 的学生")
print("-" * 45)
cursor.execute("SELECT name, age, major FROM tb_students WHERE age >= ?", (20,))
for row in cursor.fetchall():
    print(f"  姓名: {row[0]},  年龄: {row[1]},  专业: {row[2]}")

# ----------------------------------------------------------
# 查询2:模糊查询 —— LIKE 查找专业名中包含"科"字的学生
# ----------------------------------------------------------
# LIKE 用于模式匹配,% 代表任意多个字符
# '%科%' 的意思是:只要"科"出现在任意位置就匹配
print(f'\n🔍 查询2:专业包含"科"字的学生')
print("-" * 45)
cursor.execute("SELECT name, major FROM tb_students WHERE major LIKE ?", ('%科%',))
for row in cursor.fetchall():
    print(f"  姓名: {row[0]},  专业: {row[1]}")

# ----------------------------------------------------------
# 查询3:统计函数 —— COUNT / AVG / MAX / MIN
# ----------------------------------------------------------
# 统计函数对整列数据进行计算,返回单个结果
# COUNT(*) 统计总行数,AVG() 求平均值,MAX() 最大值,MIN() 最小值
print(f"\n🔍 查询3:统计信息")
print("-" * 45)
cursor.execute("SELECT COUNT(*), AVG(age), MAX(age), MIN(age) FROM tb_students")
stats = cursor.fetchone()   # fetchone() 只取一条结果(因为统计函数只返回一行)
print(f"  总人数:   {stats[0]}")
print(f"  平均年龄: {stats[1]:.1f}")     # :.1f 保留一位小数
print(f"  最大年龄: {stats[2]}")
print(f"  最小年龄: {stats[3]}")

# ----------------------------------------------------------
# 查询4:排序 + 限制条数 —— ORDER BY ... LIMIT
# ----------------------------------------------------------
# ORDER BY age DESC:按年龄降序排列(从大到小)
# LIMIT 3:只取前 3 条结果
print(f"\n🔍 查询4:按年龄降序排列(前3名)")
print("-" * 45)
cursor.execute("SELECT name, age FROM tb_students ORDER BY age DESC LIMIT 3")
for i, row in enumerate(cursor.fetchall(), 1):
    print(f"  第{i}名: {row[0]}, {row[1]}岁")

# ----------------------------------------------------------
# 查询5:分组统计 —— GROUP BY 按专业分组统计人数
# ----------------------------------------------------------
# GROUP BY major:将相同专业的行分为一组
# 再对每组使用 COUNT(*) 统计该组有多少人
print(f"\n🔍 查询5:各专业学生人数")
print("-" * 45)
cursor.execute("SELECT major, COUNT(*) as cnt FROM tb_students GROUP BY major")
for row in cursor.fetchall():
    print(f"  {row[0]}: {row[1]} 人")

# ----------------------------------------------------------
# 关闭连接
# ----------------------------------------------------------
# 查询操作不修改数据,所以不需要 commit(),直接关闭即可
cursor.close()
conn.close()
print("\n🔒 数据库连接已关闭")
截图 1:运行 lab4_create.py 的完整输出(包括连接成功、建表、插入记录数、5条学生数据表格)
截图 2:运行 lab4_query.py 的完整输出(包括5个查询的结果)
实验二
🛡️

带异常处理的修改与删除操作 使用 try / except / finally 结构完成数据的修改和删除,掌握安全操作数据库的最佳实践

掌握 Python 操作数据库的标准模板try → commit / except → rollback / finally → close。这个模板在 SQLite 和 MySQL 中完全通用,是实际开发中必须掌握的编码规范。

脚本三:安全的修改与删除(lab4_safe.py)

新建文件 lab4_safe.py(同目录),复制以下代码运行。

Python — lab4_safe.py
# =======================================================
# 脚本三:带异常处理的修改与删除操作
# =======================================================
# 本脚本演示数据库操作的"黄金模板":
#   try   → 执行操作,成功就 commit(提交)
#   except → 出错就 rollback(回滚,撤销所有修改)
#   finally → 无论成功失败,都 close(关闭连接)
# 这个结构在 SQLite 和 MySQL 中完全一样!
import sqlite3

# ----------------------------------------------------------
# 建立连接
# ----------------------------------------------------------
conn = sqlite3.connect('student.db')

try:
    # 获取游标
    cursor = conn.cursor()

    # ----------------------------------------------------------
    # 操作前:查看当前所有数据(方便和操作后对比)
    # ----------------------------------------------------------
    print("📋 操作前的数据:")
    print("-" * 55)
    cursor.execute("SELECT * FROM tb_students")
    for row in cursor.fetchall():
        print(f"  {row[0]} | {row[1]:<4} | {row[2]}岁 | {row[3]}")

    # ----------------------------------------------------------
    # 操作1:UPDATE —— 将"李四"的专业改为"人工智能"
    # ----------------------------------------------------------
    # UPDATE 语法:UPDATE 表名 SET 列名 = 新值 WHERE 条件
    # 一定要带 WHERE!否则会把所有行都改了
    sql_update1 = "UPDATE tb_students SET major = ? WHERE name = ?"
    cursor.execute(sql_update1, ('人工智能', '李四'))
    # cursor.rowcount 返回本次操作影响了多少行
    print(f"\n✏️ 操作1 UPDATE:修改了 {cursor.rowcount} 条记录(李四的专业 → 人工智能)")

    # ----------------------------------------------------------
    # 操作2:UPDATE —— 将所有"计算机科学"专业学生的年龄 +1
    # ----------------------------------------------------------
    # SET age = age + 1:在原来的年龄基础上加 1
    # 这里 WHERE 条件匹配了多行,所以会修改多条记录
    sql_update2 = "UPDATE tb_students SET age = age + 1 WHERE major = ?"
    cursor.execute(sql_update2, ('计算机科学',))
    print(f"✏️ 操作2 UPDATE:{cursor.rowcount} 名计算机科学学生年龄 +1")

    # ----------------------------------------------------------
    # 操作3:DELETE —— 删除年龄小于 20 的学生
    # ----------------------------------------------------------
    # DELETE FROM 语法:DELETE FROM 表名 WHERE 条件
    # 同样一定要带 WHERE!否则会删除所有数据
    sql_delete = "DELETE FROM tb_students WHERE age < ?"
    cursor.execute(sql_delete, (20,))
    print(f"🗑️ 操作3 DELETE:删除了 {cursor.rowcount} 条记录(年龄 < 20 的学生)")

    # ----------------------------------------------------------
    # 操作后:再次查询,对比数据变化
    # ----------------------------------------------------------
    print(f"\n📋 操作后的数据:")
    print("-" * 55)
    cursor.execute("SELECT * FROM tb_students")
    final = cursor.fetchall()
    for row in final:
        print(f"  {row[0]} | {row[1]:<4} | {row[2]}岁 | {row[3]}")
    print(f"共 {len(final)} 条记录")

    # ----------------------------------------------------------
    # 全部操作成功,提交事务
    # ----------------------------------------------------------
    # commit() 让所有修改永久生效,写入到 .db 文件中
    conn.commit()
    print("\n✅ 所有操作已成功提交!")

except Exception as e:
    # ----------------------------------------------------------
    # 任何一步出错,撤销本次所有修改
    # ----------------------------------------------------------
    # rollback() 回滚事务:把数据恢复到 try 开始前的状态
    # 这样就不会出现"改了一半"的不一致数据
    conn.rollback()
    print(f"\n❌ 操作失败,已全部回滚: {e}")

finally:
    # ----------------------------------------------------------
    # 无论成功或失败,最终都要关闭连接
    # ----------------------------------------------------------
    # finally 块中的代码一定会执行,确保资源不泄露
    cursor.close()
    conn.close()
    print("🔒 数据库连接已关闭")
关键理解:为什么要用 try / except / finally?

想象你去银行转账:从 A 账户扣了 1000 元,但往 B 账户加钱时系统出错了。如果没有回滚机制,A 的钱就凭空消失了!rollback() 就是让银行系统把 A 的钱退回去。commit() 则是确认"扣款和到账都成功了,这笔交易正式生效"。finally 保证不管转账成功还是失败,你最后都会走出银行(关闭连接)。
截图 3:运行 lab4_safe.py 的完整输出(包括操作前数据、3个操作的影响行数、操作后数据、提交成功和关闭信息)
提交清单

📎 实验报告截图清单

完成全部实验后,将以下 3 张截图按顺序粘贴到学校的 Word 实验报告模板中提交。

实验一:建表 + 插入 + 查询(2 张)

实验二:异常处理 + 修改删除(1 张)

运行顺序很重要!请严格按照 脚本一 → 脚本二 → 脚本三 的顺序运行。脚本一创建数据库文件和数据,脚本二和三依赖这些数据。

如果需要重新开始,只要删除 student.db 文件,然后从脚本一重新运行即可。
截图要求:请确保截图清晰完整,能看到终端中的全部输出。
三个 .py 文件必须放在同一个文件夹下,因为它们共用同一个 student.db 数据库文件。