Daft SQL SELECT 语句实战指南从基础查询到引擎级执行原理【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/DaftDaft 为 AI 与多模态数据场景设计的高性能数据引擎其内置 SQL 方言紧密贴近 DuckDB 与 PostgreSQL见 docs/sql/index.md而SELECT语句正是所有查询的入口。本文以仓库 SQL 参考文档 docs/sql/statements/select.md 为骨架完整覆盖其全部示例并深入daft.sqlPython API、daft-sql规划器源码与tests/sql测试套件帮助你从会写到懂原理掌握 Daft 中 SELECT 的完整用法、执行流程与能力边界。SELECT 语句是什么在 Daft 中SELECT语句用于查询某个 catalog数据目录中的表它同时支持直接求值表达式无需任何表如SELECT 1 1;查询 DataFramedaft.sql()会自动把当前 Python 作用域中的daft.DataFrame变量注册为可查询的表查询外部数据源通过read_parquet、read_csv、read_iceberg等表函数直接读取文件与湖格式从源码看Daft 的 SQL 解析与规划基于sqlparsercrate在 src/daft-sql/src/planner.rs 中实现顶层语句类型定义在 src/daft-sql/src/statement.rs其中Statement::Select(Select)直接承载SELECT ...查询Select本质就是一个LogicalPlanRef——即 SELECT 查询会被翻译成 Daft 的逻辑计划最终与 DataFrame API 走同一条执行链路。运行环境准备三种执行入口在写 SELECT 之前先明确 Daft 提供哪几种执行入口1. 模块级函数daft.sql(sql, ...)定义于 daft/sql/sql.py是使用最频繁的入口import daft df1 daft.from_pydict({a: [1, 2, 3], b: [foo, bar, baz]}) df2 daft.from_pydict({a: [1, 2, 3], c: [daft, None, None]}) # Daft 自动从 Python 全局命名空间识别 df1 和 df2 result_df daft.sql(SELECT * FROM df1 JOIN df2 ON df1.a df2.a) result_df.show()关键参数参数类型默认值说明sqlstr必填要执行的 SQL 查询register_globalsboolTrue是否把调用方作用域中的 DataFrame 变量注册进 catalog。关闭后需通过bindings显式传入否则报错见 tests/sql/test_sql.py 中test_sql_function_register_globals**bindingsDataFrame无额外的 DataFrame 绑定可视为 CTE 表优先级最高可覆盖同名全局变量从实现看daft.sql内部按全局变量 → bindingsCTE的顺序构建表绑定字典py_ctes显式传入的bindings放在最后所以不会被遮蔽对应 daft/sql/sql.py随后调用底层_sql_exec执行并把结果包装成新的DataFrame无缝融入 Daft 的懒执行lazy计划。2. Session 级方法sess.sql(...)如果你使用Session管理 catalog则通过会话执行并可先用create_temp_table注册临时表示例见 docs/sql/index.mdfrom daft import Session sess Session() sess.create_temp_table(T, daft.from_pydict({a: [0, 1]})) sess.create_temp_table(S, daft.from_pydict({b: [1, 0]})) sess.sql(SELECT * FROM T, S).show()关于 Session 与 catalog 连接的详细用法参见 docs/configuration/sessions-usage.md。3. 表达式级函数daft.sql_expr(sql)daft.sql_expr把一段 SQL 表达式直接解析为Expressiondaft/sql/sql.py可嵌入 DataFrame 操作df daft.from_pydict({a: [1, 2, 3], b: [4, 5, 6]}) df df.with_column(c, daft.sql_expr(a b)) # 与 col(a) col(b) 等价 df.show()它也会在部分 DataFrame 操作中被自动调用例如df.where(x 3 AND y 4)里的字符串过滤条件daft/sql/sql.py。基础 SELECT 用法原文档示例全集下面完整列出 docs/sql/statements/select.md 中的全部示例并补充可运行的上下文。求值单个表达式不依赖任何表直接求值常量表达式SELECT 1 1;在 Daft 中执行等价于daft.sql(SELECT 1 1).show()选择全部列从表T中取出所有列与所有行SELECT * FROM T;选择指定列从表T中选出a、b、c三列SELECT a, b, c FROM T;投影列的顺序即输出 DataFrame 的列顺序。在投影中应用标量函数对列a、b分别应用标量函数foo和barSELECT foo(a), bar(b) FROM T;Daft 的 SQL 函数集非常庞大——聚合、字符串、时间、列表、URI、图像处理等函数均可在 SELECT 投影中使用详见下文函数调用一节。计数非空值统计列a非空的行数SELECT COUNT(a) FROM T;注意COUNT(a)只统计a非NULL的行若要统计所有行用COUNT(*)。测试 tests/sql/test_sql.py 的test_sql_count_star同时验证了两种写法。分组计数按列b分组统计每组行数SELECT COUNT(*), b FROM T GROUP BY b;这是最基础的GROUP BY聚合查询其执行路径会进入规划器中的聚合分支见下节源码解析。引擎级原理SELECT 的处理顺序搞清楚 SELECT 的底层处理顺序能帮你避开大量 SQL 陷阱。从 src/daft-sql/src/planner.rs 的plan_query可以看出Daft 严格按照以下顺序规划一条 SELECT 查询CTE 绑定解析WITH子句将公共表表达式注册进PlannerContextplan_ctesFROM / JOIN解析数据来源与连接plan_fromSELECT 投影把投影列表中的每一项翻译为表达式select_item_to_exprWHERE解析过滤谓词并施加plan.filter(filter)GROUP BY解析分组表达式支持表达式分组、ROLLUP、按投影序号分组ORDER BY解析排序键plan_order_by_exprs聚合判定若投影中包含聚合函数或存在 GROUP BY走plan_aggregate_query此时解析HAVING否则走plan_non_agg_queryDISTINCTSELECT DISTINCT或DISTINCT ON (cols)施加plan.distinct(...)LIMIT / OFFSET最后施加分页值得注意的两个细节OFFSET 与 LIMIT 的父子关系规划器中有一条注释明确说明由于 Daft SQL 方言紧跟 DuckDB 与 PostgreSQL当LIMIT与OFFSET同时出现时无论书写顺序如何都会保证 OFFSET 是 LIMIT 的子节点src/daft-sql/src/planner.rs。LIMIT 7 OFFSET 2与OFFSET 2 LIMIT 7语义一致。取值校验LIMIT n与OFFSET n都必须是非负整数常量否则直接报错——测试 tests/sql/test_limit_offset.py 中test_negative_limit断言错误信息LIMIT n must be greater than or equal to 0, instead got: -1test_negative_offset同理。另外OFFSET 必须与 LIMIT 搭配使用单独OFFSET 17会抛出Offset without limit is unsupported now!见test_offset_without_limit。此外规划器在语句级还会对能力边界做前置检查Subqueries are not supportedFROM 中的子查询不被支持、VALUES are not supported、INSERT/UPDATE/DELETE/MERGE均不支持src/daft-sql/src/planner.rs遇到会抛出带^定位符的友好错误。聚合与 GROUP BY 的完整用法原文档给出了COUNT两种形态而 Daft 的 SQL 聚合远不止于此。tests/sql/test_aggs.py 的test_aggs_sql一次验证了十余种聚合函数在 SQL 与 DataFrame API 下结果完全一致SELECT sum(values) as sum, product(values) as product, mean(values) as mean, avg(values) as avg, percentile(values, 0.99) as p99, median(values) as median, min(values) as min, max(values) as max, count(values) as count, count(distinct values) as count_distinct, stddev(values) as std, stddev_pop(values) AS std_pop, stddev_samp(values) AS std_samp, variance(values) AS variance, var(values) AS var, var_samp(values) AS var_samp, var_pop(values) AS var_pop FROM df常用聚合函数速查函数语义COUNT(expr)/COUNT(*)非空计数 / 全行计数COUNT(DISTINCT expr)去重计数SUM/PRODUCT求和 / 求积AVG/MEAN平均值MIN/MAX最小值 / 最大值MEDIAN/PERCENTILE(expr, p)中位数 / 分位数如percentile(values, 0.99)表示 P99STDDEV/STDDEV_POP/STDDEV_SAMP标准差总体 / 样本VARIANCE/VAR_POP/VAR_SAMP方差总体 / 样本HAVING 过滤分组与GROUP BY配合HAVING对聚合后的分组结果过滤测试覆盖在 tests/sql/test_aggs.py 的test_having系列SELECT b, SUM(c) AS total FROM T GROUP BY b HAVING SUM(c) 100;按投影序号分组与 ROLLUP规划器支持按 SELECT 投影中的序号分组即 DuckDB/PostgreSQL 风格的GROUP BY 1, 2对应测试test_group_by_ordinal_1/2/1_2、test_group_by_ordinal_zero_raises序号从 0 开始会报错等同时支持ROLLUP生成小计行如 tests/sql/test_aggs.py 中test_simple_rollup对GROUP BY ROLLUP(dept, year)的输出会额外产生dept/year为None的小计行。函数调用SQL 嵌套等价于 Python 方法链Daft 的 SQL 可调用全部Expression能力。与 Python API 的方法链风格col(a).download().decode_image()不同SQL 中需要改用函数嵌套image_decode(url_download(a))示例见 docs/sql/index.md 的 SQL Functions 一节。df daft.from_pydict({urls: [ https://user-images.githubusercontent.com/17691182/190476440-28f29e87-8e3b-41c4-9c28-e112e595f558.png, # ...更多 URL ]}) # SQL 版本函数嵌套 df daft.sql(SELECT image_decode(url_download(urls)) FROM df) # 等价的 Python 版本方法链 df df.select(daft.col(urls).download().decode_image())两者输出相同的Image[MIXED]类型列。这也意味着你可以在 SQL 中直接完成多模态数据流水线下载 → 解码 → 后续图像处理而不是只做关系型查询。更妙的是表达式级互通daft.sql_expr(A B as C)与(daft.col(A) daft.col(B)).alias(C)打印出来完全一致均为col(A) col(B) as Cdocs/sql/index.md说明 SQL 表达式与 Python 表达式在内部是同一套 DSL可以在一个 Pipeline 里自由混用两种语法。排序与分页ORDER BY / LIMIT / OFFSET虽然原文档未展开但排序与分页是 SELECT 的高频配套子句且 Daft 行为有明确规范测试全量覆盖见 tests/sql/test_limit_offset.py-- 按 id 升序取前 7 行 SELECT name FROM input_df ORDER BY id LIMIT 7; -- 降序 跳过 2 行 取 7 行OFFSET 写在 LIMIT 前后均可 SELECT id, name FROM input_df ORDER BY id DESC OFFSET 2 LIMIT 7; -- 经典分页 SELECT id, name FROM input_df ORDER BY id OFFSET {offset} LIMIT {limit};要点归纳LIMIT/OFFSET必须是 0的整数常量负值直接报错OFFSET不能脱离LIMIT单独使用LIMIT与OFFSET顺序无关规划器统一保证 OFFSET 在 LIMIT 之下可与ORDER BY、WHERE、JOIN自由组合且支持在子查询层叠使用见test_paging、test_offset_limit_with_join顶层ORDER BY需要配合确定性的排序键才能保证分页结果稳定。另外 Daft 还支持SELECT DISTINCT与DISTINCT ON (columns)见规划器 src/daft-sql/src/planner.rs。集合操作UNION / INTERSECT在plan_query的集合操作分支中src/daft-sql/src/planner.rs支持SELECT a FROM T1 UNION ALL SELECT a FROM T2; SELECT a FROM T1 UNION SELECT a FROM T2; -- 隐式去重UNION DISTINCT SELECT a FROM T1 INTERSECT ALL SELECT a FROM T2; -- 保留重复 INTERSECT SELECT a FROM T2; -- 去重其中UNION/UNION DISTINCT默认按位置合并也支持BY NAME变体UNION ALL BY NAME等按列名合并对应测试见 tests/sql/set_ops.py。EXCEPT目前不在支持列表内。用表函数直接读取数据源SELECT 的FROM不仅能接表还能接表函数直接读文件。read_parquet、read_csv、read_json、read_iceberg、read_deltalake的选项完整清单见 docs/sql/index.md 的 Table Function Options 表格。几个实用模式# 路径 命名参数 或 : 均可 daft.sql(SELECT * FROM read_csv(data.csv, has_headers false)) daft.sql(SELECT * FROM read_csv(data.csv, has_headers : false)) # path 也可作为命名参数 daft.sql(SELECT * FROM read_csv(path data.csv)) # 多文件用 SQL 数组 daft.sql(SELECT * FROM read_parquet([a.parquet, b.parquet])) # 显式 schemastruct 字面量 daft.sql(SELECT * FROM read_csv(data.csv, schema : {a: int64, b: string})) # Iceberg 分支读取snapshot_id / branch / tag 互斥 daft.sql(SELECT * FROM read_iceberg(/warehouse/db/t/metadata/v3.metadata.json, branch audit)) # 跳过损坏文件collect 后通过 df.skipped_corrupt_files 查看 df daft.sql(SELECT * FROM read_csv(s3://my-bucket/data/**/*.csv, ignore_corrupt_files true)) df.collect() print(df.skipped_corrupt_files)以read_parquet为例其可用选项包括infer_schema、schema、coerce_int96_timestamp_unit、chunk_size、multithreaded、io_config、file_path_column、hive_partitioning、ignore_corrupt_files与 Python 侧daft.read_parquet一一对应ignore_corrupt_files的深入说明见 docs/connectors/generic-file-source-options.md。窗口函数SELECT 投影中的进阶分析Daft SQL 支持在 SELECT 投影中使用窗口函数语法为function_name([expr]) OVER ( [PARTITION BY expr_list] [ORDER BY order_list] [frame_clause] )支持的函数分三类完整文档见 docs/sql/window_functions.md排名函数ROW_NUMBER()、RANK()并列留空位、DENSE_RANK()并列不留空位偏移函数LAG(value [, offset [, default]])、LEAD(value [, offset [, default]])offset 缺省为 1聚合函数所有聚合函数都可用作窗口函数如SUM/AVG/COUNT/MIN/MAX-- 组内排名 SELECT category, value, ROW_NUMBER() OVER (PARTITION BY category ORDER BY value) AS row_num, RANK() OVER (PARTITION BY category ORDER BY value) AS rank FROM sales; -- 组内累计默认帧UNBOUNDED PRECEDING 到 CURRENT ROW SELECT category, value, SUM(value) OVER (PARTITION BY category ORDER BY value) AS running_sum FROM sales; -- 滑动平均当前行 前 2 行 SELECT date, value, AVG(value) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM time_series;窗口帧支持ROWS模式UNBOUNDED PRECEDING/n PRECEDING/CURRENT ROW/n FOLLOWING/UNBOUNDED FOLLOWINGRANGE模式尚未完全支持同时存在以下限制全局分区无PARTITION BY、WINDOW命名子句、IGNORE/RESPECT NULLS均暂不支持。错误信息与常见坑Daft 的 SQL 解析错误会给出带^定位符的友好提示规划器从sqlparser错误中提取行列号并把错误位置直接标注在原始 SQL 文本上src/daft-sql/src/planner.rs对应测试test_sql_caret_error_eof、test_sql_caret_error_multiline等tests/sql/test_sql.py。实操中最容易踩的坑汇总情况结果LIMIT -1/OFFSET -1报错必须 0单独OFFSET n无 LIMIT报错Offset without limit is unsupportedFROM 中出现子查询报错Subqueries are not supported多条语句SELECT ...; SELECT ...报错多语句不支持见test_sql_multi_statement_sql_error表名与关键字冲突如TABLE需转义处理测试见test_sql_function_table_name_is_keywordGROUP BY 0或超范围序号报错见test_group_by_ordinal_zero_raises关于标识符大小写、引号与命名规范参见 docs/sql/identifiers.md数据类型体系参见 docs/sql/datatypes.md。结语与当前状态综上所述Daft 的 SELECT 语句虽以查询 catalog 中的表为起点实际上已经成为融合关系查询、聚合分析、窗口计算、多模态函数、外部数据源读取与 DataFrame 生态的统一入口。官方在 docs/sql/statements/select.md 中明确标注 SQL Reference 文档仍在完善中Work in Progress源码中daft.sql的 docstring 也提示该功能早期开发中API 可能变动——因此建议以当前仓库版本为准并结合 tests/sql 目录下的测试样例test_sql.py、test_aggs.py、test_limit_offset.py、test_joins.py、test_window.py等验证具体行为。掌握本文的基础示例与规划器执行顺序你就具备了在 Daft 中编写、调试与优化 SELECT 查询的完整能力。【免费下载链接】DaftHigh-performance data engine for AI and multimodal workloads. Process images, audio, video, and structured data at any scale项目地址: https://gitcode.com/GitHub_Trending/da/Daft创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考