在数字化转型的浪潮中,ERP系统沉淀了海量的销售数据,但它们往往以孤立的二维表形式沉睡在关系型数据库中。本文将带你通过一套流畅、实用的ETL(抽取、转换、加载)流程,将这些传统数据转化为Apache AGE中的动态知识图谱。我们将通过真实的Python代码示例,手把手教你如何高效地将销售订单、客户、产品等实体及其错综复杂的关系同步至图数据库,为后续构建AI原生的自然语言问答与智能业务预警系统打下坚实的数据底座。
一、 蓝图绘制:从关系表到图模型的思维跃迁
在正式编写ETL代码之前,我们需要先在脑海中完成一次"降维打击"式的模型转换。传统的ERP销售模块通常由销售订单表、客户表、产品表等通过外键关联。而在Apache AGE的世界里,我们需要将这些扁平的数据重构为立体的图结构。
以核心的"销售订单"业务为例,我们的ETL目标非常明确:
- 抽取实体(节点) :将客户信息转化为
Customer节点,将订单信息转化为SalesOrder节点。 - 构建关系(边) :将原本表中的外键(如
customer_id)转化为连接两者的PLACED_ORDER边。
这种转换不仅保留了业务数据,更直接显性化了业务逻辑,让AI在后续查询时能够"顺藤摸瓜",极速穿透多层关联。

二、 核心实战:基于Python与CSV的高效ETL流水线
Apache AGE提供了极高效率的底层加载函数。为了保证ETL过程的流畅与实用,我们采用"Python处理业务逻辑 + CSV作为中间载体 + AGE内置函数批量加载"的黄金组合方案。
1. 准备阶段:模拟从RDB抽取数据
在实际生产中,这一步通常通过SQL从MySQL或PostgreSQL中查询。为了演示方便,我们直接用Python的Pandas库模拟从ERP数据库中抽取出的两张核心表数据:
python
1import pandas as pd
2import os
3from datetime import datetime
4
5# 模拟从ERP关系型数据库中提取的原始数据
6customers_data = {
7 'customer_id': ['CUST001', 'CUST002'],
8 'name': ['某某科技公司', '未来贸易集团'],
9 'region': ['华东', '华北']
10}
11
12orders_data = {
13 'order_id': ['SO20260501', 'SO20260502'],
14 'customer_id': ['CUST001', 'CUST002'], # 外键,将转化为图的边
15 'amount': [50000, 120000],
16 'status': ['待发货', '已完成']
17}
18
19df_customers = pd.DataFrame(customers_data)
20df_orders = pd.DataFrame(orders_data)
2. 转换阶段:生成AGE专属的节点与边CSV
AGE的内置加载函数对CSV格式有特定要求。节点文件需要包含ID和属性,而边文件必须包含 start_id(起点ID)、end_id(终点ID)以及关系属性。
python
1# 创建临时目录存放ETL中间文件
2etl_dir = f"/tmp/age_etl_{datetime.now().strftime('%Y%m%d%H%M')}"
3os.makedirs(etl_dir, exist_ok=True)
4
5# --- 生成节点CSV ---
6# 1. 客户节点文件
7customer_csv_path = f"{etl_dir}/customers.csv"
8df_customers.to_csv(customer_csv_path, index=False)
9
10# 2. 销售订单节点文件 (需保留customer_id用于后续关联,但在加载节点时可剔除或保留)
11order_csv_path = f"{etl_dir}/orders.csv"
12df_orders.to_csv(order_csv_path, index=False)
13
14# --- 生成边CSV ---
15# 3. 下单关系边文件 (核心:建立客户与订单的连接)
16edges_data = {
17 'start_id': df_orders['customer_id'], # 起点:客户ID
18 'end_id': df_orders['order_id'], # 终点:订单ID
19 'order_date': datetime.now().date() # 边的属性:下单日期
20}
21df_edges = pd.DataFrame(edges_data)
22edge_csv_path = f"{etl_dir}/placed_order_edges.csv"
23df_edges.to_csv(edge_csv_path, index=False)
24
25print(f"ETL转换完成,文件已生成至: {etl_dir}")
3. 加载阶段:注入图数据库灵魂
最后,我们使用Python的 psycopg 驱动连接Apache AGE,调用其原生的 load_labels_from_file 和 load_edges_from_file 函数,实现毫秒级的数据注入。
python
1import psycopg
2
3# 配置AGE数据库连接信息
4DB_CONFIG = {
5 "dbname": "erp_graph_db",
6 "user": "postgres",
7 "password": "your_password",
8 "host": "localhost",
9 "port": 5432
10}
11
12GRAPH_NAME = 'erp_sales_graph'
13
14def load_data_to_age():
15 with psycopg.connect(**DB_CONFIG) as conn:
16 with conn.cursor() as cur:
17 # 确保AGE扩展已加载并设置搜索路径
18 cur.execute("LOAD 'age';")
19 cur.execute("SET search_path = ag_catalog, \"$user\", public;")
20
21 # 1. 批量加载客户节点
22 print("正在加载客户节点...")
23 cur.execute(
24 "SELECT load_labels_from_file(%s, %s, %s, true, true);",
25 (GRAPH_NAME, 'Customer', customer_csv_path)
26 )
27
28 # 2. 批量加载订单节点
29 print("正在加载订单节点...")
30 cur.execute(
31 "SELECT load_labels_from_file(%s, %s, %s, true, true);",
32 (GRAPH_NAME, 'SalesOrder', order_csv_path)
33 )
34
35 # 3. 批量加载"下单"关系边
36 print("正在建立客户关系边...")
37 cur.execute(
38 "SELECT load_edges_from_file(%s, %s, %s, true);",
39 (GRAPH_NAME, 'PLACED_ORDER', edge_csv_path)
40 )
41
42 conn.commit()
43 print("恭喜!ERP销售数据已成功同步至Apache AGE!")
44
45# 执行加载
46# load_data_to_age()
三、 进阶优化:让ETL流程更健壮
在实际的企业级应用中,数据是源源不断产生的。为了让这套ETL流程具备生产级的健壮性,你可以考虑以下两个优化方向:
- 增量同步策略 :不要每次都全量加载。可以在ERP的源表中增加
update_time字段,ETL脚本每次只抽取"上次同步时间"之后发生变更的订单数据。同时,利用sync_metadata元数据表记录每次同步的时间戳,实现精准的增量更新。 - 容错与重试机制:网络波动或文件格式错误可能导致加载失败。建议在Python脚本中引入重试装饰器(Retry Mechanism),当加载失败时自动等待几秒后重试,并记录详细的错误日志,确保数据最终的一致性。
总结
通过上述步骤,我们成功搭建了一条从传统RDB到Apache AGE的数据高速公路。这不仅仅是数据的搬运,更是数据价值的重塑。当销售订单、客户与产品以图的形式紧密相连时,你的ERP系统就已经具备了"AI原生"的潜质。接下来,无论是让AI助手回答"华东地区上个月欠款超过10万的客户有哪些",还是实现"某产品质检异常时自动预警所有关联在途订单",都将变得轻而易举。现在,是时候去探索图数据带来的无限可能了!