惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
腾讯CDC
D
Docker
G
Google Developers Blog
D
DataBreaches.Net
雷峰网
雷峰网
Blog — PlanetScale
Blog — PlanetScale
S
SegmentFault 最新的问题
The Cloudflare Blog
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
量子位
美团技术团队
aimingoo的专栏
aimingoo的专栏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Engineering at Meta
Engineering at Meta
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - AlfredZhao

Oracle Property Graph 结合 In-Memory 的性能验证 sed 一条命令批量替换 SQL 文件路径 执行新项目 python 脚本前,先用 conda 建一个独立环境 Git 提交代码:从报错到 SSH 免密推送 GitHub 克隆他人私有仓库:从授权到下载 APEX 无法分配 SH 用户?一个存储过程轻松解决 SH 中文化样例数据使用手册 Oracle 排除非业务表:一份能直接抄的“全量过滤”SQL 进程都杀了,为什么 `netstat` 还能看到端口? MAC 空间告急?Codex 的 156GB 缓存垃圾,这样清! 人工清理问题数据:先查准,再删除 TK(Trusted Knowledge)为何而生? 使用快捷键快速切换 Mac 外接显示器模式 客户环境 Nginx 配置:流式报表与超时排查要点 Security Central:数据库安全的统一控制与运营平台 Palantir 眼中的一次“订单可能延期”,如何成为实时决策的起点? 从一条订单消息到 Bronze、Silver、Gold:我的第一次 Kafka + Lakehouse 实验 UUID v4 与 v7:同样是唯一 ID,为什么数据库表现可能完全不同? 用 crontab 给 LLM 使用量装上“监控眼” DeepSeek 与 GPT API 价格调整:该关注什么? 查看 Oracle 数据库中的定时任务执行情况 Codex 专用用户登录后自动进入默认项目目录 Mac 小技巧:用方向键优雅处理超长网址 Oracle GDD 与 Raft:别把共识机制和数据主权混为一谈 在 Oracle APEX 中用 BGE_BASE 生成向量:从模型导入到历史数据更新 行业人+AI:真正有价值的四个关键要素 知识库文件解析失败:一次由 Domain Index 引发的定位记录 Unicode 码位数、UTF-8 字节数、中英文差异和 Oracle 长度别再混淆了 Skill 的使用:从路径到能力边界 切换 Embedding 模型时,千万别忽略历史向量维度
理解Oracle Property Graph:以账户转账示例完成图特性最小测试
AlfredZhao · 2026-09-13 · via 博客园 - AlfredZhao

Oracle Property Graph 让关系型数据也能用图的方式查询。本文用一组转账数据,走一遍建表、建图、单跳与多跳查询的完整流程,并整理 ORA-40954: unknown label 的常见原因。

01 | 准备测试表与数据

建议在空白 schema 中执行。若对象已存在,先手工删除或更换名称。

CREATE TABLE money_accounts (
    account_id   NUMBER       CONSTRAINT money_accounts_pk PRIMARY KEY,
    account_name VARCHAR2(50) NOT NULL
);

CREATE TABLE money_transfers (
    transfer_id    NUMBER       CONSTRAINT money_transfers_pk PRIMARY KEY,
    from_account_id NUMBER      NOT NULL,
    to_account_id   NUMBER      NOT NULL,
    amount          NUMBER(12,2),
    transfer_time   TIMESTAMP,
    CONSTRAINT money_transfer_from_fk
        FOREIGN KEY (from_account_id)
        REFERENCES money_accounts(account_id),
    CONSTRAINT money_transfer_to_fk
        FOREIGN KEY (to_account_id)
        REFERENCES money_accounts(account_id)
);

插入 6 个账户:

INSERT INTO money_accounts VALUES (1, '账户A');
INSERT INTO money_accounts VALUES (2, '账户B');
INSERT INTO money_accounts VALUES (3, '账户C');
INSERT INTO money_accounts VALUES (4, '账户D');
INSERT INTO money_accounts VALUES (5, '账户E');
INSERT INTO money_accounts VALUES (6, '账户F');

构造一条连续转账链,并增加一条捷径:

graph LR A[A] --> B[B] B --> C[C] C --> D[D] D --> E[E] E --> F[F] %% 分支/快捷路径 B -->|快捷路径| E

INSERT INTO money_transfers
VALUES (101, 1, 2, 1000, TIMESTAMP '2026-09-01 09:00:00');
INSERT INTO money_transfers
VALUES (102, 2, 3, 900, TIMESTAMP '2026-09-01 10:00:00');
INSERT INTO money_transfers
VALUES (103, 3, 4, 800, TIMESTAMP '2026-09-01 11:00:00');
INSERT INTO money_transfers
VALUES (104, 4, 5, 700, TIMESTAMP '2026-09-01 12:00:00');
INSERT INTO money_transfers
VALUES (105, 5, 6, 600, TIMESTAMP '2026-09-01 13:00:00');
-- B 直接转到 E,形成另一条路径
INSERT INTO money_transfers
VALUES (106, 2, 5, 500, TIMESTAMP '2026-09-01 14:00:00');
COMMIT;

02 | 创建属性图

显式声明两个标签:ACCOUNT 是顶点标签,TRANSFER 是边标签。

CREATE PROPERTY GRAPH money_graph
  VERTEX TABLES (
    money_accounts
      KEY (account_id)
      LABEL account
      PROPERTIES (
        account_id,
        account_name
      )
  )
  EDGE TABLES (
    money_transfers
      KEY (transfer_id)
      SOURCE KEY (from_account_id)
        REFERENCES money_accounts (account_id)
      DESTINATION KEY (to_account_id)
        REFERENCES money_accounts (account_id)
      LABEL transfer
      PROPERTIES (
        transfer_id,
        amount,
        transfer_time
      )
  );

未加双引号的标识符会被 Oracle 规范化为大写,因此 LABEL account 可以通过 IS ACCOUNT 匹配。

03 | 单跳查询

SELECT *
FROM GRAPH_TABLE(
  money_graph
  MATCH (a IS ACCOUNT)-[t IS TRANSFER]->(b IS ACCOUNT)
  COLUMNS (
    a.account_id   AS source_account,
    a.account_name AS source_name,
    t.amount       AS amount,
    b.account_id   AS target_account,
    b.account_name AS target_name
  )
)
ORDER BY source_account, target_account;

它应返回 6 条直接转账关系。

04 | 1~5 跳查询

SELECT *
FROM GRAPH_TABLE(
  money_graph
  MATCH (a IS ACCOUNT)-[IS TRANSFER]->{1,5}(b IS ACCOUNT)
  COLUMNS (
    a.account_id   AS source_account,
    a.account_name AS source_name,
    b.account_id   AS target_account,
    b.account_name AS target_name
  )
)
WHERE source_account = 1
ORDER BY target_account;

从账户 A 出发,可以匹配到:

  • A→B:1 跳
  • A→B→C:2 跳
  • A→B→C→D:3 跳
  • A→B→C→D→E:4 跳
  • A→B→C→D→E→F:5 跳
  • A→B→E:另一条 2 跳路径
  • A→B→E→F:另一条 3 跳路径

因此 E 和 F 可能出现多次,因为查询匹配的是“路径”,而不只是去重后的终点。如果只想看可达账户,可以在外层去重:

SELECT DISTINCT target_account, target_name
FROM GRAPH_TABLE(
  money_graph
  MATCH (a IS ACCOUNT)-[IS TRANSFER]->{1,5}(b IS ACCOUNT)
  COLUMNS (
    a.account_id   AS source_account,
    b.account_id   AS target_account,
    b.account_name AS target_name
  )
)
WHERE source_account = 1
ORDER BY target_account;

05 | ORA-40954 常见原因

① 把表名当成标签名

图定义是 LABEL account,正确匹配是 (a IS ACCOUNT),而不是 (a IS MONEY_ACCOUNTS)IS 后面填的是 Label,不是底层表名。

② 没有显式声明预期标签

如果图定义没有 LABEL account,却查询 (a IS ACCOUNT),Oracle 就可能找不到该标签。建议在教学和生产 DDL 中显式声明 Label。

③ 双引号导致大小写敏感

若创建时写 LABEL "Account",标签名就是大小写敏感的 Account,查询必须写 (a IS "Account")(a IS ACCOUNT) 无法匹配。最佳实践是标签不使用双引号。

④ 标签存在,但元素类型用错

TRANSFER 只定义在边上,却写成 (a IS TRANSFER);或 ACCOUNT 只定义在顶点上,却写成 -[e IS ACCOUNT]->,都会出现标签无法用于当前位置的问题。

快速定位是哪一个标签有问题

先单独测试顶点标签:

SELECT *
FROM GRAPH_TABLE(
  money_graph
  MATCH (a IS ACCOUNT)
  COLUMNS (a.account_id AS account_id)
);

再单独测试边标签:

SELECT *
FROM GRAPH_TABLE(
  money_graph
  MATCH (a)-[t IS TRANSFER]->(b)
  COLUMNS (t.transfer_id AS transfer_id)
);

第一个失败说明 ACCOUNT 标签有问题;第二个失败说明 TRANSFER 标签有问题。

关注我,和AI一起成长~