首页 文章 精选 留言 我的

精选列表

搜索[WorkBuddy Enterprise],共6328篇文章
优秀的个人博客,低调大师

03-sql-enterprise-data-SQL与企业数据

# 第 3 课:SQL 与企业数据——FDE 怎么跟"脏数据"打交道 > 系列:AI FDE 20 课 | 主线项目:北岸智造企业 AI Assistant > 本课在主线中的位置:北岸 CRM 模拟库:找客户/订单/客服记录 → 客户 360° → 给 Agent 的查询接口 > 进了客户现场,AI 模型是最后才见面的东西——你先见面的是一堆没人说得清的表。 ## 学习目标 - 能在拿到一个陌生数据库后 2 小时内,用 SQL 摸清 Schema、行数、数据分布,画出一张"数据地图" - 能写出含 JOIN、CTE、Window Function 的复杂查询,并解释每一步在干什么 - 能对一张表做数据质量体检:NULL 比例、重复、脏值、外键失配,输出一份质量清单 - 能给"客户 360°"这种跨表视图建模,并用 Python + SQLite/PostgreSQL 封装成 Agent 可调用的查询接口 - 能判断哪些字段可以放心给 AI 用,哪些必须打标、脱敏或者干脆不给 ## FDE 场景导入 北岸智造的客服总监上周发话:"给客服做个 AI 助手,能直接回答客户打电话问的问题——我下过多少单、最近那笔到哪了、上次投诉处理得怎么样。" 你到了现场,对方 IT 给你一个只读账号和一句"CRM 数据都在里面"。你连上去一看:`cust_mstr`、`t_order_his`、`cust_info_bak`、`order_detail_2023_new`……九十多张表,表名像密码。问谁是owner,IT 说系统是外包做的,前一家供应商 2019 年就撤了。销售团队用 Salesforce,但一半人还是往 Excel 里填,月底有人导回去。客服记录居然存在 ERP 的一个备注字段里。 这就是 FDE 的日常。模型选择、Prompt 设计那些事,得等你先回答四个问题:**数据在哪?谁拥有它?哪些字段可信?哪些能给 AI?** 这节课就练这个。别小看这几小时,客户判断你靠不靠谱,往往就看你翻开数据库的第一天问出了什么问题。 ## 核心概念 **SQL 核心(SELECT/WHERE/GROUP BY)**。听起来像大学内容,但客户现场真正常用的是"聚合后比对":按客户聚合订单数、按月份统计投诉量、按部门分组看处理时长。为什么 FDE 必须懂:你要用查询结果判断业务口径,写不出来就只能等别人的排期,一等一周。 **JOIN**。企业数据天然是分表的:客户表、订单表、客服记录表分属不同模块。JOIN 是把"一个客户的全貌"拼出来的基本动作。难点不在语法,在弄清哪张表是事实表、哪张是维度表、关联键有没有脏值——`customer_id` 在订单表里存在但在客户表里查不到,这种事每天都在发生。 **CTE(WITH 子句)**。把一段查询起个名字复用。为什么 FDE 必须懂:客户现场的查询动辄两三百行,嵌套子查询没人看得懂也没人敢改。CTE 把"先算活跃客户、再算他们的订单、最后拼客服记录"拆成命名步骤,你交付的 SQL 客户方的 DBA 要接手维护的,写得像天书,第二天就没人认账。 **Window Function(窗口函数)**。`ROW_NUMBER()`、`RANK()`、`SUM() OVER (PARTITION BY ...)`。它解决"分组内排序/累计"问题——每个客户最近一笔订单、订单金额在客户内的排名、按时间累计的消费。去重取"最新一条"几乎全靠它:按 customer_id 分区、按 update_time 倒序、取第 1 行。 **数据建模**。不是让你画 ER 图给架构委员会看,而是让你心里有模型:谁是主数据(客户),谁是过程数据(订单、工单),谁是派生数据(报表、视图)。建模判断直接决定 AI 查询接口怎么设计——把派生逻辑放 SQL 视图里,还是放 Python 里,还是让 LLM 自己现场拼。 **图:客户全景数据模型** ```mermaid erDiagram CUSTOMERS ||--o{ ORDERS : 拥有订单 CUSTOMERS ||--o{ SERVICE_TICKETS : 产生工单 CUSTOMERS ||--|| CUSTOMER_360_VIEW : 汇总为全景 CUSTOMERS { string customer_id PK string company_name string region } ORDERS { string order_id PK string customer_id FK decimal amount date order_date } SERVICE_TICKETS { string ticket_id PK string customer_id FK string status } ``` **Schema Discovery**。系统性摸底的动作:列出所有表和列、看行数和最近更新时间、抽样看值、找注释和外键。信息来源除了 `INFORMATION_SCHEMA`,还有建表 DDL、ETL 任务脚本、找老员工问。FDE 版本就是"两小时出一张数据地图"。 **数据质量**。完整度(NULL 比例)、唯一性(重复客户)、一致性(同一客户在两套系统里名字不同)、时效性(最后更新是三年前)、有效性(性别字段里出现"test"和"1")。每一条都直接影响 AI 回答的可信度——垃圾进,垃圾出,只是这次垃圾是被 AI 一本正经地说出来的。 **数据血缘**。一个字段从哪来、经过谁加工、到了哪张表。报表里的"大客户营收"可能来自三套系统的 union 加一条手工修正规则。没有血缘,你就不知道一个数字错了要怪谁,也不知道字段口径变了 AI 会不会跟着错。 **图:客户全景字段血缘** ```mermaid flowchart LR A[客户表 公司名] --> D[客户全景视图] B[订单表 金额日期聚合] --> D C[工单表 数量状态聚合] --> D D --> E[查询接口函数] E --> F[AI 客服回答] ``` **数据权限**。谁能看哪些行哪些列。北岸有销售、客服、财务、HR、IT 五类角色,销售只能看自己片区的客户,客服不该看到合同金额,HR 数据是另一条命。这决定了 Agent 的查询接口必须在 SQL 层或应用层做行级/列级过滤,而不是"查出来再说"。 ## 技术原理 **为什么"AI 项目"本质上是数据项目。** 一个查客户的 Agent,准确率上限不取决于模型,取决于底下的查询和数据。模型再好,订单表里 `amount` 一半是 NULL,它就只能编。FDE 的价值在于提前把数据状况摸清楚,在方案里明确写"这个字段不可信,AI 回答时会降级为'以人工核实为准'"。这是对客户负责,也是给自己留退路。 **JOIN 的成本与陷阱。** `INNER JOIN` 会悄悄丢掉一边没匹配上的行——查"每个客户的客服记录数",没投诉过的客户直接消失,业务方一看报表以为客户都满意了。做客户 360° 要用 `LEFT JOIN` 从客户表出发,并且要清楚 NULL 会在聚合里怎么表现(`COUNT(*)` 和 `COUNT(col)` 不一样)。大表 JOIN 在客户现场还有性能问题:你一个探索性查询把生产库拖死,第二天 IT 就来找你喝茶。原则:**永远先在备份库/模拟库上跑,生产库只查行数和样本。** **CTE 和 Window Function 的组合拳。** 经典套路是"每个分组取最新一条": ```sql WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY updated_at DESC) AS rn FROM customer_contact_history ) SELECT * FROM ranked WHERE rn = 1; ``` 先分区排序打标,再过滤。这个模式在企业数据里出现频率高得惊人:客户多渠道联系记录、订单多次改价、工单多次流转,都只关心"最新状态"。 **数据建模的三层判断。** 对 FDE 来说建模是三个问题:一,这个业务问题的"实体"是什么(北岸客服场景:客户、订单、客服工单);二,实体间的关联键是什么、键可不可信;三,AI 需要的视图长什么样——"客户 360°"应该是预先物化的宽表/视图,还是运行时现拼。我的建议:给 Agent 的接口面向**预定义视图**,不要让 LLM 面对九十张原始表自由发挥。让模型写任意 SQL 看着炫,但生产上等于把数据权限和性能问题全交给概率。 **图:客户全景视图拼装** ```mermaid flowchart LR A[客户主表] -->|左连接| E[客户全景视图] B[订单聚合子查询] -->|左连接| E C[最新订单子查询] -->|左连接| E D[工单统计子查询] -->|左连接| E E --> F[Agent 白名单接口] ``` **Schema Discovery 的方法论。** 按这个顺序来,别乱翻: 1. `INFORMATION_SCHEMA.TABLES` 拿全部表名 + 粗略分类(按表名前缀猜模块) 2. 每张表查行数、`MIN/MAX(created_at)`,判断是不是活跃表 3. 抽样 `SELECT * LIMIT 20`,肉眼看列含义和脏值 4. 找外键和命名规律(`customer_id` 出现在哪些表 = 一张隐形的关联图) 5. 问人:谁是 owner、哪些表是备份/废弃、有没有口径文档 前三步机器做,后两步必须问人。文档常常是过期的,最准的地图在老员工脑子里。 **图:Schema Discovery 五步法** ```mermaid flowchart TD A[拉全表清单] --> B[看行数与更新时间] B --> C[抽样肉眼查值] C --> D[找外键与命名规律] D --> E[问人确认口径] ``` **数据质量的量化。** 质量不是一个"好/差"的形容词,是每列一个数字:NULL 占比、distinct 数、最小最大值、非法值样本。这份清单会直接变成你方案里的"数据风险登记表",也是第 11 课做 Eval 时挑边界用例的弹药库。 **权限的落地位置。** 行级权限有三种放法:数据库层(PostgreSQL RLS,客户 DBA 说了算,最稳但你未必有权限配)、应用层(查询接口强制拼 WHERE 条件,可控但要防绕过)、Agent 层(靠 Prompt 约束——**不靠谱,第 15 课会专门打这一点**)。FDE 选型原则:权限约束离数据越近越安全,Prompt 约束只能算提示,不能算防线。 ## 架构图 ``` 北岸智造 · 数据层 → AI 查询接口(本课交付) ────────────────────────────────────────────────────────────── 原始系统(不可控) 模拟 CRM 库(SQLite/PostgreSQL) ┌──────────────┐ ┌──────────────────────────────┐ │ Salesforce │──导出──▶ │ customers (客户主数据) │ │ ERP 订单模块 │ │ orders (订单) │ │ 客服工单 │ │ service_tickets (客服记录) │ │ Excel 手填 │ │ (故意埋入 NULL/重复/脏值) │ └──────────────┘ └──────────┬───────────────────┘ │ Schema Discovery + 数据质量体检 │ ┌─────────▼─────────┐ │ 数据地图 + 风险表 │ └─────────┬─────────┘ │ 建模:预定义视图 ┌─────────▼─────────┐ │ v_customer_360 │ ◀── 客户 360° │ (宽表,含质量标记) │ └─────────┬─────────┘ │ ┌─────────▼─────────┐ │ 查询接口 (Python) │ │ get_customer_360()│ ◀── 第 9 课 Agent │ search_orders() │ 直接调用 └───────────────────┘ ``` 注意接口的形状:第 9 课的 Agent 拿到的不是数据库连接,而是这几个函数。函数内部写死 SQL 和权限过滤,Agent 只能选函数、传参数。这个隔离是后面一切安全设计的基础。 ## 代码示例 **第一段:Schema Discovery——自动生成数据地图**(SQLite,PostgreSQL 把 `sqlite_master` 换成 `information_schema` 即可): ```python import sqlite3 def discover_schema(db_path: str) -> None: """两小时数据地图的第一步:表、列、行数、时间范围""" conn = sqlite3.connect(db_path) cur = conn.cursor() tables = [r[0] for r in cur.execute( "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'" )] for t in tables: cols = [(r[1], r[2]) for r in cur.execute(f"PRAGMA table_info({t})")] row_count = cur.execute(f"SELECT COUNT(*) FROM {t}").fetchone()[0] print(f"\n== {t} ({row_count} rows)") for name, ctype in cols: nulls = cur.execute( f"SELECT SUM(CASE WHEN {name} IS NULL THEN 1 ELSE 0 END) FROM {t}" ).fetchone()[0] pct = f"{100 * nulls / row_count:.0f}%" if row_count else "-" print(f" {name: dict | None: sql = "SELECT * FROM v_customer_360 WHERE customer_id = ?" params = [customer_id] if self.allowed_region: sql += " AND region = ?" params.append(self.allowed_region) row = self.conn.execute(sql, params).fetchone() if not row: return None cols = [d[0] for d in self.conn.execute( "SELECT * FROM v_customer_360 LIMIT 0").description] return dict(zip(cols, row)) api = CrmQueryAPI("beian_crm.db", allowed_region="north") print(api.get_customer_360("C1001")) ``` FDE 视角:所有参数走占位符 `?`,永远不拼字符串——Agent 传进来的任何东西都当数据不当代码。`allowed_region` 是最小化的行级权限示范,真实项目里这个值来自登录身份,绝不能由 Agent 自己决定。 ## 企业案例 北岸智造的切面:第一次跑客户 360° 视图,客服总监发现"华东区大客户 A 公司"显示有 43 笔订单——但销售说这家公司去年才合作。查下去发现,A 公司的 `company_name` 和另一家同名小供应商在客户表里是两条记录,老系统合并客户时把订单都挂到了同一个 ID 上。销售团队当年知道这事,靠"脑子记着这家数据是脏的"绕过去了。AI 没有脑子,它会把 43 笔订单一本正经地报给客服。 这件事最后变成了两条交付约定:客户 360° 视图加了一个 `data_confidence` 字段(该客户经历过合并标记为 low),AI 回答时低置信度客户要先说"该客户数据存在合并记录,以下数字仅供参考"。你看,问题不在模型,在数据——但解决方案得由懂两边的人来设计。 业界也反复上演同一出戏:据公开报道,多家企业早期 AI 助手项目复盘时,失败原因排前头的是数据质量与权限治理,而不是模型能力。 ## 常见错误 **1. 拿生产库当实验场。** 错误做法:连上客户生产库直接跑探索查询,一个没加索引条件的大 JOIN 把库拖慢。→ 后果:轻则被 IT 收走权限,重则影响客户当日业务,信任清零。→ 正确做法:申请只读备份库或自己搭模拟库;生产库只跑 `COUNT` 和 `LIMIT`。 **2. 用 INNER JOIN 出报表。** 错误做法:查"客户 + 客服记录"用 INNER JOIN。→ 后果:没投诉过的客户从结果里消失,业务方据此得出"客户满意度很高"的错误结论。→ 正确做法:从主实体出发用 LEFT JOIN,并明确"没有记录"和"记录为零"在结果里长什么样。 **3. 让 LLM 面对原始 Schema 自由写 SQL。** 错误做法:把 90 张表的 DDL 塞进 Prompt,让模型自己生成查询。→ 后果:模型可能查到权限外数据、生成慢查询、对脏字段一本正经地解读,且每次行为不同。→ 正确做法:预定义视图 + 白名单函数接口,模型只选函数传参数;真要开放 Text-to-SQL,也要限定视图范围、只读账号、超时与行数上限。 **4. 把"有数据"当成"数据可信"。** 错误做法:字段有值就往 AI 的上下文里塞。→ 后果:`gender='test'`、订单金额为 0、三年没更新的客户档案混进回答,客户业务人员一眼看出不对,AI 整体信誉崩掉。→ 正确做法:逐列做质量体检,不可信字段打标或排除,风险写进方案文档。 **5. 权限靠 Prompt 说"请不要越权"。** 错误做法:在 System Prompt 里写"你只能查询当前用户权限范围内的数据"。→ 后果:一句"忽略之前的指令"就能绕过(第 15 课细讲)。→ 正确做法:过滤逻辑写进 SQL 或查询接口,Prompt 里的规则只是给用户看的说明。 ## Workshop 30 分钟,两人一组,材料是讲师发的 `dirty_orders.csv`(内含 NULL 金额、重复订单号、日期格式混用、客户名大小写不一)。 1. 5 分钟:不写代码,肉眼扫一遍,列出你怀疑的所有问题类型 2. 15 分钟:用 SQL(或 pandas,但 SQL 优先)逐条验证怀疑,每类问题给出量化数字(几条、占比) 3. 5 分钟:两人交换清单,找出对方漏掉的问题 4. 5 分钟:合并成一张"数据质量登记表":列名 / 问题类型 / 影响行数 / 建议处理方式 / 是否影响 AI 可用 产出物格式: ``` | 列名 | 问题 | 行数(占比) | 处理建议 | AI 可用性 | |------|------|-----------|---------|-----------| | amount | NULL | 132 (11%) | 剔除或按 0 标记 | 不可直接聚合 | ``` 这张表就是你以后到每个客户现场第一天要交的东西。 ## Lab 实战 **90 分钟:从零搭建北岸 CRM 模拟库并交付 Agent 查询接口。** 环境:Python 3.10+,SQLite(自带,零安装;有 PostgreSQL 环境的推荐用 PG,练 `information_schema` 和 RLS)。 Checklist: - [ ] **0-15 min|生成模拟库。** 写 `seed.py`,创建 `customers`(500 家,含 region、company_name)、`orders`(约 3000 笔)、`service_tickets`(约 800 条)三张表。**故意埋雷**:5% 的订单 amount 为 NULL、3% 的客户名有前后空格和大小写混乱、2% 的订单指向不存在的 customer_id、20 个客户名重复(不同 ID 同名) - [ ] **15-35 min|Schema Discovery。** 用(或改写)上文 `discover_schema`,对模拟库输出完整数据地图:每表行数、每列 NULL 率;再写两条 SQL 找出:孤儿订单(外键失配)、重复客户名 - [ ] **35-55 min|数据质量登记表。** 把发现汇总成 Workshop 那个格式的表,写进 `DATA_QUALITY.md`,每条给出处理决定(清洗/打标/排除) - [ ] **55-75 min|客户 360°。** 建 `v_customer_360` 视图(含订单数、总金额、最近订单、工单数、未结工单数),验证:无工单客户不消失、金额 NULL 订单不拉爆总数、同名客户各自独立 - [ ] **75-90 min|查询接口。** 实现 `CrmQueryAPI`:`get_customer_360(id)`、`search_orders(customer_id, days)` 两个方法,含 region 行级过滤和参数化查询;写 5 个断言测试(含"普通权限查不到其他 region 的客户") 验收标准: 1. `python seed.py && python discover.py` 能完整跑通,输出数据地图 2. `DATA_QUALITY.md` 至少覆盖 4 类问题且每类有量化数字 3. `get_customer_360("C1001")` 返回完整 dict,且 region 过滤测试通过 4. 视图对埋入的每一类脏数据都有明确处理行为,测试可复现 5. 全程无字符串拼接 SQL ## 实操 1. 把 Lab 接口升级为 PostgreSQL 版本:用 `information_schema` 重写 Schema Discovery,加一张 `customers` 表的 `data_confidence` 列,视图里对"被合并过的客户"降置信度,接口返回时带上这个标记并写一句给 AI 的降级提示语 2. 给 `v_customer_360` 加一个"客户活跃度"窗口函数字段:按 region 分区、按最近 12 个月订单金额算 `NTILE(4)` 四分位,写一段说明:这个字段在给 AI 的话术里怎么用("该客户属于华东区消费前 25%"),以及为什么不用子查询实现 ## 问题思考 **1. 接手一个陌生企业数据库,你前两个小时做什么?** 答题要点:先机器后人工——用系统目录拉表清单和行数,识别活跃表与废弃表;抽样看值判断字段含义;找外键和命名规律画出隐式关联图。然后必须问人:谁是 owner、哪些表口径有坑、有没有合并/迁移历史。最后产出一张数据地图和质量登记表,这是后续所有方案的依据。关键是体现出"数据地图是交付物,不是笔记"。 **2. 客户要求 AI 助手能回答任意数据问题,你建议让 LLM 直接生成 SQL 吗?** 答题要点:不建议直接对原始库开放。分层说:理想路径是先收敛高频问题为预定义视图和函数接口;确需 Text-to-SQL 时,限定在只读视图范围内、独立只读账号、行级权限在 SQL 层强制(RLS 或条件拼接)、加查询超时和返回行数上限、生成 SQL 先过静态校验。核心观点:权限和性能约束必须放在确定性的代码层,不能依赖模型自觉。 **3. 订单表里有 5% 的金额为 NULL,AI 引用了这张表,你怎么处理?** 答题要点:先查 NULL 的业务含义——是没录入、免费单还是数据丢失,含义不同处理完全不同。技术上:聚合时明确 `SUM` 忽略 NULL 的行为是否符合口径,必要时用 COALESCE 但要写进文档。交付上:在方案的数据风险登记表里记录该字段,接口返回中保留标记,AI 话术对涉及金额的回答加限定语,重要场景给出"以人工核实为准"的降级路径。 ## 本课小结 - FDE 见模型之前先见数据:两小时出数据地图,是入场第一份交付物 - INNER JOIN 会悄悄丢行,客户 360° 从主实体 LEFT JOIN 出发 - "分组取最新"用 CTE + Window Function,企业数据高频套路 - 数据质量按列量化:NULL 率、重复、孤儿外键,写进风险登记表 - 权限过滤放在 SQL/接口层,白名单函数给 Agent,别让模型碰原始库 ## 下一课衔接 数据和接口这层打好了,接下来得让它们跑起来、部署出去。第 4 课《04-git-docker-cloud-Git与Docker与Cloud部署》我们做 Git 工作流与 Docker,把第 2 课的 API 服务和本课的 CRM 查询接口打包成可部署的东西,完成主线项目的第一次上云——Local → Docker → Cloud。届时 `beian_crm.db` 会变成容器里的真数据库。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册