尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Apache DataFusion 预编译语句(PREPARE/EXECUTE):从占位符参数到可复用查询的实现原理与实战
大数据数据分析后端【免费下载链接】datafusionApache DataFusion SQL Query Engine项目地址https://gitcode.com/gh_mirrors/datafu/datafusion点击查看免费下载DataFusion 通过PREPARE/EXECUTE语句支持 SQL 预编译先把带$1、$2等占位符的查询编译并保存在会话中之后再以不同参数值反复执行。本文基于官方文档 Prepared Statements 的完整用法展开并结合datafusion源码与 SQL 逻辑测试讲清预编译语句的类型推断、位置参数、执行期的参数校验与类型转换机制帮助你在应用层高效复用高频查询模板。1. 基本概念与语法PREPARE语句允许创建并存储一条带占位符参数的 SQL 语句预编译语句随后可以携带不同参数被高效地重复执行。其语法形如PREPARE 语句名([参数类型, ...]) AS SELECT 语句;官方文档给出的标准示例创建名为greater_than的预编译语句选出所有列a大于给定参数的记录PREPARE greater_than(INT) AS SELECT * FROM example WHERE a $1;创建完成后即可按需传入参数执行EXECUTE greater_than(20);占位符采用$n形式的数字位置参数$1、$2按出现顺序与EXECUTE时传入的参数列表一一对应。需要注意的是占位符编号后必须紧跟数字$foo这类命名占位符不被支持——逻辑测试 prepare.slt 中明确断言了对PREPARE my_plan(INT) AS SELECT ... WHERE age $foo会报错Unknown placeholder: $foo。Rust API 用法在 Rust 侧预编译语句通过SessionContext::sql直接以 SQL 文本下发文档给出的完整示例如下注册 CSV 表后执行PREPARE再用EXECUTE取回 DataFrameuse datafusion::prelude::*; #[tokio::main] async fn main() - datafusion::error::Result() { // 注册表 let ctx SessionContext::new(); ctx.register_csv(example, tests/data/example.csv, CsvReadOptions::new()).await?; // 创建预编译语句 greater_than let prepare_sql PREPARE greater_than(INT) AS SELECT * FROM example WHERE a $1; ctx.sql(prepare_sql).await?; // 执行预编译语句 greater_than let execute_sql EXECUTE greater_than(20); let df ctx.sql(execute_sql).await?; // 执行并打印结果 df.show().await?; Ok(()) }这段代码并非只是示意datafusion/core/src/lib.rs 通过doc_comment::doctest!将 prepared_statements.md 中的 Rust 示例直接注册为 doctest也就是说文档里的每一段 Rust 代码都会被 CI 编译并实际运行保证了示例与当前代码库行为的一致性示例中tests/data/example.csv是相对于datafusion/core目录的路径。2. 参数类型推断如果PREPARE时不显式指定参数类型DataFusion 会在执行时自动推断。文档对应示例PREPARE greater_than AS SELECT * FROM example WHERE a $1; EXECUTE greater_than(20);此时$1的期望类型由所在表达式的类型推导链决定例如与列a的比较运算会给出类型约束。从逻辑测试可以看到无类型声明的用法覆盖面更广不仅限于SELECT ... WHERE还适用于投影和VALUESPREPARE my_plan AS SELECT $1; EXECUTE my_plan(Foo); -- 返回 Foo PREPARE my_plan AS SELECT * FROM person WHERE first_name LIKE $1; EXECUTE my_plan(j%);以上取自 prepare.slt 的测试用例。实践建议能明确写出参数类型时建议显式声明如PREPARE greater_than(INT)因为显式声明会在执行期把传入参数强制转换为声明类型见下文第 4 节行为更可控省略类型声明则依赖推断适合参数语义灵活例如LIKE模式串、通用字符串列的场景。3. 位置参数多参数当语句包含多个参数时使用位置参数按序传递。文档示例PREPARE greater_than(INT, DOUBLE) AS SELECT * FROM example WHERE a $1 AND b $2; EXECUTE greater_than(20, 23.3);对应 Rust 侧只需替换 SQL 文本即可// 创建预编译语句 greater_than let prepare_sql PREPARE greater_than(INT, DOUBLE) AS SELECT * FROM example WHERE a $1 AND b $2; ctx.sql(prepare_sql).await?; // 执行预编译语句 greater_than let execute_sql EXECUTE greater_than(20, 23.3); let df ctx.sql(execute_sql).await?;占位符在语句体内可任意位置、任意多次出现包括IN、ANY/ALL子查询比较、GROUP BY/HAVING、甚至LIMIT子句。例如逻辑测试中验证过-- LIMIT 子句中的参数对应 issue #12294 场景 PREPARE get_N_rand_ints_from_last_run(INT) AS SELECT id FROM test WHERE run_id foo ORDER BY random() LIMIT $1; EXECUTE get_N_rand_ints_from_last_run(2); -- 子查询比较中复用同一参数 PREPARE my_plan AS SELECT id FROM person WHERE $1 IN (SELECT age FROM person); EXECUTE my_plan(20);此外EXECUTE传入的实参不要求必须是字面量测试中EXECUTE my_plan6(10 10)这类非字面量表达式也能正确执行——执行器会先把参数表达式做常量简化见下节。4. 源码剖析EXECUTE 的执行链路预编译语句的执行入口在 datafusion/core/src/execution/context/mod.rs 的execute_prepared方法。从源码可以确认以下关键行为按名称查找语句self.state.read().get_prepared(name)从会话状态中取出已存储的Prepared对象包含逻辑计划与参数字段元数据找不到则报Prepared statement {name} does not exist。参数必须是可简化为字面量的表达式execute_prepared使用ExprSimplifier逐个简化传入的参数表达式只接受Expr::Literal源码注释即 “Only allow literals as parameters for now”无法简化为字面量的表达式会触发Unsupported parameter type错误。这解释了为什么EXECUTE my_plan6(10 10)能工作——10 10先被简化为字面量20。参数个数校验若预编译时声明了参数类型而数量与传入不符报错Prepared statement {name} expects {n} parameters, but {m} provided。按声明类型强制转换当prepared.fields非空时每个实参都会向声明的类型做 cast。这正是文档中PREPARE greater_than(INT)的语义保证——EXECUTE greater_than(20)会把字符串20转为整数 20 参与比较而EXECUTE my_plan6(foo)会报Cast error: Cannot cast string foo to value of Int32 type均有 prepare.slt 中的用例佐证。另外PREPARE语句在逻辑计划层面会生成Prepare节点。逻辑计划构建入口在 datafusion/expr/src/logical_plan/builder.rs 的prepare方法而优化器同样会作用于预编译语句——逻辑测试验证了OptimizeProjections规则会对PREPARE my_plan(INT) AS SELECT id $1 FROM person把投影下推01)Prepare: my_plan [Int32] 02)--Projection: person.id $1 03)----TableScan: person projection[id]也就是说预编译语句享受与直接执行 SQL 相同的优化器流水线占位符参数会作为表达式保留在投影中参与类型推导。5. 生命周期管理DEALLOCATE 与重名处理预编译语句的生命周期与所在会话绑定可用DEALLOCATE显式释放也可写作DEALLOCATE PREPARE namePREPARE my_plan(STRING, STRING) AS SELECT * FROM (VALUES(1, $1), (2, $2)) AS t (num, letter); EXECUTE my_plan(Foo, Bar); DEALLOCATE my_plan; -- 释放后 EXECUTE 将报 does not exist EXECUTE my_plan(Foo, Bar); -- error: Prepared statement my_plan does not existprepare.slt 完整覆盖了这一生命周期语义重名冲突对已存在的名称再次PREPARE会报Prepared statement my_plan already existsDEALLOCATE释放后可以立即用同名重新创建EXECUTE必须带名称EXEC()、EXECUTE()、EXEC(any-string)等无名称写法统一报EXECUTE statement requires a name而不是 panicEXECUTE可以不带参数列表对无参语句EXECUTE my_plan2合法对声明了 1 个参数的语句省略参数则报expects 1 parameters, but 0 provided。6. 常见错误速查综合官方文档语义与逻辑测试断言预编译语句相关的典型报错及含义如下报错信息触发场景SQL error: ParserErrorPREPARE后缺少名称如PREPARE AS SELECT ...或占位符出现在不支持的位置如age IS $1Unknown placeholder: $foo使用了非数字编号的占位符Prepare specifies 1 data types but query has 2 parameters声明的参数类型个数与语句中出现的$n个数不一致多声明或少声明均会报错Prepared statement {name} does not existEXECUTE/DEALLOCATE未创建的或已释放的语句Prepared statement {name} already exists重复创建同名语句Prepared statement {name} expects N parameters, but M providedEXECUTE实参个数不匹配Cast error: Cannot cast ...实参值无法转换为PREPARE时声明的参数类型一个容易被忽略的边界如果PREPARE时声明了类型但语句体里根本没有参数如PREPARE my_plan(INT) AS SELECT id, age FROM person WHERE age 10同样会报Prepare specifies 1 data types but query has 0 parameters——类型声明必须与语句体内实际使用的占位符严格对应。7. 用 EXPLAIN 观察预编译计划DataFusion 支持对预编译语句本身做EXPLAIN便于验证占位符处的类型推导结果-- PREPARE 阶段的逻辑计划占位符处显示为 $1 EXPLAIN PREPARE my_plan(INT, INT) AS SELECT $1 AS one, $2 AS two; -- logical_plan -- 01)Prepare: my_plan [Int32, Int32] -- 02)--Projection: $1 AS one, $2 AS two -- 03)----EmptyRelation: rows1 -- EXECUTE 阶段的逻辑计划显示代入后的具体参数值 EXPLAIN EXECUTE my_plan(10*2 1, Foo); -- logical_plan Execute: my_plan params[Int64(21), Utf8(Foo)]可以看出PREPARE的EXPLAIN展示的是带类型标注的模板计划而EXECUTE的EXPLAIN会展示参数表达式简化后的具体标量值且EXPLAIN PREPARE不产生副作用不会真正占用语句名称可重复执行。8. 小结DataFusion 的预编译语句提供了一套完整的PREPARE → EXECUTE → DEALLOCATE工作流PREPARE以$n位置占位符存储查询模板并可选声明参数类型EXECUTE以字面量参数反复执行并在执行期做类型推断与强制转换DEALLOCATE释放会话级资源。从源码看execute_prepared参数必须先可简化为字面量且会按声明类型做 cast 与个数校验从 prepare.slt 看LIKE、IN/ANY/ALL子查询、HAVING、LIMIT等场景以及各类错误路径都有测试保障。对于需要以不同参数高频复用同一查询模板的应用例如参数化报表、API 后端的查询封装这一机制配合显式参数类型声明可以获得既灵活又类型安全的 SQL 使用方式。赞分享大数据数据分析后端【免费下载链接】datafusionApache DataFusion SQL Query Engine项目地址https://gitcode.com/gh_mirrors/datafu/datafusion点击查看免费下载相关推荐Presto PREPARE 语句详解预编译 SQL、参数占位符与配套命令实战Presto PREPARE 语句详解预编译 SQL、参数占位符与配套命令实战 Presto 的 PREPARE 语句用于在当前会话中按名称预编译一条 SQL大数据数据库后端drizzle-orm-pg 0.12.0-beta.40预编译语句、占位符与查询构建器 execute 支持全解析drizzle orm pg 0.12.0 beta.40预编译语句、占位符与查询构建器 execute 支持全解析 drizzle orm pg 0.12.后端数据库ORMlibpqxx 预编译语句Prepared Statements实战指南从 prepare 到 exec_prepared 的完整用法与底层原理libpqxx 预编译语句Prepared Statements实战指南从 prepare 到 exec_prepared 的完整用法与底层原理 预编译语网络安全密码学上一篇Riot.js 中集成 tsParticles 粒子动画riot-particles-demo 的启动、测试与构建实战指南下一篇从30秒到3秒Easy Dataset启动速度优化全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

Android-ObservableScrollView Samples 示例工程构建与运行指南:从 Gradle 命令行到 IDE 的完整实践

Android-ObservableScrollView Samples 示例工程构建与运行指南:从 Gradle 命令行到 IDE 的完整实践

移动开发UI组件 【免费下载链接】Android-ObservableScrollView Android library to observe scroll events on scrollable views. 项目地址: https://gitcode.com/gh_mirrors/an/Android-ObservableScrollView 点击查看 免费下载 本指南以仓库中 samples/README.m…

📅 2026/9/25 9:31:24
Atlas 300V 24G上跑YOLO:从CANN环境到MindIE推理部署实战

Atlas 300V 24G上跑YOLO:从CANN环境到MindIE推理部署实战

我拿到Atlas 300V 24G这块卡的第一天,差点把它当成一块长得比较另类的GPU显卡。直到在服务器上敲下npu-smi info,看到设备类型那一栏写的不是NVIDIA也不是AMD,才反应过来——这是一张货真价实的AI推理加速卡,但它的核心不是流处理…

📅 2026/9/25 9:26:24
Atlas 300V 24G推理卡部署YOLO全流程解析

Atlas 300V 24G推理卡部署YOLO全流程解析

我自己的第一台Atlas推理卡是Atlas 300V 24G,当时收到板卡的第一反应和大多数人一样:24G显存,那不得当成低配版训练卡用?结果一查资料、一上手,完全不是那回事。这块卡不是用来训模型的,它是专门干推理的&a…

📅 2026/9/25 9:26:24
MORE NEWS

更多资讯

📰

Open-Code-Review:开源可审计的AI代码审查CLI范式

1. 这不是又一个“AI代码审查工具”,而是一套可落地、可审计、可嵌入工作流的开源协作范式“open-code-review”这五个字母组合,乍看像某个GitHub仓库名,实则指向一个正在悄然重塑团队协作底层逻辑的实践体系——它不依赖黑盒模型调用&#x…

📰

目标识别视频素材库搭建全复盘:从素材荒到标准化标注

做目标识别相关工作的人,应该都有过同一种体验:模型结构改了一堆,训练脚本跑了几轮,最后发现卡你的不是网络,不是算力,而是素材。通用的图片数据集好找,但能直接扔进训练管线、评估脚本、项目演…

📰

从0.1.0到0.2.0:is-png-cj版本演进与路线图,附新手开源参与贡献指南

从0.1.0到0.2.0:is-png-cj版本演进与路线图,附新手开源参与贡献指南 【免费下载链接】is-png-cj 一个判断图片格式的库,根据图片的文件数据,判断图片是否为png格式 项目地址: https://gitcode.com/Cangjie-TPC/is-png-cj i…

📰

昇腾Atlas 300V部署YOLO全流程:从环境配置到性能调优

前阵子后台收到不少搜过来的朋友,问的都是同一类问题:“atlas 300v 24g 是运算加速卡吗”“atlas部署yolo能不能跑”“跑一个yolov5大概什么帧率”。我猜大多数人跟我当初一样,是刚拿到一台配了昇腾Atlas的计算设备,或者连硬件都还…

📰

如何快速上手See-through:从环境搭建到第一张动漫分层PSD的5步教程(附避坑清单)

如何快速上手See-through:从环境搭建到第一张动漫分层PSD的5步教程(附避坑清单) 【免费下载链接】see-through "Single-image Layer Decomposition for Anime Characters" (SIGGRAPH 2026 Conference Paper) 项目地址: https://g…

📰

Oracle ODAC Xcopy 免安装部署:ODP.NET 连接与避坑指南

简介:ODAC1120320Xcopy_x64是一款面向64位系统的Oracle Data Access Components 11.2.0.3.20版本,核心用途是打通SQL Server与Oracle之间的数据通道,通过Oracle Provider for OLE DB驱动在SQL Server中创建链接服务器,实现跨数据库…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬