2026-09-20 · 8 min read

6.4 数据库正传--SQL与SQLite

数据库正传:为什么需要数据库,用 SQLite 演示它长什么样、怎么操作,并讲 SQL、索引、自增字段这些通识。

课时概要

数据库正传。为什么需要数据库(文件存储的痛人人都有,七步之内必有解药);用两个维度(关系型/非关系型 × 嵌入式/服务式)选型,落在 SQLite(关系型+嵌入式,一个 .db 文件)。在 REPL 里走通 CREATE/INSERT/SELECT/WHERE/DELETE,认清 SQL 注入 和 ? 占位符,再用 ORDER BY/DESC/LIMIT 体验数据库「只说要什么」的姿态,最后看它为什么不爆内存。

以上为 UP 主的 B 站官方简介,非观看笔记;只有跟完这一节才算自己的理解。

视频:【零到全栈】6.4-数据库正传--SQL与SQLite | 时长 51:35 | 模块 6 · 生态、数据与状态

讲义:模块 6.4:数据库正传——SQLite 与 SQL(李勃老师.com)

本节要点

  • 两维度选型:关系型(表、SQL)vs 非关系型(文档/键值);嵌入式(一个文件、不用起服务)vs 服务式(独立进程守端口)。
  • 文字实验室是结构化小数据、单应用 → 关系型 + 嵌入式 → SQLite:零安装、单文件、Python 自带。
  • 增删改查:CREATE TABLE / INSERT / SELECT / DELETE;改完必须 conn.commit() 才落盘(提交事务)。
  • WHERE 千万别漏:DELETE FROM films 不带条件 = 清空整张表。
  • SQL 注入:拼用户输入 = 把数据伪装成命令;铁律——SQL 里永不拼用户输入,值永远走 ? 占位符。
  • ORDER BY ... DESC LIMIT 10:只说「要什么」,数据库自己安排怎么扫、怎么排、怎么快。
  • 数据库不爆内存三招:LIMIT 只攥 N 把椅子 / 按页读盘+磁盘外排 / 索引(提前排好的目录)。

笔记正文(讲义整理)

选型两维度

维度选项代表
数据模型关系型(表、SQL)/ 非关系型(NoSQL)MySQL/PG/SQLite;MongoDB(文档)、Redis(键值)
服务方式嵌入式(一个 .db 文件)/ 服务式(独立进程守端口)SQLite;MySQL(3306)、PG(5432)

我们:结构化小数据、单应用、不想养常驻服务 → SQLite。它普及到手机里躺着几十个(微信聊天记录、浏览器历史)。

在 REPL 里走一遍

python
import sqlite3
conn = sqlite3.connect("test.db")     # 没有就创建——就是硬盘上一个文件
cur = conn.cursor()
 
cur.execute("""
CREATE TABLE films (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT, language TEXT, release_date TEXT, created_at TEXT
)
""")
 
cur.execute(
  "INSERT INTO films (title, language, release_date, created_at) "
  "VALUES (?, ?, ?, datetime('now'))",
  ["肖申克的救赎", "英语", "1994-09-23"])
conn.commit()                          # ← 不 commit 改动不落盘
 
cur.execute("SELECT * FROM films WHERE language = ?", ["日语"]).fetchall()
cur.execute("DELETE FROM films WHERE id = ?", [2]); conn.commit()

要点:id 自增、datetime('now') 是函数不加引号、SQLite 类型最精简(INTEGER/REAL/TEXT/BLOB,日期和布尔分别用 TEXT/0-1 凑)。

SQL 注入

拼出来的 SQL:"... WHERE title = '" + keyword + "'"——当片名是 ' OR '1'='1,引号提前闭合,'1'='1' 永真,整表被吐出来。

解法:占位符。值的位置只写 ?,值放进列表单独交给 execute:

python
cur.execute("SELECT * FROM films WHERE title = ?", [keyword]).fetchall()

数据库认定 ? 处铁定是值,里面什么符号都只当纯数据。规则:永不拼用户输入。

ORDER BY / DESC / LIMIT

sql
SELECT * FROM films ORDER BY created_at DESC LIMIT 5

对比文件版:records.reverse(); records[:10](告诉程序怎么做)vs 这一行(只说要什么)。

为什么不爆内存

  1. 只要 N 条就只攥 N 把椅子:LIMIT 10 一开始就告诉数据库,前 10 条坐下,第 11 条起和当前最旧的比,椅子永远 10 把;
  2. 按页读盘 + 排不下摊到硬盘:表切成固定页,用哪页翻哪页;真要全排就把中间结果暂存磁盘临时文件;
  3. 索引:像字典检字表,提前把某列排好;常排序/常筛选的列才建(代价是占空间+拖慢写入)。底层是 B 树,插一条只局部动一下。

SQL 五件套 + SQL 注入 vs 占位符

和 GFG / 课程笔记的连接

这节内容GFG / 已学
sqlite3 模块GFG 有专门的 SQLite/DB 章节——connect/cursor/execute/commit 是标准姿势
conn.commit() 事务6.3 文件存储时点过「事务」,今天第一次真正用到
? 占位符安全习惯;和 4.6 CVE 同一精神——安全是每行代码的习惯
索引/B 树数据结构;GFG/计算机专业课主线,这里先认个名
「只说要什么」对比 6.3 手写 load_history/reverse/slice,姿态之别

GFG 里的 sqlite3 教程是「照着操作」,这里是带着自己 history 表的需求去理解为什么这么用。

关键概念

  • 关系型数据库:数据摆成固定表、固定列类型,用 SQL 增删改查。
  • 嵌入式:数据库就是一个文件,不用起服务(SQLite);服务式是独立进程守端口。
  • cursor:往数据库递 SQL 的手柄。
  • 主键 PRIMARY KEY / 自增 AUTOINCREMENT:每行的唯一身份证,数据库自动发号。
  • commit:把改动真正落盘;不 commit 就退出,改动不算数。
  • SQL 注入:把用户输入拼进 SQL,被当成命令执行;用 ? 占位符根治。
  • 索引:提前把某列排好的目录(B 树),查得快但占空间、拖慢写。

代码 / 实操

bash
cd ~ && python3
# import sqlite3
# conn = sqlite3.connect("test.db"); cur = conn.cursor()
# 建表 / INSERT / SELECT / DELETE / commit(见正文)
# ORDER BY created_at DESC LIMIT 5
python3 seed_data.py    # 灌 24 部电影

可视化工具:DB Browser for SQLite / DBeaver(开源免费);付费可选 DataGrip / Navicat。

我的收获

  1. 数据库不是「高级版文件」,是姿态变了:我只说要什么,怎么扫怎么排交给它。
  2. ? 占位符防注入这个坑,光读理论不如看那部叫 ' OR '1'='1 的电影真的能被存进去——数据就是数据。
  3. LIMIT N 只攥 N 把椅子这个比喻,一下解释了「百万行排序为什么不爆内存」。

待深入

(待填:没听懂、想回头查的。)