Skip to content

E13. 数据库

3.1. 文件是数据持久保存的基础

内存数据像沙画,文件存储才是数据的“诺亚方舟”!

3.1.1. 内存数据的局限性

python
# 示例:Python中内存数据易丢失
data = {"name": "Alice", "score": 95}
data["score"] = 96
print(data)  # 输出:{'name': 'Alice', 'score': 96}

# 重启程序后数据消失!

3.1.2. 文件存储方案(以 JSON 为例)

python
import json

# 写入文件
with open("data.json", "w") as f:
    json.dump(data, f)

# 读取文件
with open("data.json", "r") as f:
    loaded_data = json.load(f)
print(loaded_data)  # 输出:{'name': 'Alice', 'score': 95}

TIP

文件存储缺陷:若数据量大,每次读写需加载整个文件,效率低下!

3.2. 数据库减少冗余修改的损耗

3.2.1. JSON 文件的修改痛点

python
# 假设 JSON 文件存储 1000 条用户数据
with open("users.json", "r+") as f:
    users = json.load(f)
    users[0]["email"] = "new@example.com"  # 修改第一条数据
    f.seek(0)
    json.dump(users, f)  # 重写整个文件!

3.2.2. 数据库的高效更新

sql
-- 直接定位修改行
UPDATE users SET email = 'new@example.com' WHERE id = 1;
存储方式修改 1 条数据需读写的数据量适用场景
JSON全部数据(如 100MB)小型配置文件
数据库仅修改行(如 1KB)大型动态数据集

3.3. 数据库的每个字段有特定类型

3.3.1. 类型约束示例

字段类型数据示例限制条件
INTEGER用户年龄(25)必须为整数
VARCHAR(20)用户名("Alice")长度≤20字符
DATETIME创建时间格式必须是日期时间

3.3.2. 类型错误的后果

python
# 假设 age 字段定义为 INTEGER
try:
    cursor.execute("INSERT INTO users (age) VALUES ('twenty-five')")
except sqlite3.OperationalError as e:
    print(e)  # 输出:不合法的类型!

3.4. 数据库使用迁移同步数据模型

3.4.1. 迁移文件示例(Django)

python
# migrations/0001_initial.py
from django.db import migrations, models

class Migration(migrations.Migration):
    operations = [
        migrations.CreateModel(
            name='User',
            fields=[
                ('id', models.AutoField(primary_key=True)),
                ('name', models.CharField(max_length=100)),
                ('email', models.EmailField(unique=True)),
            ],
        ),
    ]

3.4.2. 迁移流程

bash
# 开发环境创建迁移文件
python manage.py makemigrations

# 生产环境应用迁移
python manage.py migrate

WARNING

切勿手动修改生产环境数据库结构!必须通过迁移脚本!

3.5. 关系型数据库存在映射关系

3.5.1. 表关系类型

关系类型示例SQL 实现
一对一用户与身份证号FOREIGN KEY + 唯一约束
一对多用户与订单(一个用户多个订单)orders.user_id → users.id
多对多学生与课程(选课系统)中间表 student_course

3.5.2. 外键约束示例

sql
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    user_id INTEGER,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

3.6. SQLite 是数据库的简单实现

3.6.1. 特点对比

特性SQLiteMySQL/MariaDB
部署复杂度零配置(文件即数据库)需安装服务
并发支持读写互斥高并发
适用场景开发测试、小型应用企业级生产环境

3.6.2. 嵌入式场景

python
# Python中直接操作SQLite
import sqlite3
conn = sqlite3.connect(":memory:")  # 内存数据库
cursor = conn.cursor()
cursor.execute("CREATE TABLE...")  # 完全无需服务端

3.7. SQLite 操作数据表

3.7.1. 基础 DDL 操作

sql
-- 创建表
CREATE TABLE students (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    grade REAL CHECK(grade >= 0 AND grade <= 100)
);

-- 修改表
ALTER TABLE students ADD COLUMN email TEXT UNIQUE;

-- 删除表
DROP TABLE IF EXISTS students;

3.8. SQLite 中运用命令进行增删改查

3.8.1. CRUD 示例

sql
-- 插入数据
INSERT INTO students (name, grade) VALUES ('Alice', 95);

-- 查询数据
SELECT * FROM students WHERE grade > 90;

-- 更新数据
UPDATE students SET grade = 96 WHERE name = 'Alice';

-- 删除数据
DELETE FROM students WHERE id = 1;

3.8.2. 参数化查询防 SQL 注入

python
# 安全写法(使用占位符)
cursor.execute("SELECT * FROM users WHERE email = ?", (user_input,))

3.9. SQLite 常见需求示例

3.9.1. 聚合查询

sql
-- 计算平均分
SELECT AVG(grade) FROM students;

-- 按班级分组统计
SELECT class_id, COUNT(*) FROM students GROUP BY class_id;

3.9.2. 关联查询

sql
-- 用户与订单关联
SELECT users.name, orders.amount 
FROM users 
JOIN orders ON users.id = orders.user_id;

TIP

性能优化:对频繁查询的字段添加索引!

sql
CREATE INDEX idx_user_email ON users(email);

知识回顾

  1. 文件 vs 数据库:文件适合小数据,数据库解决海量数据的高效更新与类型约束。
  2. 迁移工具:通过代码同步数据模型,避免生产环境手动操作风险。
  3. 关系约束:外键确保数据完整性,避免孤儿记录。
  4. SQLite 特点:轻量级、无服务端,适合开发阶段。
  5. SQL 基础:CRUD 命令、参数化查询、索引优化。

课后练习

  1. (单选)以下哪种存储方式适合存储 1000 万条用户数据?

    • A. JSON 文件
    • B. SQLite 数据库
    • C. Python 字典
    • D. CSV 文件
  2. (操作)编写 SQL 语句,创建一个 products 表,包含 id(主键)、name(不为空)、price(浮点数)字段。

  3. (分析)为什么直接修改 JSON 文件的 1 条数据会导致性能问题?

参考答案
  1. B
  2. sql
    CREATE TABLE products (
        id INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        price REAL
    );
  3. JSON 文件需加载全部数据到内存,修改后需重写整个文件,时间复杂度为 O(N)。

扩展阅读

Built by Vitepress | Apache 2.0 Licensed