05 | 召回前置准备:根据业务数据库生成各数据库(读取配置阶段)

05 | 召回前置准备:根据业务数据库生成各数据库(读取配置阶段)

项目地址:github.com/frontzhm/n2...

每一步对应的完整代码都在仓库里,跟着文档卡住了就去翻源码。

这是一篇系列文,请按照顺序阅读。


终态回顾:元数据库在整个流程中的位置

sql 复制代码
dw.sql(业务数据仓库的 CREATE TABLE 语句)
    │
    │  人工分析    ← 本文第一步
    ▼
conf/meta_config.yaml(人可读的元数据描述文件)
    │
    │  脚本自动    ← 本文第二步
    ▼
MySQL meta 库(机器可查的元数据表)
    │
    │  后续文章
    ▼
Qdrant 向量库 + ES 全文索引(召回用的基础设施)

元数据库是"数据的数据"------它不存业务数据,存的是描述业务数据长什么样的说明书

问题来了:这份说明书从哪来?

答案是从业务数据仓库倒推 。你有 dw.sql(定义了什么表、什么字段),人工读一遍,翻译成 meta_config.yaml,再写一个脚本把它灌进 MySQL 的 meta 库。


第一步:从 dw.sql 到 meta_config.yaml

读 dw.sql,提取表结构

打开 docker/mysql/dw.sql,里面定义了 5 张表:

表名 角色猜测 说明
dim_region dim(维度表) 前缀 dim_,存地区信息
dim_customer dim(维度表) 前缀 dim_,存客户信息
dim_product dim(维度表) 前缀 dim_,存商品信息
dim_date dim(维度表) 前缀 dim_,存时间信息
fact_order fact(事实表) 前缀 fact_,存订单交易记录

fact_order 为例,看它的 DDL:

sql 复制代码
CREATE TABLE fact_order (
    order_id       VARCHAR(30) PRIMARY KEY,
    customer_id    VARCHAR(20),
    product_id     VARCHAR(20),
    date_id        INT,
    region_id      VARCHAR(20),
    order_quantity INT,
    order_amount   FLOAT
);

逐字段分析:从 DDL 到元数据描述

fact_order 的每个字段来解释"为什么要加这项元数据":

① name: 字段名 --- 直接从 DDL 抄过来,这是代码里调用的名字。

② role: 字段角色 --- 这是人工判断的,决定后续行为:

字段 DDL 线索 判断的角色 原因
order_id PRIMARY KEY primary_key SQL 里直接标注了
customer_id 命名含 _id,非主键 foreign_key 关联到 dim_customer 的外键
product_id 命名含 _id,非主键 foreign_key 关联到 dim_product 的外键
date_id 命名含 _id,非主键 foreign_key 关联到 dim_date 的外键
region_id 命名含 _id,非主键 foreign_key 关联到 dim_region 的外键
order_quantity INT,非 id measure 数值字段,可聚合(SUM/AVG)
order_amount FLOAT,非 id measure 数值字段,可聚合

角色为什么重要?合并节点会补全 primary_keyforeign_key 防止 JOIN 漏掉;measure 角色告诉 LLM 哪些字段能 SUM/AVG/COUNT。

③ description: 字段描述 --- 语义化解释这个字段是什么。DDL 里没有注释,需要人工补充。这是向量检索的关键------用户说中文,向量靠描述来匹配:

arduino 复制代码
用户说 "销售额" → 向量 → 命中 order_amount(描述:"订单金额")

描述写得越好,向量匹配越准。

④ alias: 中文别名 --- 用户可能用不同说法指代同一个字段:

css 复制代码
order_amount → 别名:[销售额, 订单金额, 收入]
order_quantity → 别名:[销量, 购买数量, 件数]

别名也会参与向量化,扩充匹配面。

⑤ sync: 是否同步到 ES 全文检索 --- 维度字段才需要同步(如 province 的"北京""上海"),主键、外键、度量的值没有搜索价值:

bash 复制代码
region_id → sync: false(搜"R001"没意义)
province → sync: true(搜"上海"有意义)
order_amount → sync: false(搜"8999.00"没意义)

转化成 YAML

把上面的分析结果写成 conf/meta_config.yaml。以 fact_order 为例,完整 YAML 如下:

yaml 复制代码
tables:
  - name: fact_order
    role: fact
    description: 订单事实表,记录订单数量和金额等核心指标。
    columns:
      - name: order_id
        role: primary_key
        description: 订单唯一标识。
        alias: [订单ID]
        sync: false

      - name: customer_id
        role: foreign_key
        description: 关联客户维度的外键。
        alias: [客户ID, 用户ID]
        sync: false

      - name: product_id
        role: foreign_key
        description: 关联商品维度的外键。
        alias: [商品ID, 产品ID]
        sync: false

      - name: date_id
        role: foreign_key
        description: 关联时间维度的外键。
        alias: [日期, 下单日期]
        sync: false

      - name: region_id
        role: foreign_key
        description: 关联地区维度的外键。
        alias: [地区ID, 区域ID]
        sync: false

      - name: order_quantity
        role: measure
        description: 订单中商品的购买数量。
        alias: [销量, 购买数量, 件数]
        sync: false

      - name: order_amount
        role: measure
        description: 订单金额。
        alias: [销售额, 订单金额, 收入]
        sync: false

另外还需要定义指标 。指标是跨字段的计算逻辑,比如 GMV = SUM(order_amount)。指标也作为 tables 下的一个条目,名字就是 MySQL 里的表名 metric_info

yaml 复制代码
  - name: metric_info
    role: metric
    description: 指标定义表,存储跨字段的计算指标,如 GMV、AOV 等。
    columns:
      - name: GMV
        description: 全称Gross Merchandise Value,表示所有订单的成交金额总和。
        alias: [成交总额, 订单总额]
        relevant_columns:
          - fact_order.order_amount

      - name: AOV
        description: 全称Average Order Value,表示所有订单的成交金额平均值。
        alias: [平均单价, 平均订单金额]
        relevant_columns:
          - fact_order.order_quantity

把指标放在 tables 下面而不是另起一个 metrics 顶级 key,是为了结构统一------tables 里的每一项对应 meta 库的一张表,role 字段区分是 dim(维度表)、fact(事实表)还是 metric(指标表)。

完整的 meta_config.yaml 已放在 conf/ 目录下,覆盖了 dw.sql 的 5 张表和 2 个指标。

总结:YAML 作为"单一事实来源"

meta_config.yaml 的核心价值是一份文件描述整个数据库的元数据。为什么不全自动从 dw.sql 解析?

  • 角色(role)无法自动判断customer_id 到底是主键还是外键?要给 dim_customer 才知道。order_quantity 是度量还是普通整数?SQL 分不出。
  • 描述和别名没法自动生成order_quantity 可以叫"订单数量"也可以叫"购买件数",没有 AI 能替业务方做这个决定。
  • sync 标记是业务决策:哪些字段值值得被搜索引擎索引?这是人定的。

所以元数据的生产路线是半自动:SQL 提供骨架(表名、字段名、类型),人补充语义(角色、描述、别名、sync),输出 YAML。


第二步:从 meta_config.yaml 到 MySQL meta 库

YAML 是人读的,MySQL 表是机器读的。这一步写一个 Python 脚本,读取 YAML,INSERT 进 MySQL。

目标表结构回顾

docker/mysql/meta.sql 建了四张表:

YAML 中的对应位置 id 格式
table_info tables[] {table_name}
column_info tables[].columns[] {table_name}_{column_name}
metric_info tables[]role: metric 的条目 {metric_name}
column_metric tables[].columns[].relevant_columns[] 联合主键

idVARCHAR 而不是自增整数的原因是:后续代码中直接用 id 拼出表名和字段名,不用 JOIN 查。column_info.id = "fact_order_order_amount",一眼就知道这是 fact_order 表的 order_amount 字段。

映射关系(YAML → SQL)

ini 复制代码
meta_config.yaml                         MySQL table_info / column_info / metric_info
─────────────────                        ────────────────────────────────────────────
tables[0].name   "dim_region"    ──────→  table_info.id = "dim_region"
tables[0].role   "dim"           ──────→  table_info.role = "dim"
tables[0].description "地区..."   ──────→  table_info.description = "地区..."

tables[0].columns[0]:
  name "region_id"               ──────→  column_info.id = "dim_region_region_id"
  role "primary_key"             ──────→  column_info.role = "primary_key"
  description "地区唯一标识。"     ──────→  column_info.description = "..."
  alias ["地区ID","区域ID"]       ──────→  column_info.alias = '["地区ID","区域ID"]'  (JSON)
  sync false                     ──────→  (不存数据库,脚本里控制 ES 同步逻辑)
  ─                                   →  column_info.table_id = "dim_region"

tables[N].columns[M] (role: metric):
  name "GMV"                     ──────→  metric_info.id = "GMV"
  description "成交金额总和..."    ──────→  metric_info.description = "..."
  alias ["成交总额","订单总额"]    ──────→  metric_info.alias = '["成交总额","订单总额"]'
  relevant_columns:
    "fact_order.order_amount"    ──────→  column_metric(column_id, metric_id)
                                         column_id = "fact_order_order_amount"
                                         metric_id = "GMV"

读取配置

pyyaml 读两个 YAML 文件。

py 复制代码
import yaml
from pathlib import Path

CONF_DIR = Path(__file__).resolve().parent  # conf/
META_CONFIG_PATH = CONF_DIR / "meta_config.yaml"
APP_CONFIG_PATH = CONF_DIR / "app_config.yaml"


def load_meta_config(META_CONFIG_PATH: Path = META_CONFIG_PATH) -> dict:
    """从 YAML 文件加载元数据配置,返回原生 dict。"""
    if not META_CONFIG_PATH.exists():
        raise FileNotFoundError(f"配置文件不存在:{META_CONFIG_PATH.absolute()}")
    with open(META_CONFIG_PATH, "r", encoding="utf-8") as f:
        return yaml.safe_load(f)

def load_app_config(APP_CONFIG_PATH: Path = APP_CONFIG_PATH) -> dict:
    """从 YAML 文件加载应用配置,返回原生 dict。"""
    if not APP_CONFIG_PATH.exists():
        raise FileNotFoundError(f"配置文件不存在:{APP_CONFIG_PATH.absolute()}")
    with open(APP_CONFIG_PATH, "r", encoding="utf-8") as f:
        return yaml.safe_load(f)

app_config = load_app_config()
meta_config = load_meta_config()

# ── 快速自测 ──

if __name__ == "__main__":
    print(app_config)
    print(meta_config)

然后执行uv run python conf/get_config.py就先配置就位


脚本设计

conf/sync_db.py 是同步脚本,核心逻辑分三步------把 meta_config.yaml 里的元数据分别灌入三处:

py 复制代码
# conf/sync_db.py 调用骨架
meta_config = load_meta_config()

# 第一步:写入 MySQL meta 库(结构化元数据)
#      → table_info, column_info, metric_info, column_metric
sync_to_mysql(meta_config)

# 第二步:字段向量化 → 写入 Qdrant(语义检索)
#      → 每个字段的 description + alias 生成 embedding
#      → 存入 Qdrant collection
sync_to_qdrant(meta_config)

# 第三步:维度值 → 写入 Elasticsearch(全文检索)
#      → sync=true 的维度字段,从 dw 库 SELECT DISTINCT 值
#      → 写入 ES index
sync_to_elasticsearch(meta_config)

第一步:写 MySQL

把 YAML 里的表→table_info、字段→column_info、指标→metric_info、关联→column_metric 四张表填满。映射关系上文已经画过图表,这里就是读 YAML → 拼 INSERT。代码暂时是空函数,后续实现。

第二步:写 Qdrant

Qdrant 是向量数据库,专门做语义匹配。用户问"各个地区的销售额",系统要找到 order_amount 这个字段,靠的就是向量。

具体做法:把每个字段的 name + description + alias 拼接成一段文本,用 BGE 模型把它变成一个 1024 维的浮点数数组(向量),存入 Qdrant。用户 query 来了也做同样的向量化,在 Qdrant 里找余弦相似度最高的几个字段。

yaml 复制代码
column_info 中的一条记录:
  name:        order_amount
  description: 订单金额
  alias:       [销售额, 订单金额, 收入]

  ↓ 拼接

"订单金额 order_amount 销售额 订单金额 收入"

  ↓ BGE 模型向量化

[0.023, -0.451, 0.892, ...]  (1024 个 float)

  ↓ 存入 Qdrant

collection: column_vectors
  id:       fact_order_order_amount
  vector:   [0.023, -0.451, 0.892, ...]
  payload:  {table: "fact_order", name: "order_amount", role: "measure", ...}

用户问"销售额"时,"销售额"的向量和 order_amount 的向量很接近,Qdrant 就把它排在第一位返回。

同理,metric_info 里的 GMV、AOV 也需要向量化存入 Qdrant,这样用户说"成交总额"才能命中 GMV 指标。

第三步:写 Elasticsearch

ES 用来做字段 的模糊搜索。比如用户问"手机类商品的销售额",ES 负责找到 dim_product 表中 category 的值 "手机数码"。

具体做法:遍历 sync=true 的维度字段,对每个字段去 dw 库执行 SELECT DISTINCT 拿到不重复的值列表,把每条值写入 ES index。ES 的 IK 中文分词器会把 "手机数码" 切成 "手机"、"数码",用户搜"手机"也能命中。

css 复制代码
column_info 中 sync=true 的字段:
  dim_product.category  → SELECT DISTINCT category FROM dim_product
                          → [手机数码, 家用电器, 鞋靴, 服饰, 食品饮料, 休闲零食]
  dim_product.brand     → SELECT DISTINCT brand FROM dim_product
                          → [苹果, 三星, 华为, 戴森, 美的, ...]
  dim_region.province   → SELECT DISTINCT province FROM dim_region
                          → [广东省, 浙江省, 四川省, 北京市, 上海市, 湖北省]
  ...

  ↓ 逐条写入 ES

ES index: data_agent
  doc 1: {field: "dim_product_category", value: "手机数码"}
  doc 2: {field: "dim_product_category", value: "家用电器"}
  ...

为什么这样做?------ "可重放的同步"

这整套设计的底层思想是幂等同步

复制代码
不管执行多少次,结果都一样,不会丢数据、不会重复。

具体做法:每次执行脚本时,先 DELETE meta 库中所有旧数据,再全部重新 INSERT。看起来粗暴,但有三个巨大好处:

  1. 改 YAML 即改完 :加一张表、改一个字段的 alias、增删一个指标------只要改 meta_config.yaml,跑一遍脚本,元数据库就同步了。

  2. 不怕脚本中途崩溃:如果中间报错退出,mysql 默认 autocommit=1 会导致部分数据写入。解决方案是用事务包起来:

    python 复制代码
    async with pool.begin() as conn:  # 开启事务
        await conn.execute("DELETE FROM column_metric")
        await conn.execute("DELETE FROM metric_info")
        await conn.execute("DELETE FROM column_info")
        await conn.execute("DELETE FROM table_info")
        # 批量 INSERT...
        # 事务结束自动 COMMIT------要么全成功,要么全回滚
  3. Git 友好meta_config.yaml 是一份纯文本 YAML,改了什么一目了然。团队协作时,改字段 alias 不再需要靠脑子记,git diff 直接看。


当前目录结构

txt 复制代码
n2sql-agent/
├── conf/
│   ├── meta_config.yaml          # 元数据定义(人读)
│   ├── app_config.yaml           # 连接配置
│   ├── get_config.py             # 读取配置
│   └── sync_db.py                # 同步脚本(三步灌入)
├── docker/
│   └── mysql/
│       ├── meta.sql              # meta 库 DDL(机器读,空壳表)
│       └── dw.sql                # dw 库 DDL + 测试数据(业务数据)
└── ...

下一步

元数据库就绪后,下一篇文章开始写真正的召回节点------从 meta 库读数据、灌入 Qdrant 和 ES、再用关键词去查。

科普:元数据库

一句话理解

元数据库(Meta Database)就是「描述数据的数据」。

业务数据库里存的是订单、客户、商品这些真实数据;元数据库里存的是这些表"长什么样"的说明书。

以图书馆来类比:

概念 图书馆类比 n2sql-agent 中
业务数据 书架上的真实书籍 dw.sql 里的 dim_productfact_order
元数据 图书馆编目卡片(书名、作者、书架号、分类) meta_config.yaml 里的字段描述、别名、角色
元数据库 存放编目卡片的卡片柜(可查询、排序、检索) MySQL 里的 meta 库(table_infocolumn_info 等表)

为什么需要元数据库?

用户问题:"各地区的 GMV 变化趋势怎么样?"

n2sql-agent 需要回答三个问题才能写出 SQL:

  1. "GMV"是什么 ?→ 查 metric_info 表,发现 GMV = SUM(order_amount)
  2. "各地区"对应哪个字段 ?→ 查 column_info 表,用向量匹配"地区" → 命中 dim_region.region_name
  3. GMV 从哪张表取 ?→ 查 metric_info.relevant_columnsfact_order.order_amount

没有元数据库,AI 只能靠"猜",猜错了 SQL 就跑不了。

核心概念

① 表(table_info)

字段 含义 示例
id 表编号 dim_region
name 表名称 dim_region
role 表类型 dim(维度表)/ fact(事实表)/ metric(指标表)
description 表的功能说明 "地区维度表,用于描述订单发生的地理区域信息"

② 列(column_info)

字段 含义 示例
id 列编号,格式 {表名}_{字段名} fact_order_order_amount
name 列名称 order_amount
type 数据类型 FLOAT
role 列角色 measure(指标量)/ dimension(维度) / foreign_key / primary_key
description 列的业务含义 "订单金额"
alias 中文别名列表(JSON) ["销售额", "订单金额", "收入"]
examples 数据示例(JSON) [8999.00, 9499.00, 125.00]
table_id 所属表 fact_order

③ 指标(metric_info)

字段 含义 示例
id 指标编码 GMV
name 指标名称 GMV
description 指标的定义和计算方式 "所有订单的成交金额总和"
relevant_columns 涉及哪些字段(JSON) ["fact_order.order_amount"]
alias 中文别名(JSON) ["成交总额", "订单总额"]

元数据如何参与召回?

整个流程是三层递进的:

vbnet 复制代码
用户说 "销售额"
    │
    ├── 向量检索(Qdrant):用 "销售额" 的向量去匹配 column_info 的 description + alias 的向量
    │   → 命中 order_amount(描述:"订单金额")
    │
    ├── 全文检索(ES):用 "销售额" 去 ES 里搜索字段对应的值
    │   → 在 dim_product 中搜 "手机" → 返回 "iPhone 15 Pro"、"Mate 60 Pro"
    │
    └── 元数据表(MySQL meta):根据命中结果补全上下文
        → order_amount 属于 fact_order 表
        → fact_order 通过 foreign_key 关联 dim_region、dim_product 维度表
        → 要写 JOIN 才能查出"各地区的销售额"

没有元数据库,这三层都做不到。它是一切召回的基础数据源


科普:维度表和事实表

一句话理解

维度表(Dimension Table):描述"谁、什么、何时、何地"。 事实表(Fact Table):记录"发生了什么"。

这两类表合在一起,构成了星型模型------数据仓库最经典的设计模式。

markdown 复制代码
         dim_region(何地)
              │
dim_customer──┼── fact_order ──dim_date(何时)
(谁)         │              (何时)
              │
         dim_product(什么)

中心是 fact_order(事实表),四个角是四张维度表,像一颗星星。

类比:超市收银小票

你去超市买完东西,拿到的小票长这样:

arduino 复制代码
超市小票
─────────────────────────
日期:2025-01-15           ← "何时"信息
商品:康师傅红烧牛肉面 ×2  ← "什么"信息(商品名、数量)
金额:¥19.60              ← "发生了多少钱"(事实)
收银台:2号                ← "何地"信息
会员卡号:8800123          ← "谁"信息
─────────────────────────
小票上的信息 属于 对应本项目表 为什么
金额 ¥19.60、数量 2 事实 fact_order(order_amount, order_quantity) 可量化的、会变化的数据
日期 2025-01-15 维度 dim_date(year, month, day) 描述"何时"
商品 康师傅红烧牛肉面 维度 dim_product(product_name, category) 描述"什么"
会员卡号 8800123 维度 dim_customer(customer_name, gender) 描述"谁"
收银台 2号 维度 dim_region(province, region_name) 描述"何地"

判断规则:这字段是维度还是事实?

一个简单的判断方法:

看这个字段能不能做 SUM / AVG / COUNT 聚合运算。能 → 事实表;不能 → 维度表。

字段 能做 SUM 吗? 角色
order_amount(订单金额) 可以,SUM 后 = 总销售额 事实
order_quantity(购买数量) 可以,SUM 后 = 总销量 事实
gender(性别) 不能,SUM("男") 毫无意义 维度
province(省份) 不能,SUM("广东省") 毫无意义 维度
product_name(商品名) 不能 维度

事实表的关键特征(以 fact_order 为例)

yaml 复制代码
order_id    customer_id  product_id  date_id   region_id  order_quantity  order_amount
──────────  ───────────  ──────────  ────────  ─────────  ──────────────  ────────────
ORD2025...  C001         P001        20250101  R001       1               8999.00
ORD2025...  C005         P003        20250101  R005       1               6999.00
...
  1. 行数最多:每笔订单一条记录,随业务增长不断涨
  2. 全是外键 + 度量customer_idproduct_id 等外键指向维度表;order_quantityorder_amount 是度量值
  3. 几乎全数字:事实表的列要么是数字 ID(外键),要么是能算的数值(度量)
  4. 自己"说不清" :看到 customer_id = C001 你不知道是谁,必须 JOIN 维度表才知道是"李伟"

维度表的关键特征(以 dim_product 为例)

erlang 复制代码
product_id  product_name              category    brand
──────────  ────────────────────────  ──────────  ──────
P001        iPhone 15 Pro             手机数码    苹果
P002        Galaxy S24 Ultra          手机数码    三星
...
  1. 行数稳定:商品种类不会频繁变化
  2. 描述性的列:都是文字(名称、类别、品牌),不是数字
  3. 自描述 :看到 product_id = P001 → 查到 iPhone 15 Pro,信息完整

为什么要分开存?------ 避免数据冗余

如果不用星型模型,把商品信息直接塞进每笔订单会怎样?

yaml 复制代码
❌ 不用星型模型(信息冗余):
order_id     product_name      category  brand    order_amount
ORD2025...   iPhone 15 Pro     手机数码  苹果     8999.00
ORD2026...   iPhone 15 Pro     手机数码  苹果     8999.00
ORD2027...   iPhone 15 Pro     手机数码  苹果     8999.00
→ "iPhone 15 Pro / 手机数码 / 苹果" 这个组合重复了 100 万次!

✅ 用星型模型(只存 ID):
order_id     product_id  order_amount    → fact_order 只有三列
ORD2025...   P001        8999.00
ORD2026...   P001        8999.00         → 商品信息只在 dim_product 存一次
ORD2027...   P001        8999.00

这样维度表里的 iPhone 15 Pro 改了只要改一个地方,不会漏掉。


科普:YAML

一句话理解

YAML(YAML Ain't Markup Language)是一种「给人看比给机器看更舒服」的配置文件格式。

和 JSON 一样是描述数据的,但 JSON 全是括号和引号看着累,YAML 靠缩进和短横线,更像笔记。

JSON vs YAML(同样描述一张表)

JSON 写法:

json 复制代码
{
  "name": "fact_order",
  "role": "fact",
  "description": "订单事实表,记录订单数量和金额等核心指标。",
  "columns": [
    {
      "name": "order_amount",
      "role": "measure",
      "description": "订单金额。",
      "alias": ["销售额", "订单金额", "收入"],
      "sync": false
    }
  ]
}

YAML 写法:

yaml 复制代码
name: fact_order
role: fact
description: 订单事实表,记录订单数量和金额等核心指标。
columns:
  - name: order_amount
    role: measure
    description: 订单金额。
    alias: [销售额, 订单金额, 收入]
    sync: false

同样一段数据,YAML 少了 {}""、逗号,视觉噪音少得多。这就是 n2sql-agent 选 YAML 存元数据配置的原因------人要反复改 alias 和 description,JSON 看得眼花,YAML 一眼扫过去就懂了。

YAML 的核心语法

① 键值对 --- 基础单元,用冒号分隔:

yaml 复制代码
name: fact_order         # 字符串
role: fact               # 字符串可以不加引号
order_quantity: 100      # 数字
sync: true               # 布尔值

② 列表 --- 用短横线 - 开头:

yaml 复制代码
# 行内写法(短列表推荐)
alias: [销售额, 订单金额, 收入]

# 多行写法(元素复杂时推荐)
columns:
  - name: order_id
    role: primary_key
    description: 订单唯一标识。
  - name: order_amount
    role: measure
    description: 订单金额。

③ 嵌套 --- 靠缩进(空格,不能用 Tab)表示层级:

yaml 复制代码
tables:                       # 第一层
  - name: fact_order          # 第二层(tables 的子项)
    columns:                  # 第三层(fact_order 的子项)
      - name: order_amount    # 第四层(columns 的子项)
        role: measure         # 第五层(order_amount 的子项)

YAML 里容易踩的坑

错误写法 原因
缩进混用 Tab 和空格 name: xxx(Tab 缩进) YAML 只认空格
冒号后没空格 name:fact_order 解析器分不清是键值对还是普通字符串
字符串含特殊字符没加引号 description: 订单:包括金额、数量 冒号被当成键值对分隔符
整数前导零 month: 08 08 在 YAML 里被当作八进制数,会出错

安全写法:不确定就加引号。

yaml 复制代码
description: "订单:包括金额、数量。"   # 有特殊字符时加引号保平安

本项目中 YAML 扮演的角色

markdown 复制代码
人  ←──── YAML ────→  Python 脚本 ────→ MySQL
    (读起来舒服)    (pyyaml 库解析)    (INSERT)

meta_config.yaml 是人和机器之间的中间语言

  • 对人来说:看起来像笔记,改 alias 改描述跟改文本文档一样简单
  • 对机器来说:yaml.safe_load() 一行代码就变成 Python 的 dict 和 list,直接遍历写入数据库
python 复制代码
import yaml
with open("conf/meta_config.yaml") as f:
    config = yaml.safe_load(f)
# config 现在是 Python 字典,直接遍历:
for table in config["tables"]:
    print(table["name"], table["role"])

这就是选 YAML 不选 JSON 也不选纯数据库管理的核心理由------配置类数据,先让人舒服,再让机器舒服。

相关推荐
Wang's Blog1 小时前
Go-Zero项目开发24: 基于Bitmap实现群聊消息已读未读
开发语言·后端·golang
Infedium1 小时前
英特物理AI仿真赋能制造!打破传统有限元瓶颈,研发提效降本翻倍
人工智能·制造
zandy10111 小时前
衡石 Agentic BI的ReAct 推理框架在 Agentic BI 中的工程化实践
前端·javascript·react.js
罗超驿2 小时前
JavaEE进阶之路:从Web架构原理到HTML标签全解析
前端·html·web·javaee
天国梦2 小时前
2026英语教学系统选型实战:AI如何让备课效率提升42%?天学网技术落地全解析
人工智能·学习
西安小哥2 小时前
从前端到AI工程师:一场跨越鸿沟的真实蜕变之旅
前端
Prince4182 小时前
侧边栏收起缩放适配方案
前端
Conan在掘金2 小时前
鸿蒙报错速查:struct 里嵌 namespace 声明就炸,根因 + 真解法
后端
hqyjzsb2 小时前
AI证书和传统证书有什么区别?
人工智能·金融·数据挖掘·数据分析·aigc·创业创新·学习方法