news 2026/9/10 4:58:18

Polars SQL SELECT 语句实战指南:查询、聚合、排序、JOIN 与表函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Polars SQL SELECT 语句实战指南:查询、聚合、排序、JOIN 与表函数

Polars SQL SELECT 语句实战指南:查询、聚合、排序、JOIN 与表函数

【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars

Polars 是使用 Rust 编写的高性能 DataFrame 查询引擎,其内置的 SQL 接口(polars-sqlcrate)允许开发者直接在SQLContext上执行标准 SQL 语句并把结果转换为DataFrame/LazyFrame。本文以官方文档 docs/source/user-guide/sql/select.md 为骨架,结合仓库源码,系统讲解 Polars SQL 中SELECT的完整用法:基础投影、GROUP BY聚合、ORDER BY排序、多表JOIN、SQL 内置函数与read_csv/read_parquet等表函数。读完本文,你将能够在 Polars 项目中用一条 SQL 完成原本需要多行 DataFrame 表达式才能实现的取数、聚合与多源读取。

1. 从 DataFrame 到 SQLContext:SELECT 的入口

在 Polars SQL 中,SELECT语句用于从已注册的表(本质上是LazyFrame)中检索数据。其基础语法与标准 SQL 一致:

SELECT column1, column2, ... FROM table_name;

其中column1, column2, ...是你想选取的列,也可以用通配符*选取全部列;table_name是注册在SQLContext中的表名。

要在 SQL 中查询一个 Python 侧的DataFrame,需要先把它注册进SQLContext。官方示例(见 docs/source/src/python/user-guide/sql/select.py 的df片段)如下:

import polars as pl df = pl.DataFrame( { "country": ["USA", "USA", "USA", "USA", "USA", "Netherlands"], "city": [ "New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Amsterdam", ], "population": [8399000, 3997000, 2705000, 2320000, 1680000, 900000], } ) ctx = pl.SQLContext(population=df, eager=True) print(ctx.execute("SELECT * FROM population"))

这里的关键点在于:

  • pl.SQLContext(population=df, eager=True)以关键字参数形式把DataFrame df注册为名为population的表;
  • eager=True表示execute()返回的查询结果是一个急切求值的DataFrame(而非惰性的LazyFrame),便于直接打印查看。

从源码看,SQLContextpolars-sqlcrate 的核心入口(crates/polars-sql/src/context.rs)。它内部维护了一张table_map: Arc<RwLock<PlHashMap<String, LazyFrame>>>,把表名映射到LazyFrame——也就是说,注册进 SQL 的 DataFrame 在底层是以惰性框架形式被引用的。Rust 侧的等价操作是:

use polars_sql::SQLContext; use polars_core::prelude::*; use polars_lazy::prelude::*; let mut ctx = SQLContext::new(); let df = df! { "a" => [1, 2, 3], }.unwrap(); ctx.register("df", df.clone().lazy()); let sql_df = ctx.execute("SELECT * FROM df").unwrap().collect().unwrap(); assert!(sql_df.equals(&df));

(该示例即源码中registerexecute的 doc 测试,见 crates/polars-sql/src/context.rs。)

1.1 注册 / 注销表

SQLContext在 Rust 侧暴露了完整的表管理接口:

  • ctx.register(name, lf):将LazyFrame以指定名称注册为表(context.rs 中register实现为向table_map插入一条映射);
  • ctx.register_many(...):一次注册多张表(官方 SQL 示例中注册income表即使用该方法);
  • ctx.unregister(name):按名称移除表;
  • ctx.get_tables():返回当前已注册表名的排序列表。

Python 侧在 py-polars/src/polars/_plr.pyi 中对应暴露了SQLContext.execute(query) -> PyLazyFrameSQLContext.register(name, lf)等接口。

1.2 惰性还是急切:execute 的底层行为

execute()(crates/polars-sql/src/context.rs)会先解析单条 SQL 语句(parse_single_statement),然后通过can_defer判断是否可以延迟到 DSL → IR 转换阶段再解析。从源码注释看,只要查询是只读的且不涉及用户自定义函数注册,就会走defer_query路径,构建一个DslPlan::SQL惰性计划;只有查询引用了已注册关系或需要用户自定义函数时才会急切执行。这意味着 Polars SQL 的SELECT查询默认可以无缝参与惰性查询优化(谓词下推、投影下推等),与 DataFrame API 的延迟计算模型一致。

2. 分组聚合:GROUP BY

GROUP BY用于按一个或多个列对表中的行分组,并在每个组上计算聚合函数。官方示例(select.pygroup_by片段):

result = ctx.execute( """ SELECT country, AVG(population) as avg_population FROM population GROUP BY country """ ) print(result)

输出结果形如:

shape: (2, 2) ┌─────────────┬────────────────┐ │ country ┆ avg_population │ ╞═════════════╪════════════════╡ │ USA ┆ 3819800.0 │ │ Netherlands ┆ 900000.0 │ └─────────────┴────────────────┘

要点解析:

  • AVG(population) as avg_population为聚合结果列起了别名avg_population
  • GROUP BY country后,未出现在GROUP BY中的普通列不能再直接SELECT(标准 SQL 语义),只能选择分组列与聚合表达式;
  • Polars SQL 支持的聚合函数包括SUMAVGMINMAXCOUNTSTDDEVFIRST等(详见第 4 节)。相关聚合实现与测试可参考 crates/polars-sql/tests/functions_aggregate.rs 与 crates/polars-sql/tests/functions_cumulative.rs。

3. 结果排序:ORDER BY

ORDER BY用于按一个或多个列对查询结果进行升序(ASC,默认)或降序(DESC)排序。官方示例(select.pyorderby片段):

result = ctx.execute( """ SELECT city, population FROM population ORDER BY population """ ) print(result)

输出为按population升序排列的城市人口列表:

shape: (6, 2) ┌─────────────┬────────────┐ │ city ┆ population │ ╞═════════════╪════════════╡ │ Amsterdam ┆ 900000 │ │ Phoenix ┆ 1680000 │ │ Houston ┆ 2320000 │ │ Chicago ┆ 2705000 │ │ Los Angeles ┆ 3997000 │ │ New York ┆ 8399000 │ └─────────────┴────────────┘

也可以指定多列与方向,例如ORDER BY country ASC, population DESC。Polars SQL 的ORDER BY通过sql_expr模块中的order_by_sort_options转换为排序表达式(crates/polars-sql/src/sql_expr.rs),最终映射到底层LazyFramesort逻辑。

4. 多表关联:JOIN

SELECT可以与JOIN组合,把多张注册表按关联键合并。官方示例(select.pyjoin片段)先注册第二张表income,再进行左连接:

income = pl.DataFrame( { "country": [ "USA", "USA", "USA", "USA", "Netherlands", "Netherlands", "Netherlands", ], "city": [ "New York", "Los Angeles", "Chicago", "Houston", "Amsterdam", "Rotterdam", "Utrecht", ], "income": [55000, 62000, 48000, 52000, 42000, 38000, 41000], } ) ctx.register_many(income=income) result = ctx.execute( """ SELECT income.*, population.population FROM population LEFT JOIN income ON population.city = income.city """ ) print(result)

输出结果:

shape: (6, 4) ┌─────────────┬────────────┬──────────────┬────────────┐ │ country ┆ city ┆ income ┆ population│ ╞═════════════╪════════════╪══════════════╪════════════╡ │ USA ┆ New York ┆ 55000 ┆ 8399000 │ │ USA ┆ Los Angeles┆ 62000 ┆ 3997000 │ │ USA ┆ Chicago ┆ 48000 ┆ 2705000 │ │ USA ┆ Houston ┆ 52000 ┆ 2320000 │ │ USA ┆ Phoenix ┆ null ┆ 1680000 │ │ Netherlands ┆ Amsterdam ┆ 42000 ┆ 900000 │ └─────────────┴────────────┴──────────────┴────────────┘

要点解析:

  • SELECT income.*, population.populationincome.*是限定通配符,只展开income表的所有列;再显式补上population表的population列;
  • LEFT JOIN ... ON population.city = income.city:以population为左表按city关联。Phoenixincome中没有对应行,因此income侧出现null
  • ctx.register_many(income=income)一次注册多张表;
  • Polars SQL 还支持INNERRIGHTFULL等连接类型(见 crates/polars-sql/src/keywords.rs 中的JOINLEFTRIGHTFULLINNEROUTERSEMIANTI等关键字)。连接相关实现位于 crates/polars-sql/src/context.rs(JoinCoalesceMaintainOrderJoin等),框架层支持semi_anti_joinfeature 等高级连接。

从源码结构看,多表查询中若多个表存在同名列,Polars SQL 提供了限定通配符消歧机制(disambiguate_projection_cols函数,crates/polars-sql/src/context.rs):当tbl1.*tbl2.*展开后列名冲突时,会用列名:表名的后缀形式避免歧义。

5. SQL 内置函数

Polars SQL 提供了种类丰富的内置函数,官方文档列举了以下几大类(完整清单见源码 crates/polars-sql/src/functions.rs 与 crates/polars-sql/src/keywords.rs):

类别函数示例
数学函数ABSEXPLOGASINACOSATAN
字符串函数LOWERUPPERLTRIMRTRIMSTARTS_WITHENDS_WITH
聚合函数SUMAVGMINMAXCOUNTSTDDEVFIRST
数组函数EXPLODEUNNESTARRAY_SUMARRAY_REVERSE

从源码可以看到这些函数在PolarsSQLFunctions枚举中的定义与 SQL 文档注释,例如:

  • 字符串前缀匹配:SELECT STARTS_WITH(col1, 'a') FROM df;StartsWith变体,functions.rs);
  • 字符串后缀匹配:SELECT ENDS_WITH(col1, 'a') FROM df;EndsWith变体);
  • 数组求和:SELECT ARRAY_SUM(col1) FROM df;ArraySum变体);
  • 数组逆序:SELECT ARRAY_REVERSE(col1) FROM df;ArrayReverse变体);
  • 展开数组为多行:SELECT UNNEST(col1) FROM df;EXPLODEExplode变体,底层实现为e.explode(ExplodeOptions { empty_as_null: true, keep_nulls: true, .. }))。

官方示例(select.pyfunctions片段)演示了在WHERE中使用字符串函数:

result = ctx.execute( """ SELECT city, population FROM population WHERE STARTS_WITH(country,'U') """ ) print(result)

输出只保留countryU开头的城市(即全部美国城市):

shape: (5, 2) ┌─────────────┬────────────┐ │ city ┆ population │ ╞═════════════╪════════════╡ │ New York ┆ 8399000 │ │ Los Angeles ┆ 3997000 │ │ Chicago ┆ 2705000 │ │ Houston ┆ 2320000 │ │ Phoenix ┆ 1680000 │ └─────────────┴────────────┘

注意:STARTS_WITH/ENDS_WITH也常用于WHERE谓词,实现位于PolarsSQLFunctionsStartsWith/EndsWith变体(functions.rs)。

6. 表函数:直接从文件读取数据

前面所有示例都先把 DataFrame 注册进SQLContext再查询。Polars SQL 还支持在查询中直接读取 CSV、Parquet、JSON 与 IPC 文件,无需预先注册——这就是表函数read_xxx(官方文档中称为 Table Functions)。

官方示例(select.pytablefunctions片段):

result = ctx.execute( """ SELECT * FROM read_csv('docs/assets/data/iris.csv') """ ) print(result)

这会把docs/assets/data/iris.csv直接读成一张表并执行SELECT *

从源码看,表函数定义于 crates/polars-sql/src/table_functions.rs 的PolarsTableFunctions枚举,目前支持:

SQL 表函数底层读取器说明
read_csv('path')LazyCsvReader读取 CSV,自动启用try_parse_datesmissing_is_null(table_functions.rs)
read_parquet('path')LazyFrame::scan_parquet读取 Parquet
read_ipc('path')LazyFrame::scan_ipc读取 IPC(Arrow 文件)
read_json('path')LazyJsonLineReader读取 JSON,目前仅支持 NDJSON(JSON Lines)

实现要点(crates/polars-sql/src/table_functions.rs):

  • 每个表函数只接受一个参数:单引号包裹的文件路径字符串。传入其他形式会报SQLSyntax错误(源码中使用polars_ensure!(args.len() == 1, ...)校验,且路径必须来自SQLValue::SingleQuotedString);
  • 返回值为(PlRefPath, LazyFrame),即文件路径与惰性读取框架,因此FROM read_xxx(...)同样享受惰性扫描优化;
  • 读取 CSV 时默认开启try_parse_dates(true)(尝试解析日期列)与missing_is_null(true)(缺失值视为 null)。

因此你可以写出跨文件源的 SQL,例如:

SELECT * FROM read_csv('data/sales.csv') WHERE STARTS_WITH(region, 'EU'); SELECT * FROM read_parquet('data/orders.parquet') ORDER BY order_date DESC;

7. 更多 SELECT 变体与深入学习路径

SELECT的常见变体远不止本文所述,Polars SQL 还支持SELECT DISTINCTWHERE过滤、HAVINGLIMIT/OFFSETCASE WHENUNION/INTERSECT/EXCEPT集合运算、WITH(CTE)、窗口函数(OVER)、* EXCLUDE/RENAME/REPLACE修饰符等(相关关键字见 crates/polars-sql/src/keywords.rs,测试覆盖见 crates/polars-sql/tests/statements.rs 与 crates/polars-sql/tests/simple_exprs.rs)。

如需继续深入,可参考仓库中的以下资源:

  • 官方 SQL 用户指南目录:docs/source/user-guide/sql(含select.mdgroup_by.mdjoins.md等专题);
  • SQL 完整函数/关键字清单:crates/polars-sql/src/keywords.rs(all_keywords()all_functions());
  • 函数实现与 SQL 语法示例:crates/polars-sql/src/functions.rs;
  • 表函数实现:crates/polars-sql/src/table_functions.rs;
  • 核心执行上下文:crates/polars-sql/src/context.rs;
  • 测试套件:crates/polars-sql/tests(覆盖聚合、字符串、数学、窗口、累计、UDF、空表函数等场景)。

8. 小结

Polars SQL 的SELECT完整覆盖了关系查询的核心能力:

  1. 基础投影SELECT col1, col2 / *+FROM,通过SQLContext注册表(register/register_many)即可查询;
  2. 分组聚合GROUP BY+SUM/AVG/MIN/MAX/COUNT/STDDEV/FIRST等聚合函数;
  3. 排序ORDER BY支持多列与ASC/DESC方向;
  4. 多表连接LEFT/RIGHT/INNER/FULL JOIN+ON,支持表名.*限定通配符与自动列名消歧;
  5. 内置函数:数学、字符串、聚合、数组四类函数可直接在SELECT/WHERE中使用;
  6. 表函数read_csv/read_parquet/read_ipc/read_json让你免注册直接查询文件,且默认惰性扫描、可参与查询优化。

所有能力在底层都映射为LazyFrame与 Polars 表达式,因此 SQL 写出的查询与 DataFrame API 构建的查询共享同一套优化与执行引擎——这正是 Polars SQL 既能带来 SQL 的声明式便利、又不牺牲性能的原因。

【免费下载链接】polarsExtremely fast Query Engine for DataFrames, written in Rust项目地址: https://gitcode.com/GitHub_Trending/po/polars

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 4:57:48

昇腾CANN/GE数据转储模块设计

Dump Module Overall Design Document 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对…

作者头像 李华
网站建设 2026/9/10 4:57:18

ARM Cortex-M4边缘AI静态审计:ML-KWS-for-MCU代码健壮性深度解析

1. 项目概述&#xff1a;为什么一个KWS小模型的静态代码审计值得花三天时间深挖&#xff1f;ARM架构正在从手机芯片悄悄接管工业现场、智能终端和边缘网关——不是靠算力碾压&#xff0c;而是靠能效比、确定性调度和裸金属控制能力。最近在给某国产语音模组做预研时&#xff0c…

作者头像 李华
网站建设 2026/9/10 4:56:32

智慧显示终端存储升级:全志T507适配长江存储EC150实战

上个月在客户现场处理一台智慧广告机的数据异常问题&#xff0c;设备每隔几天就会出现一次文件系统损坏。排查到最后&#xff0c;问题出在机器里的TF卡上——频繁断电写入把卡上的FTL表搞乱了。这种场景我见过太多次了&#xff1a;很多做智慧显示终端的团队&#xff0c;一开始都…

作者头像 李华
网站建设 2026/9/10 4:52:20

Keploy 快速上手指南:5分钟为 API 与集成测试自动生成用例

Keploy 快速上手指南&#xff1a;5分钟为 API 与集成测试自动生成用例 【免费下载链接】keploy Open-source platform for creating safe, isolated production sandboxes for API, integration, and E2E testing. 项目地址: https://gitcode.com/GitHub_Trending/ke/keploy …

作者头像 李华
网站建设 2026/9/10 4:51:42

CANN/ge静态执行器特性分析

GE Static Executor (Known Shape Executor) Feature Analysis 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少…

作者头像 李华