简介:面向高校Python课程的一份数据库操作教学课件,聚焦Python与数据库的交互,重点讲解SQLite这一轻量级关系型数据库的使用。内容从数据库基础入手,介绍常见数据库类型(关系型、键值存储、文档型、图数据库)及DBMS的主要功能,系统梳理SQL语法,包括创建表、删除表、查询、where条件过滤、分组统计等,并说明SQLite五种数据类型及灵活存储机制。课件详细讲解Python操作数据库的核心API,涵盖sqlite3连接、游标执行、数据插入、查询获取、连接关闭等步骤,最后通过一个综合案例完整演示从建库、建表到增删改查的实践流程,案例贴近教学与复习场景。资源包内为1个pptx课件,压缩包大小3.46MB,结构紧凑、即下即用。目前已有853人学习下载,内容系统、示例直观,能帮助读者快速构建Python数据库操作知识体系,适合高校教师备课、课堂教学以及学生期末复习巩固。
1. 为什么教学课件里的 SQLite 反而是最值得拆的数据库
期末复习最怕什么?不是语法背不住,而是数据库连接这一步就把人劝退了。MySQL 要装服务、配账号、处理字符集,折腾半小时还没看到一行数据。这个课件没有选那些重型数据库,而是从 Python 标准库自带的 sqlite3 讲起,把数据库基础、SQL 语法、Python DB-API 和操作流程串成一条线。它面向高校教师教学,也适合学生课后复习,核心价值在于:用最小的环境成本,把建表、增删改查、分组统计、事务提交这些高频动作全部跑通。无论你是在做数据库课程设计,还是准备 Python 期末复习,都可以把它当作一份可复现的实操笔记。
2. 先理清关系型数据库的模型:二维表、主键与 SQLite 的类型亲和
2.1 关系模型中的基础概念
课件开篇就从数据库基础入手,这其实是很关键的铺垫。关系型数据库把复杂的数据结构归结成二维表:一张表就是一组行和列,行在数据库中称为记录或元组,列称为字段或属性。比如 school 表,一行就是一所高校,school_code 是唯一标识,这就是主键。这个模型的好处是,无论是 MySQL、SQL Server 还是 SQLite,操作的对象和返回的结果都是二维表,所以 SQL 语句可以在不同数据库之间直接移植。
需要说明的是,关系模型里的域指字段的取值范围,分量是某个记录里某个字段的具体值。这些概念在课件里一笔带过,但如果你要在期末复习或课程设计答辩里解释表结构设计,能把这些术语和实际表字段对应上,会显得你对数据库基础有真正的理解。另外,表与表之间通过外键建立关联,虽然 SQLite 默认不强制启用外键约束,但设计表结构时仍然要有一对多或多对多的意识。比如后续要建学生表,学生表里就可以加一个 school_code 字段指向这里的主键。
2.2 SQLite 的五种存储类型与类型亲和
SQLite 与 MySQL 的一个显著差异在于存储类型。SQLite 内部只支持五种基本数据类型:NULL、INTEGER、REAL、TEXT、BLOB。它虽然也接受 varchar(n)、char(n) 这类写法,但本质上不会像 MySQL 那样严格校验字段长度和类型。也就是说,你在建表语句里写 school_code char(6),SQLite 实际存储时仍按 TEXT 处理,存一个 10 个字符的字符串也不会报错。
这种设计叫类型亲和,对教学场景是好事:你不用一开始就纠结字段长度、精度、字符集,可以先把注意力放在 SQL 逻辑和 Python 代码上。但也要提醒,生产环境里如果依赖 SQLite 做严格数据校验,需要自己在应用层控制,或者改用 MySQL、Oracle 这类字段约束更严格的数据库。另外,如果声明 integer primary key,SQLite 会自动把它变成自增 rowid 的别名,适合做学生表、成绩表这类需要自动编号的场景。下面的表列出了五种类型的典型用途。
| 类型 | 用途 | 示例 |
|---|---|---|
| NULL | 空值 | 未填写的字段 |
| INTEGER | 整数、自增主键 | age、id |
| REAL | 浮点数 | 成绩、评分 |
| TEXT | 字符串 | 学校名称、省份 |
| BLOB | 二进制对象 | 图片、序列化数据 |
2.3 用建表语句把概念落成实践
把概念落到代码,最常见的就是课件里的 school 表创建。这里我一般会强调两条规则:建表前用 if not exists 判断,删表前用 if exists 判断。否则重复执行脚本时会直接报错,浪费排错时间。
create table if not exists school ( school_code char(6) primary key, school varchar(50), province varchar(30), is_985 varchar(10), is_211 varchar(10), is_self_marking varchar(10), school_type varchar(10) );这段 SQL 里,primary key 指定 school_code 作为主键,它是每条记录的唯一标识。varchar(50) 这类声明在 SQLite 中不强制长度,但建议保留,因为当你要把表结构迁移到 MySQL 时,这些声明就是现成的类型模板。删除表使用 drop table if exists school,执行后表结构连同数据一起消失,和 delete 只删数据的行为完全不同。
这里还需要注意一个容易混淆的点:drop table 是删除整张表,delete from 是删除数据但保留表结构。课件里专门强调了这个区别,实际写代码时也要先想清楚。建表语句中的字段顺序同样重要,它决定了 insert 省略字段列表时的默认插入顺序,这一点会和后面的 SQL 实操直接相关。
3. 从增删改查开始:SQL 语句在 Python 课件里的高频用法
3.1 select 查询的完整结构与 where 条件组合
SQL 里最常用、也最能体现数据库能力的是 select 查询。课件给出的语法结构是:select 字段列表 from 表名 where 查询条件 group by 分组字段 order by 字段 [asc|desc]。实际执行时,数据库会先过滤 where,再分组,再排序,最后投影出字段列表。理解这个执行顺序,对排查查询结果异常很有帮助。
where 子句支持五类常见条件,可以把它们整理成一张速查表。比较运算用于精确匹配,between 用于范围筛选,in 用于匹配一组值,and 和 or 组合多条件,like 配合百分号做模糊匹配。下面这张表覆盖了课件中的例子和最常见的扩展。
| 运算符类型 | 语法示例 | 说明 |
|---|---|---|
| 比较 | where province = '江西省' | 等值或大小比较 |
| 范围 | where school_code between '10404' and '10410' | 闭区间筛选 |
| 列表 | where province in ('江西省','湖南省') | 匹配多个离散值 |
| 逻辑 | where is_985='是' and is_211='是' | 多条件组合 |
| 模糊 | where school like '%师范%' | 包含关键字搜索 |
这里最容易犯的错误是把单引号和双引号混用。SQL 标准里字符串一般用单引号,SQLite 也兼容双引号,但推荐统一用单引号,避免和 Python 字符串的引号产生冲突。模糊匹配里的百分号是通配符,%师范% 表示包含师范二字的任意字符串,这类写法在爬虫数据过滤和关键词匹配场景中非常常见。
3.2 分组统计与排序:聚合函数是期末复习的重点
如果只做条件查询,还体会不到 SQL 的统计能力。group by 子句用于把相同字段值的记录合并成一组,通常和聚合函数一起使用。课件里的例子是统计各个省份的高校数量,语句本身很简短,但信息密度很高。
select province, count(*) as 学校数 from school group by province order by 学校数 desc;这条语句的逻辑是:先按 province 分组,再对每组调用 count(*) 统计行数,最后按别名学校数降序排列。as 关键字可以省略,但建议保留,因为 Python 端通过游标读取结果时,列名就是这里的别名,后续配合 Row 对象按字段名访问会更方便。
常见的聚合函数还有 sum(列名)、max(列名)、min(列名)、avg(列名)。这里有一个容易被忽略的细节:count() 统计的是记录行数,count(列名) 会跳过该列为 NULL 的行。如果字段允许为空,两个写法可能得到不同结果。另外,group by 之后如果还想过滤分组结果,需要用到 having,而不是 where。where 在分组前执行,having 在分组后执行,例如只显示高校数大于 10 的省份,就要写成 having count() > 10。
3.3 insert、update、delete 的注意点
增删改查里的增删改虽然语法简单,但坑最多。insert 语句有两种写法:指定字段列表,或者省略字段列表直接给全部值。指定字段时,字段顺序和 values 里的值顺序必须一一对应;省略字段列表时,值的顺序必须和建表语句里的字段顺序完全一致,否则数据就错位了。
insert into school(school, province) values('江西财经大学', '江西省'); update school set school = '赣南师范大学' where school = '赣南师范学院'; delete from school where province = '江西省';update 和 delete 都要求必须带上明确的 where 条件,否则会更新或删除整张表。实际开发中我建议先写 where 条件,再写 set 或 delete 关键字,从书写顺序上避免误操作。如果你确实要清空表,delete from 表名 仍然会保留表结构,而 drop table 表名 会把表彻底删掉。
提示:update 和 delete 语句在正式环境执行前,最好先用同条件 select 确认一下匹配范围。
再补充一个批量插入场景。如果数据来自爬虫或外部文件,逐条 insert 效率很低。更好的做法是在 Python 端用 executemany,把数据整理成列表再一次性写入,这个内容在下一章展开。
4. 深入 Python DB-API:从连接对象到游标的执行链路
4.1 DB-API 规范与三类核心对象
Python 操作数据库并不需要为每种数据库单独记一套语法,因为 PEP 249 定义了 DB-API 规范,sqlite3、PyMySQL、psycopg2 都实现了同一套接口。这个抽象层的价值在于:业务代码里只依赖 connect()、cursor()、execute()、commit() 这些通用方法,未来从 SQLite 换到 MySQL,只需要改连接方式和驱动包,SQL 部分和数据处理逻辑基本不用动。
DB-API 规范里最常用的是三个类:Connection 负责管理连接和事务,Cursor 负责执行 SQL 并获取结果,Row 表示结果集中的一行。实际开发中,很多人容易忽略 rowcount 和 arraysize。cursor.rowcount 返回的是受影响行数,比如 update 匹配了多少行;arraysize 控制 fetchmany(size) 一次取多少行。处理几十万行数据时,用 fetchall 一次性加载会占用大量内存,改成循环 fetchmany(500) 会平滑很多。教学中我会把三个类的核心方法整理成一张速查表。
| 类 | 核心方法或属性 | 说明 |
|---|---|---|
| Connection | close()、commit()、rollback()、cursor() | 管理连接和事务 |
| Cursor | execute()、executemany()、fetchall()、fetchmany(size) | 执行 SQL、获取结果 |
| Row | keys()、下标访问 | 按字段名或位置取值 |
4.2 Python 操作 SQLite 的标准流程
课件给出了一个标准七步流程,我在项目里会把它浓缩成四个阶段:连接、游标、执行、清理。下面是一个可以完整运行的示例代码。
import sqlite3 # 1. 连接数据库,文件不存在时会自动创建 conn = sqlite3.connect('school.db') # 2. 创建游标 cursor = conn.cursor() # 3. 执行 SQL,创建表 cursor.execute(''' create table if not exists school ( school_code char(6) primary key, school varchar(50), province varchar(30), is_985 varchar(10) ) ''') # 4. 提交事务,注意关键一步 conn.commit() # 5. 关闭游标和连接 cursor.close() conn.close()代码里的注释已经标出最关键的点:对数据库结构或数据做了修改,必须执行 conn.commit(),否则关闭连接后修改会丢失。sqlite3 模块默认不开启自动提交,这一点和很多人的直觉相反。如果是查询操作,不 commit 也没关系,但为了代码一致,我通常都会在需要持久化的操作后补上 commit。关闭顺序也有讲究,先关游标再关连接,避免持有未释放的资源。
还有一个容易被忽略的配置:conn.row_factory = sqlite3.Row 可以让查询结果支持字段名访问。默认 fetchall 返回的是元组,想取 school 字段只能靠下标,改成 Row 后可以直接写 row['school'],配合 Row.keys() 在构建 JSON 返回结果时非常方便。
4.3 参数样式与 executemany 批量写入
执行带条件的 SQL 时,千万别用字符串拼接来构造语句。比如下面这种写法很容易引发注入问题,或者因为引号转义出错。
# 不推荐:字符串拼接 school = "江西财经大学" cursor.execute("select * from school where school = '" + school + "'")正确做法是使用参数占位符。sqlite3 模块支持 ? 作为参数样式,这也是 DB-API 规范里的 paramstyle 之一。代码可以改成下面这样。
cursor.execute('select * from school where school = ?', (school,))? 对应第二个参数里的元组元素,一个 ? 占一个位置。这样做有两个好处:一是 SQL 语句和参数分离,避免注入风险;二是字符串里的单引号不需要手动转义,驱动会帮你处理。需要注意的是,不同数据库模块的参数占位符有差异,psycopg2 和 MySQLdb 都用 %s,所以在写跨数据库代码时,最好把 SQL 和参数封装在同一个函数里,不要散落在业务各处。
批量写入是 executemany 的高频使用场景。假设你从爬虫结果里拿到上千所高校信息,逐条 execute 要往返数据库上千次,而 executemany 只需一次调用。
data = [ ('10421', '江西财经大学', '江西省', '否'), ('10422', '华东交通大学', '江西省', '否'), ] cursor.executemany(''' insert into school(school_code, school, province, is_985) values (?, ?, ?, ?) ''', data)data 是列表,每个元素是一个元组,元组内的字段顺序和 SQL 中的字段顺序一致。executemany 会自动复用同一条 SQL 模板,把参数依次绑定执行,适合批量导入、批量初始化的场景。需要留意的是,executemany 也是事务操作,结束后记得 commit,否则数据不会落盘。
5. 综合案例:用 sqlite3 封装一个高校信息查询小工具
5.1 表结构与初始化数据
最后用一个完整案例把前面的知识点串起来。我们要在本地文件中创建一个学校信息库,包含 school_code、school、province、is_985 四个核心字段,然后导入少量数据。这样可以模拟数据库课程设计里最常见的初始化流程。
import sqlite3 def init_db(db_path='school.db'): conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute(''' create table if not exists school ( school_code text primary key, school text, province text, is_985 text ) ''') # 清空旧数据,保证 init_db 可重复执行 cursor.execute('delete from school') data = [ ('10401', '北京大学', '北京市', '是'), ('10402', '清华大学', '北京市', '是'), ('10421', '江西财经大学', '江西省', '否'), ('10422', '南昌大学', '江西省', '否'), ] cursor.executemany('insert into school values (?, ?, ?, ?)', data) conn.commit() cursor.close() conn.close()init_db 函数里先建表,再清空旧数据,最后批量插入。这样每次调用都会回到干净的初始状态。如果不清空,重复运行脚本会产生重复记录,导致后续统计结果翻倍。
5.2 封装查询函数并处理事务
接下来把查询和更新封装成函数。查询函数返回结果,更新函数要处理异常回滚。下面这个实现里,用 try/except 把 execute 和 commit 包在一起,一旦出错就 rollback,避免数据库停留在部分更新状态。
def query_by_province(conn, province): cursor = conn.cursor() cursor.execute('select school, is_985 from school where province = ?', (province,)) rows = cursor.fetchall() cursor.close() return rows def update_school_name(conn, old_name, new_name): cursor = conn.cursor() try: cursor.execute('update school set school = ? where school = ?', (new_name, old_name)) conn.commit() return cursor.rowcount except Exception: conn.rollback() raise finally: cursor.close()query_by_province 里的参数绑定把 province 传入 where 条件,返回的是列表,每个元素是一个元组。update_school_name 通过 rowcount 得到受影响行数,用来判断更新是否真的匹配到了记录。如果 rowcount 为 0,说明 old_name 不存在,调用方可以据此给出业务提示。
5.3 验证与排错:最后一步这样检查
完成封装后,最直接的验证方式是打开交互式 Python 环境,逐步调用函数并观察返回结果。
conn = sqlite3.connect('school.db') print(query_by_province(conn, '江西省')) print(update_school_name(conn, '南昌大学', '南昌大学前湖校区')) conn.close()如果结果不符合预期,优先检查三个地方:一是建表语句是否真的执行成功,可以查 sqlite_master 表;二是事务是否提交,没 commit 的话数据只存在内存里;三是参数占位符的数量是否与字段数量一致,多一个或少一个都会抛出 ProgrammingError。最后一个检查技巧是打印 cursor.rowcount,很多更新无效的问题其实是因为 where 条件没有匹配到任何记录。这个排查思路同样适用于 PyMySQL、psycopg2 等数据库模块,因为它们的错误信息都遵循 DB-API 规范。
本文还有配套的精品资源,点击获取