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. 特点对比
特性 | SQLite | MySQL/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);
知识回顾
- 文件 vs 数据库:文件适合小数据,数据库解决海量数据的高效更新与类型约束。
- 迁移工具:通过代码同步数据模型,避免生产环境手动操作风险。
- 关系约束:外键确保数据完整性,避免孤儿记录。
- SQLite 特点:轻量级、无服务端,适合开发阶段。
- SQL 基础:CRUD 命令、参数化查询、索引优化。
课后练习
(单选)以下哪种存储方式适合存储 1000 万条用户数据?
- A. JSON 文件
- B. SQLite 数据库
- C. Python 字典
- D. CSV 文件
(操作)编写 SQL 语句,创建一个
products
表,包含id
(主键)、name
(不为空)、price
(浮点数)字段。(分析)为什么直接修改 JSON 文件的 1 条数据会导致性能问题?
参考答案
- B
- sql
CREATE TABLE products ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, price REAL );
- JSON 文件需加载全部数据到内存,修改后需重写整个文件,时间复杂度为 O(N)。