
前言FDE系列内容总纲【大纲】FDE 前沿部署工程师学习系列教程-CSDN博客前置课程列表见文档结尾附录。阶段2·Day 35Python SQL — 工单接入 MySQL 本周收官FDE 学习系列教程 · 第二阶段 · 第 3 周 · Day 5预计时长3 小时 | 难度★★★★☆ | 前置知识Day 31-34 全部内容一句话目标装好 MySQL用 SQLAlchemy 和 pandas 在 Python 里读写数据库搭一条脏 CSV → 清洗 → MySQL管道并把第 2 周的工单 API 从内存升级为 MySQL 持久化。 开场从玩具到真家伙兄弟前四天我们都在 SQLite 上练。它轻便、零安装是最好的训练场。但它有个天花板不支持多人高并发不适合当生产服务器。客户现场跑的是MySQL——真正的企业级数据库作为一个服务 7×24 小时跑在服务器上成百上千个请求同时来都扛得住。今天完成三级跳Day31-34 今天 最终成果 ┌──────────┐ ┌──────────────┐ ┌────────────────┐ │ SQLite │ ───► │ MySQL 服务 │ ───► │ FastAPI 工单API │ │ 单文件 │ │ SQLAlchemy │ │ 数据持久化 │ │ 自己练 │ │ pandas │ │ 重启不丢 │ └──────────┘ └──────────────┘ └────────────────┘今天内容多遇到安装报错别烦躁——装数据库本身就是 FDE 的必备技能客户服务器上你也得这么干。一步步来。️ 一、安装 MySQLWindows安装步骤打开 https://dev.mysql.com/downloads/installer/ 下载MySQL Installer for Windows选体积较大的那个完整版 MSI约 400MB组件全运行安装程序安装类型选Server only只装服务器即可体积小想要图形界面可选 Full一路 Next到配置环节Config Type选Development Computer开发机端口Port保持 3306勾上开机自启认证方式选推荐的Use Strong Password Encryption设置root 密码学习阶段建议设成好记的比如fde2026生产环境务必用强密码点 Execute 应用配置 → Finish验证安装成功打开开始菜单里的MySQL Command Line Client输入刚才的密码看到mysql提示符就成了Enter password: ******** Welcome to the MySQL monitor. mysql⚠️两个信息务必记牢后面 Python 连接全靠它们用户名root密码fde2026你自己设的端口3306装不上的常见原因① 3306 端口被别的程序占了之前装过 MySQL/MariaDB② 缺少 Visual C 运行库按提示装一个即可。️ 二、建库建表在 MySQL Command Line 里逐段执行注意 MySQL 的语法和 SQLite 略有不同-- ① 创建数据库指定 utf8mb4 字符集才能正确存中文和 emoji CREATE DATABASE IF NOT EXISTS fde_tickets CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE fde_tickets; -- ② 工程师表 CREATE TABLE engineers ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, phone VARCHAR(20), department VARCHAR(50), title VARCHAR(50) DEFAULT 工程师, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- ③ 设备表DECIMAL 定点数存温度比浮点精确 CREATE TABLE devices ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, type VARCHAR(20) NOT NULL, temperature DECIMAL(5,1) DEFAULT 0, vibration DECIMAL(4,1) DEFAULT 0, is_running BOOLEAN DEFAULT TRUE, engineer_id INT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (engineer_id) REFERENCES engineers(id) ); -- ④ 工单表 CREATE TABLE tickets ( id INT AUTO_INCREMENT PRIMARY KEY, device_id INT NOT NULL, title VARCHAR(200) NOT NULL, priority VARCHAR(10) DEFAULT 中, status VARCHAR(20) DEFAULT 待处理, reporter_id INT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (device_id) REFERENCES devices(id), FOREIGN KEY (reporter_id) REFERENCES engineers(id) ); -- 看一眼表建好了没 SHOW TABLES;SQLite → MySQL 数据类型对照含义SQLiteMySQL整数INTEGERINT/BIGINT小数REALDECIMAL(5,1)/FLOAT/DOUBLE字符串TEXTVARCHAR(50)/TEXT布尔INTEGER(0/1)BOOLEAN/TINYINT(1)时间TEXTDATETIME/TIMESTAMP自增主键AUTOINCREMENTAUTO_INCREMENTDECIMAL(5,1) 是啥意思总共 5 位数字小数部分占 1 位所以整数部分最多 4 位——能存-9999.9到9999.9。温度用定点数而非 FLOAT是为了避免浮点误差0.10.2≠0.3 的老问题。涉及金额必须用 DECIMAL。️ 三、安装 Python 库在你的项目虚拟环境里pip install sqlalchemy pymysql pandas三个库分工明确库角色大白话sqlalchemyORM / 数据库工具包Python 和数据库之间的万能翻译官pymysqlMySQL 驱动让 Python 能说 MySQL 的方言pandas数据分析库它提供了高性能、易用的数据结构和数据分析工具是数据清洗、处理、分析的瑞士军刀。一张表在 Python 里变成强大的 DataFrame你的 Python 代码 │ ▼ SQLAlchemy统一接口换数据库只改连接串 │ ▼ PyMySQLMySQL 专用驱动 │ ▼ MySQL 服务器3306 端口连接串URL是关键格式背下来mysqlpymysql://用户名:密码主机:端口/数据库名?charsetutf8mb4 mysqlpymysql://root:fde2026localhost:3306/fde_tickets?charsetutf8mb4mysqlpymysql的意思是用 pymysql 驱动连 MySQL。charsetutf8mb4保证中文不乱码。️ 四、SQLAlchemy 连接与操作新建07_sqlalchemy.py SQLAlchemy 操作 MySQL 文件07_sqlalchemy.py from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker # ① 创建引擎整个程序通常只创建一次它内部管连接池 engine create_engine( mysqlpymysql://root:fde2026localhost:3306/fde_tickets?charsetutf8mb4, echoTrue, # echoTrue 打印实际执行的 SQL学习时强烈建议开 pool_pre_pingTrue # 连接前探活避免用到断开的旧连接 ) # ② 用 with 管理连接自动关闭不用手写 close with engine.connect() as conn: # 插入工程师 —— 参数用 :名字 占位SQLAlchemy 风格防注入 conn.execute(text( INSERT INTO engineers (name, phone, department, title) VALUES (:name, :phone, :dept, :title) ), [ {name: 李工, phone: 13800001111, dept: 一车间, title: 高级工程师}, {name: 王工, phone: 13800002222, dept: 二车间, title: 工程师}, {name: 赵工, phone: 13800003333, dept: 一车间, title: 工程师}, ]) conn.commit() # 查询 result conn.execute(text(SELECT id, name, department FROM engineers)) print(\n工程师列表) for row in result: print(f {row.id}. {row.name} | {row.department})运行python 07_sqlalchemy.pyechoTrue会让你看到 SQLAlchemy 实际发出的 SQL 语句——这是学习Python 代码如何变成 SQL的最佳窗口。熟悉后在生产代码里关掉echoFalse。参数占位符的演变SQLite 原生驱动?MySQL 原生驱动pymysql%sSQLAlchemy 统一用:名字命名参数不管底层啥数据库都一样这就是用 SQLAlchemy 的一大好处换数据库时业务代码几乎不动只改连接串。pandas 读写数据库爽到飞起在同一文件追加import pandas as pd # 读一句 SQL → DataFrame带表头的二维表类似 Excel df pd.read_sql(SELECT * FROM engineers, engine) print(\npandas 读出来的) print(df) # 写DataFrame → 数据库表一行搞定 new_devices pd.DataFrame([ {name: 注塑机A1, type: 注塑, temperature: 65.0, vibration: 8.0, is_running: True, engineer_id: 1}, {name: 注塑机A2, type: 注塑, temperature: 82.0, vibration: 12.0, is_running: True, engineer_id: 2}, ]) new_devices.to_sql(devices, engine, if_existsappend, indexFalse) print(f\n✅ pandas 写入 {len(new_devices)} 条设备)解说pd.read_sql(...)直接执行 SQL 查询把结果集自动转成DataFrame。第一个参数SQL 语句字符串第二个参数数据库连接engineto_sql()参数详解参数值含义namedevices目标数据库表名conengine数据库连接if_existsappend表已存在时的行为追加不覆盖indexFalse不写入 DataFrame 的行索引0,1,2…to_sql的if_exists三种模式必须分清参数值行为什么时候用fail表已存在就报错默认最安全防止误覆盖replace先 DROP 整表再建数据全没临时表、整表重灌append往现有表追加数据增量导入最常用⚠️replace是删表重建不是更新对着生产表手滑用了 replace眼泪能流一升。日常导入用append。️ 五、数据管道脏 CSV → pandas 清洗 → MySQL把 Day 34 的清洗脚本升级成 MySQL 版本。新建08_pipeline.py 数据管道脏 CSV → pandas 清洗 → MySQL 持久化 文件08_pipeline.py import pandas as pd from sqlalchemy import create_engine import logging logging.basicConfig(levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s) log logging.getLogger(__name__) engine create_engine( mysqlpymysql://root:fde2026localhost:3306/fde_tickets?charsetutf8mb4 ) def load_and_clean(csv_path): 读 CSV 并清洗全程向量化操作比手写循环快得多 df pd.read_csv(csv_path) log.info(f原始数据{len(df)} 行) # ① 标准化设备名去空格→大写→去内部空格负责人去空格 df[device_name] df[device_name].astype(str).str.strip()\ .str.upper().str.replace( , ) df[owner_name] df[owner_name].astype(str).str.strip() # ② 温度中文数字映射 → 转数字转不了的变 NaN对应数据库 NULL cn_map {八十二: 82, 九十: 90, 九十一: 91} df[temperature] df[temperature].astype(str).str.strip().replace(cn_map) df[temperature] pd.to_numeric(df[temperature], errorscoerce) # errorscoerce 是灵魂无法转换的变 NaN 而不是报错 # ③ 手机号去横杠空格pandas 把空读成 NaN字符串nan还原成 None df[phone] df[phone].astype(str).str.replace(-, , regexFalse)\ .str.replace( , , regexFalse) df.loc[df[phone].isin([nan, None, ]), phone] None # ④ 去重 before len(df) df df.drop_duplicates(subset[device_name, owner_name]) log.info(f去重{before} → {len(df)} 行删除 {before-len(df)} 条) return df def save_to_mysql(df, table_name): df.to_sql(table_name, engine, if_existsappend, indexFalse) log.info(f✅ 写入 {table_name} 表 {len(df)} 条) # 跑管道 df load_and_clean(dirty_data.csv) print(df) # 直接打印 DataFrame 检查清洗效果 save_to_mysql(df, devices_clean) # 验证 cnt pd.read_sql(SELECT COUNT(*) AS c FROM devices_clean, engine) log.info(fdevices_clean 表现共 {cnt.iloc[0,0]} 条) 对比 Day 34 的纯 Python 版本pandas 的str.strip()、to_numeric(errorscoerce)、drop_duplicates()都是向量化操作——不用自己写 for 循环一行顶十行处理几万行 CSV 速度快几十倍。这就是为什么数据分析人人爱 pandas。⚠️ 第一次跑前先在 MySQL 里建devices_clean表列名要和 DataFrame 对得上或用to_sql(..., if_existsreplace)让 pandas 自动建表学习阶段图省事可以生产环境建议手动建表好控制类型。️ 六、重头戏工单 API 接入 MySQL持久化回到第 2 周的 FastAPI 工单项目。当时数据存在内存列表里重启就没。今天把数据层换成 MySQL。还记得 Day 26 学的分层架构吗现在它开始回报你了——我们只替换数据层接口层一行不用改。分层架构的回报 ┌─────────────────────────┐ │ 接口层 main.py路由 │ ← 完全不用改 │ app.get(/tickets) │ └───────────┬─────────────┘ │ 调用 ┌───────────▼─────────────┐ │ 数据层 data_store.py │ ← 只换这一层 │ 旧内存 TICKETS 列表 │ │ 新MySQL 增删改查 │ └─────────────────────────┘新建data_store.py 数据层MySQL 版本替换原来的内存列表 文件data_store.py from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker from contextlib import contextmanager engine create_engine( mysqlpymysql://root:fde2026localhost:3306/fde_tickets?charsetutf8mb4, pool_pre_pingTrue ) SessionLocal sessionmaker(bindengine) contextmanager def get_db(): 数据库会话上下文管理器自动提交/回滚/关闭 session SessionLocal() try: yield session session.commit() # 没出异常 → 提交 except Exception: session.rollback() # 出异常 → 回滚保证不留半截脏数据 raise finally: session.close() # 无论如何都关闭归还连接给连接池 # CRUD def list_tickets(statusNone, priorityNone): 动态拼条件查工单列表 with get_db() as db: sql SELECT * FROM tickets WHERE 11 # 11 方便后面无脑 AND params {} if status: sql AND status :status params[status] status if priority: sql AND priority :priority params[priority] priority sql ORDER BY created_at DESC result db.execute(text(sql), params) return [dict(row._mapping) for row in result] def get_ticket(ticket_id): with get_db() as db: result db.execute( text(SELECT * FROM tickets WHERE id :id), {id: ticket_id} ) row result.fetchone() return dict(row._mapping) if row else None def create_ticket(data): with get_db() as db: db.execute(text( INSERT INTO tickets (device_id, title, priority, status, reporter_id) VALUES (:device_id, :title, :priority, 待处理, :reporter_id) ), data) new_id db.execute(text(SELECT LAST_INSERT_ID() AS id)).fetchone()[0] return get_ticket(new_id) def update_ticket(ticket_id, data): with get_db() as db: sets, params [], {id: ticket_id} for key, val in data.items(): if val is not None: sets.append(f{key} :{key}) params[key] val if sets: db.execute(text(fUPDATE tickets SET {, .join(sets)} WHERE id:id), params) return get_ticket(ticket_id) def delete_ticket(ticket_id): with get_db() as db: result db.execute(text(DELETE FROM tickets WHERE id:id), {id: ticket_id}) return result.rowcount 0解说pool_pre_pingTrue的作用连接池里的连接可能因超时被数据库断开。开启后每次取连接前先 ping 一下失效就自动重连避免MySQL server has gone away错误。SessionLocal sessionmaker(bindengine)创建一个会话工厂每次调用SessionLocal()就得到一个与数据库交互的 session。保证事务安全阶段动作目的进入创建 session拿到数据库连接正常结束commit()把改动真正写入数据库出异常rollback()撤销本次所有改动不留半截脏数据无论如何close()归还连接到池避免泄漏几个关键设计点1.contextmanager上下文管理器——数据库操作的安全护栏with get_db() as db: 进入时自动开一个会话 db.execute(INSERT) 正常 → commit() 提交 db.execute(UPDATE) 异常 → rollback() 回滚 退出时close() 一定执行哪怕报错这保证了连接不泄漏、出错不留半成品。比每次手写 try/finally 清爽。2.WHERE 11是动态拼 SQL 的经典技巧因为查询条件是可选的用户可能只传 status也可能 status/priority 都传写死 WHERE 会很麻烦。用WHERE 11打底后面每个条件直接AND ...往上拼即可。3. 列名不能用参数占位符:参数只能占位值不能占位列名。所以update_ticket里列名用 f-string 拼、值用:参数传。这里列名来自你自己的代码白名单内安全绝不能把用户输入直接拼进 SQL。接口层 main.py 几乎不变from fastapi import FastAPI, HTTPException from pydantic import BaseModel import data_store # ← 关键只改这一行的 import原来可能是内存版 app FastAPI(title工单 APIMySQL 版) class TicketIn(BaseModel): device_id: int title: str priority: str 中 reporter_id: int | None None app.get(/tickets) def list_tickets(status: str | None None, priority: str | None None): return data_store.list_tickets(status, priority) app.post(/tickets, status_code201) def create_ticket(t: TicketIn): return data_store.create_ticket(t.model_dump()) app.get(/tickets/{ticket_id}) def get_ticket(ticket_id: int): t data_store.get_ticket(ticket_id) if not t: raise HTTPException(404, 工单不存在) return t app.delete(/tickets/{ticket_id}, status_code204) def delete_ticket(ticket_id: int): if not data_store.delete_ticket(ticket_id): raise HTTPException(404, 工单不存在)见证奇迹的时刻uvicorn main:app --reload用 Swaggerhttp://localhost:8000/docs创建 2 张工单关掉uvicorn 服务CtrlC重新启动uvicorn main:app --reload再查GET /tickets——数据还在这就是持久化。在 MySQL Command Line 里也能直接查到USE fde_tickets; SELECT * FROM tickets;这一刻你应该体会到分层架构的价值换数据层内存→MySQL接口层、Pydantic 模型、文档全部零修改。以后要从 MySQL 换成 PostgreSQL改个连接串的事。这就是 Day 26 埋的伏笔在今天开花结果。 本周复习知识地图SQL 与数据处理Day 31-35 │ ├─ 基础 CRUDDay 31 │ ├─ SQLite 上手 DBeaver 可视化 │ ├─ SELECT / WHERE / ORDER BY / LIMIT │ ├─ INSERT / UPDATE / DELETE写操作必加 WHERE │ ├─ 主键、数据类型、? 占位符防注入 │ └─ commit 落盘 │ ├─ 多表查询Day 32 │ ├─ 为什么拆表消除重复一处改处处生效 │ ├─ 主键 / 外键 / 表关系1:N │ ├─ INNER JOIN交集/ LEFT JOIN左全保留补 NULL │ ├─ 聚合 COUNT/SUM/AVG/MAX/MIN GROUP BY │ ├─ HAVING 过滤组WHERE 过滤行 │ └─ 执行顺序 FROM→WHERE→GROUP BY→HAVING→SELECT→ORDER BY→LIMIT │ ├─ 进阶查询Day 33 │ ├─ 子查询 / CTEWITH把复杂查询拆成流水线 │ ├─ ROW_NUMBER / RANK / DENSE_RANK 的区别 │ ├─ PARTITION BY分组但不压扁行 │ ├─ 组内 TopN 模板CTE ROW_NUMBER WHERE rnN │ └─ SUM/AVG OVER(PARTITION BY)每行带组统计 │ ├─ 数据清洗Day 34 │ ├─ 四类脏数据重复 / 缺失 / 格式不一致 / 类型异常 │ ├─ 顺序先标准化 → 再去重 → 后补缺转类型 │ ├─ TRIM/UPPER/REPLACE/CAST/COALESCE │ ├─ 脱敏 SUBSTR拼接原始数据只读不覆盖 │ └─ 全程日志留痕 │ └─ Python SQLDay 35 ├─ MySQL 安装 建库建表utf8mb4 / DECIMAL ├─ 连接串 mysqlpymysql://user:pwdhost:3306/db ├─ SQLAlchemy engine / text(:命名参数) / 上下文管理器 ├─ pandas read_sql() / to_sql(append/replace/fail) ├─ 数据管道脏 CSV → pandas 清洗 → MySQL └─ 工单 API 接入 MySQL分层架构接口层零修改 本周验收清单序号验收项达标标准自测方式1基础查询熟练写 SELECTWHEREORDER BYLIMITDay31 练习 1 全对2增删改会 INSERT/UPDATE/DELETE且记得加 WHERE能说出忘加 WHERE 的后果3多表 JOIN能独立写三表关联Day32 练习 1 跑通4分组统计GROUP BY HAVING 用对能讲清 WHERE/HAVING 区别5窗口函数写出分组 TopNDay33 练习 3 跑通6CTE用 WITH 把复杂查询分段Day33 练习 4 跑通7数据清洗脏 CSV → 干净数据入库9 行洗成 6 行且无报错8脱敏手机号/身份证打码Day34 练习 3 正确9PythonSQLSQLAlchemy pandas 连 MySQL脚本能读写真实 MySQL10数据管道CSV→清洗→MySQL 一键跑通08_pipeline.py 成功11API 持久化工单 API 重启数据不丢重启后数据仍在全绿了吗有 1-2 项不踏实很正常回看对应那天的实操再敲一遍。SQL 这东西手上敲过和眼睛看过是两种技能。 本周收官你获得了什么这一周你完成了从数据在内存里裸奔到企业级数据处理的跨越你能在数据库里设计表、写各种查询包括多表 JOIN、分组统计、窗口排名你能把客户给的脏 CSV 洗成干净数据还知道怎么脱敏、怎么留痕你能用 PythonSQLAlchemy pandas操作真正的 MySQL你的工单 API 接上了数据库重启不丢数据有了生产应用的雏形你现在手里的完整数据能力栈 数据源 清洗加工 存储 对外服务 ┌────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ │ 脏 CSV │→│ pandas │→│ MySQL │ ←── │ FastAPI │ │ 设备日志 │ │ 标准化 │ │ 三表关联 │ │ 工单 API │ │ 客户 Excel│ │ 去重补缺 │ │ 索引/外键 │ │ Swagger │ └────────┘ └──────────┘ └─────────┘ └──────────┘这套数据进来 → 清洗 → 入库 → 对外提供 API的链路就是无数企业系统的骨架。FDE 在客户现场做的数据对接本质都是它的变体。 下阶段预告第 3 周拿下数据第 4 周要攻克部署环境。客户的服务器 90% 是 Linux——没有图形界面、没有资源管理器全靠命令行。下周你会在 Windows 上用 WSL2 装一个 Ubuntu拥有本地 Linux 环境掌握文件操作、权限、进程管理、文本三剑客grep/awk/sed写 Shell 脚本把重复操作自动化自动备份、健康检查用 nohup、crontab 让服务后台常驻、定时执行你的工单 API 现在跑得好好的但总不能一直占着你的终端窗口。下周教你怎么把它扔到服务器上一直跑。周末好好休息下周进入部署工程师的世界附录前置课程列表阶段一【FDE系列】阶段1Day 1AI 层级关系 — 四个嵌套的圈-CSDN博客【FDE系列】阶段1Day 2AI 三阶段发展史 — 会认 → 会判断 → 会创造-CSDN博客【FDE系列】阶段1Day 3符号 AI vs 机器学习 — 两条路线的本质区别-CSDN博客【FDE系列】阶段1Day 4Transformer 的历史意义 — 2017 年的分水岭-CSDN博客【FDE系列】阶段1Day 5本周复习与自测 — 检验你的 AI 认知地基-CSDN博客【FDE系列】阶段1Day 6Transformer 架构 — 一张图纸盖出千千万万栋楼-CSDN博客【FDE系列】阶段1Day 7LLM 本质 — 文字接龙机器-CSDN博客【FDE系列】阶段1Day 8Token — 模型眼中的最小单位-CSDN博客【FDE系列】阶段1Day 9AI 幻觉 — 为什么会一本正经地胡说八道-CSDN博客【FDE系列】阶段1Day 10上下文窗口 — 模型的记忆力上限 本周复习-CSDN博客【FDE系列】阶段1Day 11Prompt — 给模型立规矩-CSDN博客【FDE系列】阶段1Day 12Memory — 让模型记住上下文【FDE系列】阶段1Day 13RAG — 给模型配图书管理员-CSDN博客【FDE系列】阶段1Day 14Tool Use — 让模型动手操作-CSDN博客【FDE系列】阶段1Day 15MCP — 统一的工具接口标准 第三周复习-CSDN博客【FDE系列】阶段1Day 16什么是 FDE — 把 AI 变成客户结果的人-CSDN博客【FDE系列】阶段1Day 17FDE vs 传统实施 — 三大本质区别-CSDN博客【FDE系列】阶段1Day 18FDE 三重身份 C6 胜任力模型-CSDN博客【FDE系列】阶段1Day 19七阶段行动路径 行业经验的价值-CSDN博客【FDE系列】阶段1Day 20阶段总结与产出物 — 第一阶段收官-CSDN博客阶段二【FDE系列】阶段2Day 21Python 环境搭建 — 写出你的第一行代码-CSDN博客【FDE系列】阶段2Day 22变量、数据类型、条件判断 — Python 的“记忆“和“判断“-CSDN博客【FDE系列】阶段2Day 23循环与函数 — 让代码跑 100 遍、把逻辑打包复用-CSDN博客【FDE系列】阶段2Day 24数据结构 — 列表、字典、集合、元组-CSDN博客【FDE系列】阶段2Day 25文件读写与 JSON — 让程序连通外部数据第一周收官-CSDN博客【FDE系列】阶段2Day 26模块化编程 — 把代码拆成“抽屉柜“-CSDN博客【FDE系列】阶段2Day 27异常处理与日志 — 让程序“摔不烂、查得到“-CSDN博客【FDE系列】阶段2Day 28FastAPI 入门 — 把你的函数变成 API 服务-CSDN博客【FDE系列】阶段2Day 29FastAPI 进阶 — Pydantic 模型与完整 CRUD 实战-CSDN博客【FDE系列】阶段2Day 30生产代码规范 — 测试、类型注解、配置管理第二周收官-CSDN博客【FDE系列】阶段2Day 31SQL 基础 — 增删改查一把梭-CSDN博客【FDE系列】阶段2Day 32多表查询 — JOIN 与聚合-CSDN博客【FDE系列】阶段2Day 33进阶查询 — 窗口函数与 CTE-CSDN博客【FDE系列】阶段2Day 34数据清洗 — 把脏数据捋干净-CSDN博客