小白python入门 - 24. Python 为什么需要数据库

发布时间:2026/7/24 2:22:40
小白python入门 - 24. Python 为什么需要数据库 1. 从「会读写文件」到「业务数据」前面章节已经能用 Python 读写文本、CSV、Excel 等。本地小工具、一次性脚本把结果落成文件往往够用。但一旦数据要长期保存、反复查询、多人或多程序同时改纯文件就会开始别扭。例如用户注册信息、订单流水、库存数量——要增删改查不是「整文件重写」那么简单要按条件过滤某用户近 7 天消费、汇总各商品销量——用文件自己扫一遍能做但又慢又容易写错两个程序同时往同一个 CSV 末尾追加——可能交错、丢行、读到半截内容。数据库就是专门解决「结构化数据如何可靠存储与访问」的一类系统。Python 通过驱动或标准库与之对话程序负责业务逻辑数据库负责存、查、并发与一致性。2. 纯文件方案的典型痛点痛点说明查询靠自己扫没有「按字段条件索引」数据一大全文件遍历成本高更新成本高改中间一行常要读全量再写回写到一半断电易损坏并发不安全多进程同时写同一文件缺少事务易出现脏数据约束靠自觉主键是否重复、金额是否合法都要业务代码处处检查结构易漂移CSV 列含义靠约定字段增删后旧文件与新代码不兼容文件仍然很有用配置、日志、导出报表、交换数据。只是核心业务状态更适合交给数据库。3. 数据库能带来什么结构化存储表、列、类型明确记录之间可建关联如订单 → 用户。声明式查询用 SQL 等语言描述「要什么」不必手写整套遍历逻辑。事务Transaction一组操作要么都成功要么都回滚避免「扣了库存却没生成订单」。并发控制多连接同时读写时由引擎协调锁与隔离级别。完整性约束主键、非空、外键等把规则尽量放在数据层。常见分类选型直觉即可类型例子适合关系型SQLite、MySQL、PostgreSQL表格业务、强一致、复杂查询文档型MongoDB 等结构多变、文档 JSON 为主键值 / 缓存Redis会话、计数、热点缓存入门阶段优先搞清关系型 SQLPython 标准库自带SQLite零安装服务端即可练手。4. 实践同一业务文件 vs SQLite4.0 运行环境说明项目要求Python3.10推荐 3.12标准库含sqlite3无需单独安装数据库服务工作目录自拟下文以~/python-lab/src/day24为例ShellBash 兼容环境需支持mkdir、cat EOF等各系统建议系统建议Linux / macOS系统自带终端 已安装的python3即可Windows推荐安装 Cygwin在 Cygwin 终端中完成本课命令安装时勾选python/python3相关包或在 Cygwin 内使用已配置好的python3。也可使用WSLUbuntu操作方式与 Linux 相同。不推荐在「纯 CMD / 未配置 heredoc 的 PowerShell」中硬套下文的cat 命令。先创建目录并进入mkdir-p~/python-lab/src/day24cd~/python-lab/src/day24 python3--version# 确认可用若命令为 python 而非 python3下文请自行替换业务简单记账——写入多条「用户、金额、备注」再按用户汇总。下文输出为实测结果便于对照。4.1 文件方案CSV 追加核心思路向ledger.csv追加行汇总时自行split再累加。一键生成脚本在day24目录下整段复制执行catfile_store_demo.pyEOF # 用纯文件模拟「记账」追加写入、全量读取 from pathlib import Path DATA Path(__file__).with_name(ledger.csv) def add_record(user: str, amount: float, note: str) - None: line f{user},{amount},{note}\n with DATA.open(a, encodingutf-8) as f: f.write(line) def list_all() - list[str]: if not DATA.exists(): return [] return DATA.read_text(encodingutf-8).strip().splitlines() def sum_for_user(user: str) - float: total 0.0 for line in list_all(): u, amt, *_ line.split(,, 2) if u user: total float(amt) return total if __name__ __main__: if DATA.exists(): DATA.unlink() add_record(alice, 12.5, lunch) add_record(bob, 30.0, taxi) add_record(alice, -5.0, refund) print(--- all lines ---) for row in list_all(): print(row) print(alice total , sum_for_user(alice)) print(bob total , sum_for_user(bob)) EOF运行python3 file_store_demo.py实测输出--- all lines --- alice,12.5,lunch bob,30.0,taxi alice,-5.0,refund alice total 7.5 bob total 30.0数据少时没问题行数上万、还要按多条件筛选时代码会迅速变复杂。4.2 SQLite 方案使用标准库sqlite3数据文件为同目录ledger.db。一键生成脚本catsqlite_store_demo.pyEOF # 用 SQLite 做同样的记账支持条件查询与汇总 import sqlite3 from pathlib import Path DB Path(__file__).with_name(ledger.db) def connect(): conn sqlite3.connect(DB) conn.execute( CREATE TABLE IF NOT EXISTS ledger ( id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT NOT NULL, amount REAL NOT NULL, note TEXT ) ) return conn def add_record(conn, user: str, amount: float, note: str) - None: conn.execute( INSERT INTO ledger (user, amount, note) VALUES (?, ?, ?), (user, amount, note), ) conn.commit() def list_all(conn): return conn.execute( SELECT id, user, amount, note FROM ledger ORDER BY id ).fetchall() def sum_for_user(conn, user: str) - float: row conn.execute( SELECT COALESCE(SUM(amount), 0) FROM ledger WHERE user ?, (user,), ).fetchone() return float(row[0]) if __name__ __main__: if DB.exists(): DB.unlink() conn connect() add_record(conn, alice, 12.5, lunch) add_record(conn, bob, 30.0, taxi) add_record(conn, alice, -5.0, refund) print(--- all rows ---) for row in list_all(conn): print(row) print(alice total , sum_for_user(conn, alice)) print(bob total , sum_for_user(conn, bob)) # 条件查询只看金额 10 的记录 print(--- amount 10 ---) for row in conn.execute( SELECT user, amount, note FROM ledger WHERE amount ?, (10,), ): print(row) conn.close() EOF运行python3 sqlite_store_demo.py实测输出--- all rows --- (1, alice, 12.5, lunch) (2, bob, 30.0, taxi) (3, alice, -5.0, refund) alice total 7.5 bob total 30.0 --- amount 10 --- (alice, 12.5, lunch) (bob, 30.0, taxi)注意插入使用?占位符避免把用户输入直接拼进 SQL注入风险在后续课展开SUM/WHERE由数据库完成业务代码更短同目录可生成ledger.csv与ledger.db后者为二进制库文件不要当文本编辑。关键概念数据库按一定规则管理数据的系统本课用嵌入式 SQLite。表 / 行 / 列类似 Excel 工作表但类型与约束更严格。查询语言SQL描述「插入什么、筛选什么、如何汇总」。嵌入式 vs 客户端-服务器SQLite 嵌在进程内MySQL 等通常独立进程供多应用共享。5. 什么时候仍用文件什么时候上数据库场景更合适配置、一次性导出、与人用表格交换文件JSON / CSV / Excel日志追加、归档文件或专用日志系统用户、订单、库存等核心状态数据库高频条件查询、多表关联、并发写入数据库单机小工具、学习 SQLSQLite 足够多机、多服务、生产业务MySQL / PostgreSQL 等经验法则「要当系统的记忆来用」→ 数据库「要当文档/交换格式」→ 文件。总结文件读写解决「数据怎么落地」数据库解决「业务数据如何长期、安全、可查询地活着」。痛点集中在复杂查询、局部更新、并发、约束与一致性。关系型数据库 SQL 是入门主线Python 可用sqlite3零服务练手后续再接 MySQL 等。本课对比了 CSV 与 SQLite 的同一记账业务汇总结果一致但数据库侧条件查询更自然。下一课起将进入 SQL 与表设计再过渡到用 Python 连接更完整的数据库服务。小练笔题 1下列哪项最适合继续用纯文件而不是立刻上数据库A. 电商网站的实时库存B. 导出给财务的一份月度 Excel 报表C. 多用户同时写入的订单流水D. 需要按手机号毫秒级查找的用户表题 2判断只要把数据存成很多个小 CSV 文件就等价于使用了数据库因为也「分开存了」。对 / 错题 3在本课 SQLite 示例中为什么推荐写成INSERT INTO ledger (user, amount, note) VALUES (?, ?, ?)而不是用 f-string 把变量直接拼进 SQL 字符串题 4简答若程序写了「扣库存」后崩溃还没来得及「创建订单」从事务角度你希望数据库如何表现题 5可选实践在本课day24目录下已生成并运行过sqlite_store_demo.py再插入一条carol的记录并查询alice的SUM(amount)。写出你的命令与结果。若重新跑过 demo库会被重建以你当前环境为准。小练笔参考答案题 1B月度报表是一次性/交换用文档文件合适A/C/D 都是高频业务状态或强查询更适合数据库。题 2错多个文件只是存储形态数据库还提供查询引擎、事务、约束、并发控制等不是「拆文件」就能替代。题 3使用参数绑定?由驱动负责转义与传参降低SQL 注入风险也避免引号、特殊字符弄坏语句。用户输入绝不要直接拼进 SQL。题 4希望「扣库存」与「创建订单」在同一事务中要么都提交成功要么全部回滚不应留下只扣了库存却没有订单的中间状态。题 5示例做法与一次实测在day24目录下执行python3 -PY import sqlite3 from pathlib import Path db Path(ledger.db) conn sqlite3.connect(db) conn.execute( INSERT INTO ledger (user, amount, note) VALUES (?, ?, ?), (carol, 8.0, snack), ) conn.commit() print(conn.execute( SELECT COALESCE(SUM(amount),0) FROM ledger WHERE useralice ).fetchone()[0]) conn.close() PY在「仅运行过本课 demo、未删库」的前提下打印结果为7.512.5 (−5.0)carol为新增行不影响 alice 合计。若先删了ledger.db或重跑了sqlite_store_demo.py请以当前库中的查询输出为准。)