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

推荐订阅源

The GitHub Blog
The GitHub Blog
IT之家
IT之家
B
Blog RSS Feed
罗磊的独立博客
GbyAI
GbyAI
博客园 - Franky
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News
博客园 - 聂微东
N
Netflix TechBlog - Medium
博客园 - 三生石上(FineUI控件)
人人都是产品经理
人人都是产品经理
U
Unit 42
博客园 - 叶小钗
Jina AI
Jina AI
MyScale Blog
MyScale Blog
雷峰网
雷峰网
B
Blog
Hugging Face - Blog
Hugging Face - Blog
Blog — PlanetScale
Blog — PlanetScale
Recent Announcements
Recent Announcements
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell

博客园 - 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一起成长~