FDB Record Layer SQL进阶技巧:JOIN、子查询、Unnesting与窗口函数的8个实战示例

📅 2026/8/23 13:08:24
FDB Record Layer SQL进阶技巧:JOIN、子查询、Unnesting与窗口函数的8个实战示例
FDB Record Layer SQL进阶技巧JOIN、子查询、Unnesting与窗口函数的8个实战示例【免费下载链接】fdb-record-layerA relational database with SQL support built on FoundationDB项目地址: https://gitcode.com/gh_mirrors/fd/fdb-record-layerFDB Record LayerFoundationDB Record Layer是构建在 FoundationDB 之上的开源关系型数据库其SQL 接口Relational Layer提供了完整的关系查询引擎不仅支持标准 SQL 语法还内置了智能查询规划器会自动选择最优索引。本文面向新手用 8 个实战示例带你掌握JOIN 连接查询、子查询、Unnesting 数组展开、窗口函数这 4 类进阶 SQL 技巧并附上各特性的限制说明帮你避坑。SQL 层的核心实现位于 fdb-relational-core 模块官方 SQL 参考文档在docs/sphinx/source/SQL_Reference.md快速入门指南见docs/sphinx/source/SQL_Getting_Started.md。先了解FDB Record Layer 的 SQL 引擎与其他数据库不同FDB Record Layer 的表结构通过Schema Template模式模板实例化天然支持多租户架构-- 定义一次模板可创建多个相互隔离的租户数据库 CREATE SCHEMA TEMPLATE my_template CREATE TABLE customers ( customer_id BIGINT, name STRING, email STRING, PRIMARY KEY(customer_id) ) CREATE INDEX email_idx AS SELECT email FROM customers ORDER BY email CREATE DATABASE /my_app/production CREATE SCHEMA /my_app/production/main WITH TEMPLATE my_template 官方建议使用 SQL 接口而非直连 API因为查询规划器能力更强能自动完成索引选择等决策。JOIN 连接查询支持 INNER / LEFT / RIGHTFDB Record Layer 支持INNER JOIN、LEFT OUTER JOIN和RIGHT OUTER JOIN都接受ON条件或USING列列表。详细语法见docs/sphinx/source/reference/sql_commands/DQL/JOIN.rst。⚠️FULL OUTER JOIN目前暂不支持。示例1INNER JOIN 关联员工与部门表以下示例假设已有员工表和部门表emp中 Dave 的dept_id99在dept表中不存在CREATE TABLE emp ( id BIGINT, fname STRING, lname STRING, dept_id BIGINT, PRIMARY KEY(id) ) CREATE TABLE dept ( id BIGINT, name STRING, PRIMARY KEY(id) )INNER JOIN只保留两边都满足连接条件的行USING是列相等连接的简写并自动去重列SELECT e.fname, e.lname, d.name AS department FROM emp e INNER JOIN dept d USING (id) -- 等价写法ON e.dept_id d.idINNER关键字可省略直接写JOIN即可。示例2LEFT OUTER JOIN 找出「没有部门」的员工外连接会完整保留一侧的所有行另一侧没有匹配时自动填充NULL——这是查缺失数据的利器SELECT e.fname, e.lname, d.name AS department FROM emp e LEFT OUTER JOIN dept d ON e.dept_id d.id WHERE d.id IS NULL结果中 Davedept_id99会保留department 列显示为NULL。OUTER关键字可省略LEFT JOIN与LEFT OUTER JOIN等价A LEFT JOIN B与B RIGHT JOIN A产出行集相同。子查询技巧EXISTS 与派生表子查询可出现在 SQL 语句的多个位置分为相关子查询引用外层列和非相关子查询独立执行。语法参考docs/sphinx/source/reference/Subqueries.rst。示例3EXISTS 子查询做存在性判断EXISTS只关心子查询是否返回任何行常比IN更高效-- 非相关子查询子查询独立于外层执行 SELECT ida FROM a WHERE EXISTS (SELECT ida FROM a WHERE ida 1) -- 相关子查询内层引用外层 t1 的列对每行外层记录执行一次 SELECT * FROM a t1 WHERE EXISTS (SELECT * FROM b WHERE b.q t1.x)示例4相关子查询作为 FROM 派生表子查询还能放在FROM子句中充当派生表derived table配合相关条件可实现灵活的逐行关联-- sq 是每行 a 动态计算的派生表 SELECT x, sq.idr, sq.nr FROM a, (SELECT * FROM r WHERE r.idr a.x) sq这类写法在 Unnesting 场景中还有用武之地见下文。Unnesting 数组展开PartiQL 风格语法这是 FDB Record Layer 最独有的特性之一把数组字段展开成行流。官方文档明确说明它遵循PartiQL 风格——数组列直接作为表源出现在FROM子句而不支持标准 SQL 的UNNEST()表函数。语法细节见docs/sphinx/source/reference/Unnesting.rst。示例5展开标量数组 AT 生成序号-- 表 orders 含 tags STRING ARRAY 列 SELECT table.*, element, ordinal FROM orders, orders.tags AS element AT ordinal要点速记要点说明基本写法SELECT t.*, elem FROM t, t.arr AS elemAT ordinal生成元素的 1-based 位置序号NULL/空数组该行不产生任何输出行语义等同 INNER JOIN限制不支持 LEFT OUTER 展开不支持按 ordinal 排序示例6展开 STRUCT 数组访问嵌套字段当数组元素是STRUCT类型时可用element.field访问字段也可用element.*星号展开CREATE TYPE AS STRUCT line_item (sku STRING, qty INTEGER) CREATE TABLE orders ( order_id BIGINT, items line_item ARRAY, PRIMARY KEY (order_id) ) -- 展开明细并访问 struct 字段 SELECT o.order_id, item.sku, item.qty FROM orders o, o.items AS item WHERE item.qty 1 也可以用等价的长写法lateral 派生子查询规划器会生成相同执行计划SELECT o.order_id, elements.item.sku FROM orders o, (SELECT item FROM o.items) AS elements窗口函数ROW_NUMBER() 驱动向量 Top-K 搜索窗口函数对关联行集合做计算但不像聚合函数那样把多行压成一行。当前 Relational Layer 中窗口函数服务于一个高光场景基于 HNSW 向量索引的语义搜索KNN核心函数是ROW_NUMBER()RANK()、LAG()等暂未支持。完整文档见docs/sphinx/source/reference/sql_commands/DQL/WINDOW_FUNCTIONS.rst。 关键规则按 SQL 标准窗口函数不允许出现在 WHERE 子句中必须用QUALIFY子句过滤窗口函数结果。示例7ROW_NUMBER() QUALIFY 实现 Top-K 最近邻搜索假设documents表存有文本向量并创建了 HNSW 向量索引CREATE VECTOR INDEX documentsEuclideanIndex USING HNSW ON documentsView(embedding) PARTITION BY(zone, bookshelf) OPTIONS (metric euclidean_metric) -- 找到 fiction 书架上与目标向量最相似的 Top 3 文档 SELECT docId, euclidean_distance(embedding, CAST([1.0, 0.0, 0.0] AS VECTOR(3, HALF))) AS distance FROM documents WHERE zone zone1 AND bookshelf fiction QUALIFY ROW_NUMBER() OVER ( PARTITION BY zone, bookshelf ORDER BY euclidean_distance(embedding, CAST([1.0, 0.0, 0.0] AS VECTOR(3, HALF))) ASC ) 3查询规划器会自动识别这一模式并走 HNSW 索引高效完成检索。示例8分区 Top-K 与 ef_search 调参PARTITION BY把结果集切分成独立分区如按zone, bookshelf每个分区各自排名OPTIONS (ef_search n)可动态控制召回质量——值越大召回率越高但查询越慢QUALIFY ROW_NUMBER() OVER ( PARTITION BY zone, bookshelf ORDER BY euclidean_distance(embedding, target) ASC OPTIONS (ef_search 100) ) 5⚠️ 两个易错点查询中使用的距离函数必须与建索引时的 metric 一致如euclidean_metric配euclidean_distance()。QUALIFY中ROW_NUMBER()目前只支持和比较。附赠WITH 递归 CTE 查层级数据除本文四大主题外WITH公共表表达式CTE也值得一试递归 CTE特别适合员工汇报线、目录树等层级数据还支持TRAVERSAL ORDER指定遍历策略深度优先/广度优先。语法参考docs/sphinx/source/reference/sql_commands/DQL/WITH.rstWITH RECURSIVE reports(id, name, depth) AS ( SELECT id, name, 0 FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.name, r.depth 1 FROM employees e JOIN reports r ON e.manager_id r.id ) SELECT * FROM reports⚠️ 系统不会自动防止无限递归请确保递归查询有正确的终止条件。常见限制速查表特性状态FULL OUTER JOIN❌ 暂不支持标准UNNEST()表函数❌ 不支持请使用 PartiQL 风格语法数组展开的 LEFT OUTER 变体❌ 不支持语义等同 INNER JOINRANK()/DENSE_RANK()/LAG()❌ 窗口函数场景暂仅支持ROW_NUMBER()WHERE 中使用窗口函数❌ 必须改用QUALIFY递归 CTE✅ 支持需自行保证终止条件总结本文带你用 8 个实战示例走完了 FDB Record Layer SQL 的进阶之路JOININNER / LEFT / RIGHT OUTER JOINON/USING外连接是查缺失数据的首选子查询EXISTS做存在性判断派生表 相关子查询实现灵活逐行关联Unnesting记住 PartiQL 风格FROM t, t.arr AS elem AT ordinal这是它区别于传统 SQL 的招牌特性窗口函数ROW_NUMBER() OVER (...) QUALIFY是向量 Top-K 语义搜索的标准姿势。搭配智能查询规划器与 HNSW 向量索引FDB Record Layer 的 SQL 层已经把关系查询和向量检索统一在同一套语法之下——对新手来说掌握以上四招就足以应对绝大多数进阶查询场景了。【免费下载链接】fdb-record-layerA relational database with SQL support built on FoundationDB项目地址: https://gitcode.com/gh_mirrors/fd/fdb-record-layer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考