DeepSeek总结的PostgreSQL 19 中的 SQL/PGQ:无需图数据库的图查询
来源:https://thebuild.com/blog/sqlpgq-in-postgresql-19-graph-queries-without-the-graph-database/
PostgreSQL 19 中的 SQL/PGQ:无需图数据库的图查询
作者:未指明
日期:2026-06-01
阅读时间:5 分钟
分类:PostgreSQL
PostgreSQL 19 附带了GRAPH_TABLE,这是对 ISO/IEC 9075-16:2023 SQL/PGQ 标准的一个实现。简而言之:你可以在关系表集合上声明一个属性图,然后使用看起来很像 Cypher 的模式匹配语法来查询该图,而无需离开 Postgres。
详细的版本需要一些背景信息,因为图数据库是这样一个产品类别:其营销宣传始终领先于工程技术,而且“图查询到底做什么”这个技术问题的直白答案比它本应有的更难找到。
图数据库是什么(简述)
图数据库存储顶点(节点)和边(关系)。一个顶点代表一个事物——一个人、一个账户、一个产品。一条边代表一种连接——一段友谊、一种所有权、一次购买。两者都可以携带属性:一个 Person 顶点可能有姓名和出生日期;一条 Knows 边可能有“起始时间”属性。
想要使用图数据库的原因——撇开建模美学的争论不谈,唯一令人信服的技术理由是——你的工作负载中的查询是“找出这些顶点之间满足边和中间顶点约束的长度不超过 N 的所有路径”。这种查询,在使用规范化模式的 SQL 中表达,是自连接和递归 CTE 的噩梦。在图查询语言中表达,它就是一行可读的代码。
业界主要的三种图查询语言是 Cypher(Neo4j 的语言,现在也被 Memgraph 等实现)、Gremlin(Apache TinkerPop 的遍历语言,更具过程性)和 SPARQL(W3C 为 RDF 三元组存储设计的语言,技术上是不同的数据模型)。ISO 标准机构审视了这种局面,决定每个人都应该统一到一种语言上,于是产生了作为独立语言的 GQL,以及作为 SQL 内嵌形式的相同模式匹配语法的 SQL/PGQ。两者都在 2023 年定稿。PostgreSQL 19 实现的是 SQL/PGQ。
PostgreSQL 如何实现它
该实现刻意不是一个图存储引擎。它实际上是一个重写器:属性图是将现有关系表映射到顶点/边抽象的元数据,而GRAPH_TABLE中的图模式会被重写为一棵关系连接和过滤器的树,然后规划器使用其现有机制来处理这棵树。
你声明图:
CREATEPROPERTY GRAPH social_graph VERTEXTABLES(people LABEL Person PROPERTIES(id,name,birth_year))EDGETABLES(friendships SOURCEKEY(person_a)REFERENCESpeople(id)DESTINATIONKEY(person_b)REFERENCESpeople(id)LABEL Knows PROPERTIES(since));你查询它:
SELECTb_nameFROMGRAPH_TABLE(social_graphMATCH(a:PersonWHEREa.name='Jan')-[k:Knows]->(b:Person)COLUMNS(b.nameASb_name));MATCH子句描述一个模式;(a:Person)是一个标签为Person的顶点,-[k:Knows]->是一条标签为Knows的出边,其余部分是过滤器和投影。重写器在pg_propgraph_element中查找Person和Knows,找到底层的people和friendships表,生成等效的连接,然后规划器从那里接手。
这个设计带来了两点你应该留意的后果。
第一:你现有的索引可以工作。因为重写产生的是普通的关系连接,所以friendships(person_a)上的索引的使用方式与任何其他查询完全相同。没有单独的图存储需要填充,没有单独的索引结构需要维护,没有单独的查询优化器需要调优。成本模型就是你已有的成本模型。
第二:你现有的性能特征也同样适用。深度为 N 的图遍历变成了 N 次连接。对于较小的 N,这没问题。对于可变深度的遍历——而最初的 PG19 实现尚不支持这些——重写器将执行你的递归 CTE 已经在做的事情,性能也相似。
相对于 Neo4j 等的定位
诚实的比较分为两种情况。
对于浅层、固定深度的图查询——实际生产系统中绝大多数“图”查询——Postgres 19 现在具有竞争力。在 Postgres 中,经过索引的外键上的两跳或三跳遍历速度很快,因为 Postgres 擅长索引连接,而这正是重写器产生的结果。如果你的“图”工作负载是“向我展示我朋友最近的购买记录”,你应该在 Postgres 上运行它。你几乎肯定已经在这样做了,只不过是以笨拙的连接形式;SQL/PGQ 只是让笨拙的部分消失了。
对于深层、可变长度的遍历——Neo4j 真正为之构建的工作负载——Postgres 目前还没有竞争力,而且 19 中的 SQL/PGQ 并没有改变这一点。PG19 的实现明确不支持量化的模式(-[k:Knows*1..5]->,查找最多五跳的所有路径)。可变长度路径计划在未来的版本中实现。即使它们落地了,底层的执行模型——由现有规划器驱动的关系连接——在结构上也与 Neo4j 的“免索引邻接”不同,后者每个顶点都存储指向其关联边的直接指针,遍历成本与结果大小成正比,而不是与总图大小成正比。
“免索引邻接”是运行 Neo4j 的一个真正的技术原因。它每一步都为你带来 O(度) 的遍历成本,而关系模型的等效实现最好是每步 O(log n),如果你的边表很大且索引不太适合访问模式,成本甚至会急剧增加。对于一个在拥有上亿顶点的图上进行六度分隔的查询,这种差异可能达到三个数量级。
对于“找出我的 CFO 在上个季度通过电子邮件联系过的会计师”这样的查询,则不然。
这对你的意义
如果你曾经因为模糊地感觉你的数据是图形态而启动了 Neo4j,那么这个版本就是让你重新考虑的契机。要问的问题是,你的实际查询是浅层且固定深度的,还是深层且可变长度的。第一类现在由 Postgres 很好地服务,你可以精简你的技术栈。第二类仍然是专用图数据库的用武之地,至少在可变长度路径实现落地并且规划器学会明智地计算其成本之前是这样——即使到那时,“免索引邻接”的差距仍将使得专用图数据库在真正的深度遍历工作负载中保持竞争力。
更有趣的是第三种情况:你原本没有图工作负载,但现在语法就在那里,你可能会有了。有一类操作型查询,用纯 SQL 表达非常笨拙,以至于没人会去写,而使用 SQL/PGQ 它们就变得可写了。例如,审计通过权限模型的访问路径、追踪派生数据集经过一系列转换的血缘关系、映射应用程序外键关系的依赖图。这些在 SQL 中一直都是可表达的;但它们很少在 SQL 中被实际编写出来。降低语法成本也就降低了团队中有人真正去做这项分析的门槛。
这就是将 SQL/PGQ 内置到数据库中的被低估的好处。这并不是说 Postgres 变成了一个图数据库;而是说,在不改变存储层的情况下,你可以从现有数据中提出的图形态问题的范围大大扩大了。
