ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

在 PostgreSQL 中用 PRQL 编写查询:PL/PRQL 扩展与 prqlc 方言支持实战指南

在 PostgreSQL 中用 PRQL 编写查询:PL/PRQL 扩展与 prqlc 方言支持实战指南 后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载本篇技术指南围绕 PRQL 项目官方文档中 PostgreSQL 集成指南 展开系统讲解 PL/PRQL一个让开发者直接在 PostgreSQL 数据库中编写 PRQL 函数的扩展的原理、两种核心用法——plprql语言函数与prql即时编译函数并结合仓库中 prqlc 编译器的sql.postgres方言实现说明 PRQL 代码在底层是如何被翻译成 PostgreSQL 可执行的 SQL 的。读完本文你将能够在自己的 PostgreSQL 实例中定义 PRQL 函数、在 ORM 中嵌入 PRQL 查询并理解 PRQL 到 Postgres SQL 的完整编译链路。PL/PRQL 是什么PL/PRQL 是一个 PostgreSQL 扩展extension它赋予 PostgreSQL 一种名为plprql的过程语言允许开发者用 PRQL 而不是传统 SQL 来编写数据库函数。它解决的核心痛点是PRQL 语法比 SQL 更适合表达数据管道pipeline式的查询逻辑而通过扩展形式PRQL 代码可以直接生长在数据库内部无需在应用层与数据库之间反复搬运字符串。从 PRQL 项目的视角看PL/PRQL 位于外部集成层与 ClickHouse、DuckDB、Jupyter 等集成并列参见 集成总览。它在整个 PRQL 技术栈中的定位是消费端——将用户编写的 PRQL 编译为 PostgreSQL 可以直接执行的 SQL 语句。工作原理pgrx 框架PL/PRQL 函数本质上是中介intermediaries它接收用户的 PRQL 代码将其编译成 SQL 语句再交给 PostgreSQL 执行。这个过程的关键在于扩展本身是基于 pgrx 框架用 Rust 开发的。pgrx 是社区广泛使用的、用 Rust 开发 PostgreSQL 扩展的框架它封装了 PostgreSQL 的内部 API、类型转换以及编写函数时所需的各种函数钩子function hooks使 Rust 开发者不必直接面对 PostgreSQL 复杂的 C 语言 ABI。PL/PRQL 正是借助 pgrx 完成了 PRQL 与 PostgreSQL 的胶水工作通过 pgrx 注册plprql语言处理器让CREATE FUNCTION ... LANGUAGE plprql语法可用通过 pgrx 的类型转换机制处理 PRQL 查询参数如函数入参与 PostgreSQL 类型之间的映射在函数调用时把函数体内的 PRQL 源码交给 PRQL 编译器即仓库中的prqlc编译再将产出的 SQL 交给 PostgreSQL 执行。关于PRQL 代码如何变成 SQL这正是本仓库prqlc编译器所负责的后文会结合源码详细展开。用 plprql 语言定义数据库函数PL/PRQL 的核心用法是用plprql语言说明符language specifier创建函数。官方文档给出一个完整的比赛战绩统计示例我们用这个例子深入拆解其语法结构。完整示例与执行结果create function match_stats(int) returns table(player text, kd_ratio float) as $$ from matches filter match_id $1 group player ( aggregate { total_kills sum kills, total_deaths sum deaths } ) filter total_deaths 0 derive kd_ratio total_kills / total_deaths select { player, kd_ratio } $$ language plprql; select * from match_stats(1001) player | kd_ratio ------------------- Player1 | 0.625 Player2 | 1.6 (2 rows)这个例子演示了 PL/PRQL 函数的完整工作流函数声明create function match_stats(int)声明函数入参为intreturns table(...)声明返回的表结构player text, kd_ratio float函数体as $$ ... $$之间是 PRQL 管道代码$$是 PostgreSQL 的标准美元引号dollar-quoting用于避免转义函数体内的单引号语言说明language plprql告诉 PostgreSQL 用 PL/PRQL 处理器来解释函数体参数引用PRQL 代码中的$1引用函数的第一个入参match_id这是 PL/PRQL 与 PostgreSQL 函数参数体系的衔接点PRQL 管道函数体是一段标准的 PRQL 查询包含from、filter、groupaggregate、derive、select等变换。这段 PRQL 会在函数被调用时编译为 SQL 再执行调用结果select * from match_stats(1001)返回按player分组统计的击杀/死亡比KD Ratio并过滤掉total_deaths为 0 的行避免除零。值得注意的 PRQL 语法点filter match_id $1PRQL 用表示相等比较单用于赋值/命名这与 SQL 的不同group player ( aggregate {...} )按player分组并对组内做聚合total_kills sum kills是命名聚合语法derive kd_ratio total_kills / total_deaths在已有列的基础上派生新列select { player, kd_ratio }用花括号选择输出的列集合管道式书写顺序与人类思考顺序一致先from数据源再逐步filter、group、derive、select而不是 SQL 那种先 SELECT 后 FROM的倒序结构。用 prql 函数即时执行 PRQL除了定义持久化函数PL/PRQL 还提供prql函数允许在普通 SQL 中直接传入 PRQL 字符串即时编译执行。这对需要在 ORM 或既有 SQL 代码中嵌入 PRQL 逻辑的场景非常有用。方式一指定静态返回类型select prql(from matches | filter player Player1) as (id int, match_id int, round int, player text, kills int, deaths int) limit 2; id | match_id | round | player | kills | deaths --------------------------------------------- 1 | 1001 | 1 | Player1 | 4 | 1 3 | 1001 | 2 | Player1 | 1 | 7 (2 rows)这里prql(...)接收一个 PRQL 字符串作为入参字符串中的单引号用 PostgreSQL 的双写规则转义。as (...)子句显式声明结果集的列名与类型这样 PostgreSQL 可以在编译期确定返回结构结果集可直接以普通表的形式消费。方式二返回游标无需静态类型-- Same as above without the need for the static types, but returns cursor select prql(from matches | filter player Player1, player1_cursor); fetch 2 from player1_cursor;第二种调用方式多传一个游标名参数此时prql函数把编译结果写入命名游标并返回游标无需预先声明列类型。之后用标准的fetch命令从游标中取数据。这适合返回结构动态变化、或者调用方希望分批拉取结果的场景。两种方式的取舍很直观方式一类型安全、可直接作为子查询/CTE 使用但要求调用方明确写出全部列结构方式二更灵活省去了类型声明代价是必须通过游标逐批读取。使用场景ORM 中的自定义 SQL官方文档特别指出prql函数对于 ORM 中的自定义 SQL 很有用。当 ORM 的查询构建器表达能力不足、需要手写 SQL 片段时与其手拼字符串易错且难读不如直接内嵌一段 PRQL——既保留了管道语法的可读性又由 PL/PRQL 负责正确的 SQL 编译与转义降低了拼接错误与注入风险。底层原理PRQL 如何编译为 PostgreSQL SQLPL/PRQL 之所以能把 PRQL 函数体变成可执行的 SQL依赖的是本仓库的参考实现编译器prqlc。prqlc是一个用 Rust 编写的、从 PRQL 到 SQL 的编译器同时提供 CLI 与库两种形态详见 prqlc 编译器说明。它可以作为过滤器使用$ echo from employees | filter has_dog | select salary | prqlc compile SELECT salary FROM employees WHERE has_dogPL/PRQL 内部所做的本质上就是拿到 PRQL 字符串 → 调用编译逻辑 → 拿到 SQL 字符串 → 交给 PostgreSQL 执行。指定 PostgreSQL 方言sql.postgresPRQL 允许在查询头部声明目标方言从而让编译器输出特定数据库的 SQL 风味参见 目标方言与版本说明prql target:sql.postgres from employees sort age take 10在 PRQL 编译器内部sql.postgres被解析为Target::Sql(Some(Dialect::Postgres))。仓库的单元测试 lib.rs 中的 target 解析测试 直接验证了这一映射assert_debug_snapshot!(Target::from_str(sql.postgres), Ok( Sql( Some( Postgres, ), ), ) );Dialect::Postgres是方言枚举Dialect的一个变体见 dialect.rs 方言枚举定义并且被标记为Supported受支持级别——这意味着它在可能的情况下支持所有 PRQL 语言特性、每个提交都会测试、缺陷会被修复与sql.duckdb、sql.sqlite、sql.generic、sql.mysql、sql.redshift、sql.clickhouse同属受支持阵营而sql.mssql、sql.ansi、sql.bigquery、sql.snowflake、sql.oracle则处于 Unsupported最小化测试、可能存在特性缺口状态。PostgresDialect 的方言特化实现PRQL 编译器在生成 SQL 时遵循优先通用方言、仅在有充分理由时做方言特化的原则dialect.rs 设计注释。PostgresDialect的实现PostgresDialect 特化实现明确特化了以下几个关键点理解了它们就能明白 PL/PRQL 函数最终会生成什么样的 SQL方言能力PostgreSQL 行为对 PRQL 语法的影响supports_distinct_on返回true支持DISTINCT ONgrouptake 1这类每组取一行的查询可以编译为更高效的DISTINCT ON而非窗口函数对比MySQL 不支持需回退到窗口函数interval_quoting_style返回ValueAndUnitQuoted即INTERVAL 1 day整体加引号时间间隔字面量按 PostgreSQL 风格输出translate_chrono_item将 chrono/strftime 格式串映射到 PostgreSQL 的to_char格式如%Y→YYYY、%m→MM、%b→Mon、RFC3339 →YYYY-MM-DDTHH24:MI:SS.USZPRQL 的date类型to_text等格式化函数能正确翻译成 PostgreSQL 语义supports_zero_columns返回true支持零列如纯聚合、SELECT 1类查询prefers_subquery_parentheses_shorthand返回trueUNION等集合操作中子查询的括号组织更贴近 PostgreSQL 习惯其中translate_chrono_item的实现细节很有意思它把 PRQL 日期格式串逐 token 映射并且对包含字母的文本片段自动加双引号format!(\{literal}\)避免被 PostgreSQL 误解释为格式化模式——这正是PRQL 语法与数据库方言差异被编译器内部消化的一个缩影。在编译器中指定 Postgres 目标如果你不使用 PL/PRQL 扩展而是想独立验证某段 PRQL 编译出的 PostgreSQL SQL可以通过prqlcCLI 的--target参数指定echo prql target:sql.postgres from employees sort age take 10 | prqlc compile需要注意优先级规则target.md 优先级说明编译器的--target参数优先于查询头部的prql target:...声明若想在命令行下尊重查询内部声明的目标需传--target sql.any。安装与后续探索PL/PRQL 扩展的安装与构建说明位于其独立仓库中官方文档指引For installation instructions and more information on the extension, see the PL/PRQL repository一般流程遵循 pgrx 扩展的标准安装方式先安装 PostgreSQL 开发头文件与 Rust 工具链再通过cargo pgrx install或编译产物将扩展装入 PostgreSQL 的extension目录最后在目标数据库中执行CREATE EXTENSION启用。由于该扩展不在本仓库内本文不再展开其构建细节建议以官方仓库的最新 README 为准。如果希望深入了解 PRQL 语言本身与 PostgreSQL 方言的交互本仓库的以下资源值得继续阅读目标方言与版本说明完整列出受支持与不受支持的方言清单及优先级规则prqlc 编译器使用说明CLI 的compile、交互模式与库 API 用法方言特化源码查看PostgresDialect及其他方言的完整特化逻辑目标解析单元测试验证sql.postgres字符串如何被解析为编译器目标去重查询指南其中说明了grouptake 1在 PostgreSQL 方言下可编译为DISTINCT ON的优化路径该功能当前处于 TODO 注释状态实际以仓库最新行为为准。小结PL/PRQL 为 PostgreSQL 开发者提供了两种渐进式的 PRQL 使用方式用plprql语言定义可复用的数据库函数或在 SQL/ORM 中用prql函数即时编译执行。其底层依赖 pgrx 框架完成扩展与 PostgreSQL 的对接并依靠 prqlc 编译器将 PRQL 管道翻译为针对 PostgreSQL 方言优化的 SQL。整个链路中方言差异由编译器的sql.postgres目标统一消化——从DISTINCT ON到日期格式映射——使得上层开发者只需专注撰写清晰的数据管道即可。赞分享后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载相关推荐PRQL方言支持指南如何在PostgreSQL、MySQL、Snowflake等数据库中使用PRQL方言支持指南如何在PostgreSQL、MySQL、Snowflake等数据库中使用 PRQLPipelined Relational Query后端Lattigo加密安全指南理解IND-CPA与CPA-D安全机制的3个关键要点Lattigo加密安全指南理解IND CPA与CPA D安全机制的3个关键要点 Lattigo是一个基于格的多方同态加密Go语言库为开发者提供了强大的加密工密码学PRQL 语言与编译器深度解析基于 prql 仓库的管道式 SQL 替代方案实战指南PRQL 语言与编译器深度解析基于 prql 仓库的管道式 SQL 替代方案实战指南 PRQL P ipelined R elational Q uery后端上一篇如何快速掌握顶级公司的软件测试秘诀howtheytest项目完整指南下一篇FUXA项目中电能消耗数据可视化问题的分析与解决创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表