ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PRQL 标准库 date 模块完全指南:to_text、now、trunc 与 diff 的跨数据库日期处理实践

PRQL 标准库 date 模块完全指南:to_text、now、trunc 与 diff 的跨数据库日期处理实践 后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载PRQLPipelined Relational Query Language是一种面向数据转换的现代语言其标准库中的date模块为日期与时间处理提供了to_text、now、trunc、diff四个核心函数。本指南以官方文档 web/book/src/reference/stdlib/date.md 为骨架结合仓库源码与集成测试系统讲解每个函数的用法、各数据库方言的 SQL 编译差异、完整格式说明符对照以及跨方言移植时必须注意的兼容性边界。读完本文你将能够在 BigQuery、Clickhouse、DuckDB、MySQL、MSSQL、Postgres、SQLite、Snowflake 等目标数据库上正确使用 PRQL 的日期函数并理解它们编译为 SQL 的底层机制。date 模块概览标准库中的四个日期函数date模块定义在标准库源码 prqlc/prqlc/src/semantic/std.prql 中共包含四个函数module date { # Returns the current date and time as a timestamp. let now - timestamp internal std.date.now let to_text formattext column - text internal std.date.to_text let diff noresolve.unit start end - int internal std.date.diff let trunc unittext column - internal std.date.trunc }从签名可以读出三个关键设计now是无参数函数返回timestamp类型to_text的第一个参数formattext是字符串字面量格式模板第二个参数是要格式化的列diff的单位参数unit被声明为noresolve.unit即以裸关键字形式传入如day这与trunc将单位写成字符串字面量如day形成鲜明对比trunc的单位是unittext必须为字符串字面量。这些函数的主体在std.prql中只是标记为internal std.date.*的内部占位真正实现位于 SQL 方言标准库 prqlc/prqlc/src/sql/std.sql.prql 中。由于不同 RDBMS 的日期函数差异巨大PRQL 采用通用实现 各方言覆盖的策略通用模块提供 ANSI 兼容的默认实现各数据库方言再在module date中覆盖对应函数。to_text把日期转换为文本to_text将一个日期或时间戳转换为文本。由于日期的表示形式多种多样它需要一个format参数来描述日期/时间戳的结构格式使用 日期与时间格式说明符 一节中的说明符。[!NOTE] 由于所有 RDBMS 格式化日期和时间的方式各不相同PRQL要求显式指定目标方言dialect才能编译to_text。目前受支持的数据库包括BigQuery、Clickhouse、DuckDB、MySQL、MSSQL 和 Postgres。以下是官方文档给出的三种方言示例prql target:sql.duckdb from invoices select (invoice_date | date.to_text %d/%m/%Y)prql target:sql.postgres from invoices select (invoice_date | date.to_text %d/%m/%Y)prql target:sql.mysql from invoices select (invoice_date | date.to_text %d/%m/%Y)相同的 PRQL 表达式在不同方言下会编译为各自的原生格式化函数这可以从 prqlc/prqlc/src/sql/std.sql.prql 中各方言module date的实现看出方言编译后的 SQL 函数源码位置BigQueryFORMAT_TIMESTAMP({format}, CAST({column} AS TIMESTAMP))std.sql.prql#L238-L244ClickhouseformatDateTimeInJodaSyntax({column}, {format})std.sql.prql#L258-L265DuckDBstrftime({column}, {format})std.sql.prql#L288-L293MSSQLFORMAT({column}, {format})std.sql.prql#L321-L324MySQLDATE_FORMAT({column}, {format})std.sql.prql#L340-L349PostgresTO_CHAR({column}, {format})std.sql.prql#L387-L392格式字符串的编译机制to_text的关键实现位于 prqlc/prqlc/src/sql/gen_expr.rs 的process_date_to_text函数中。该函数只接受字符串字面量作为格式参数——如果格式是列引用或任意表达式编译期会直接报错std.date.to_text only supports a string literal as format其处理流程是从参数中提取字符串字面量format调用ctx.dialect.translate_prql_date_format(format)把 PRQLchrono 风格格式转换为目标方言的格式语法生成方言对应的格式化函数调用并继续走常规的运算符翻译。格式转换的底层实现在 prqlc/prqlc/src/sql/dialect.rstranslate_prql_date_format使用 Rustchrono库的StrftimeItems逐项解析 PRQL 格式串再调用各方言的translate_chrono_item做逐项映射。由此可以推断to_text的格式语言本质上是chronostrftime 格式的一个子集。真实运行结果验证仓库集成测试 prqlc/prqlc/tests/integration/queries/date_to_text.prql 在 MSSQL 上运行了 12 种格式组合其快照 prqlc/prqlc/tests/integration/snapshots/integration__queries__results__date_to_text.snap 展示了真实输出from invoices take 20 select { d1 (invoice_date | date.to_text %Y/%m/%d), d2 (invoice_date | date.to_text %F), d3 (invoice_date | date.to_text %D), d4 (invoice_date | date.to_text %H:%M:%S.%f), d5 (invoice_date | date.to_text %r), d6 (invoice_date | date.to_text %A %B %-d %Y), d7 (invoice_date | date.to_text %a, %-d %b %Y at %I:%M:%S %p), d8 (invoice_date | date.to_text %), d9 (invoice_date | date.to_text %-d/%-m/%y), d10 (invoice_date | date.to_text %-Hh %Mmin), d11 (invoice_date | date.to_text %M%S\), d12 (invoice_date | date.to_text 100%% in %d days), }对应的部分输出以invoice_date 2009/01/01为例2009/01/01 | 2009-01-01 | 01/01/09 | 00:00:00.000000 | 12:00:00 AM Thursday January 1 2009 | Thu, 1 Jan 2009 at 12:00:00 AM 2009-01-01T00:00:00.000000Z | 1/1/09 | 0h 00min | 0000 | 100% in 01 days注意两点该测试文件头部带有# generic:skip和# sqlite:skip标记说明to_text不支持通用generic方言和 SQLite 方言——这与必须显式指定支持的方言的约束一致d11中单引号、双引号与%S、%M混合使用是合法的说明格式串可以自由混入字面量字符d12演示了转义%%输出一个字面百分号。now获取当前日期时间now返回当前日期和时间类型为时间戳timestamp。它在语义上等价于各数据库的当前时间函数常用作过滤条件例如from test_tables filter test_time date.now由于没有显式指定prql target:这段查询会按通用generic方言编译。now在不同方言下的 SQL 输出并不相同官方文档给出了对照表方言SQL 输出GenericCURRENT_TIMESTAMPMySQLNOW()BigQueryCURRENT_TIMESTAMP()Clickhousenow()其他方言CURRENT_TIMESTAMP对照 prqlc/prqlc/src/sql/std.sql.prql 源码可以看到差异的成因通用模块中let now - sCURRENT_TIMESTAMP并注释说明CURRENT_TIMESTAMP 是 ANSI SQL 标准方言可以覆盖std.sql.prql#L143-L144BigQuery 覆盖为CURRENT_TIMESTAMP()注释明确指出BigQuery 要求CURRENT_TIMESTAMP带括号std.sql.prql#L242-L243Clickhouse 覆盖为now()std.sql.prql#L264MySQL 覆盖为NOW()std.sql.prql#L345-L346。trunc按单位截断日期trunc将日期或时间戳截断到指定单位。与date.diff不同trunc的单位必须写成字符串字面量如day同时单位必须在编译期已知因此把列或任意表达式放在单位位置会直接报错。可接受的单位集合取决于目标数据库。prql target:sql.postgres from events select (event_time | date.trunc day)大多数方言把单位作为带引号的第一个参数但 MSSQL 和 BigQuery 各自采用自己的形式。官方文档给出的对照表方言SQL 输出GenericDATE_TRUNC(day, event_time)DuckDBDATE_TRUNC(day, event_time)PostgresDATE_TRUNC(day, event_time)MySQLDATE_TRUNC(day, event_time)ClickhouseDATE_TRUNC(day, event_time)SnowflakeDATE_TRUNC(day, event_time)MSSQLDATETRUNC(day, event_time)BigQueryDATE_TRUNC(event_time, DAY)通用实现位于 std.sql.prql#L154let trunc unit column - sDATE_TRUNC({unit:0}, {column:0})。MySQL 在 std.sql.prql#L348 同样实现了DATE_TRUNC其注释链接到 MySQL 8.4 的 date-trunc 文档而 MSSQL、BigQuery、Snowflake 的特殊形态目前由编译器的方言能力处理。[!NOTE] SQLite 没有任何日期截断函数因此针对 SQLite 方言编译date.trunc时会直接报错。这在 std.sql.prql#L447 中体现为let trunc unit column - null返回空实现。diff计算两个日期的时间差diff返回end减去start的结果单位为unit——即date.diff unit start end在end晚于start时取正值。与trunc相反diff的单位必须写成裸关键字如day可接受单位同样取决于目标数据库。from events derive { days_open (date.diff day start_date end_date) }date.diff编译为目标数据库自己的日期差函数因此精确的舍入行为——部分单位是否计数——遵循目标数据库的规则而不是由 PRQL 统一归一化。各方言的 SQL 输出对照方言SQL 输出GenericDATEDIFF(day, start_date, end_date)DuckDBdatediff(day, start_date, end_date)MSSQLDATEDIFF(day, start_date, end_date)MySQLTIMESTAMPDIFF(day, start_date, end_date)ClickhousedateDiff(day, start_date, end_date)BigQueryDATE_DIFF(end_date, start_date, day)SnowflakeDATEDIFF(day, start_date, end_date)源码层面的证据通用实现let diff unit start end - sDATEDIFF({unit:0}, {start:0}, {end:0})std.sql.prql#L142其上方注释罗列了 ClickHouse、DuckDB、MariaDB、MySQL、MSSQL、Snowflake、Redshift 的参考文档链接DuckDB 覆盖为datediff({unit:0}, {start:0}, {end:0})std.sql.prql#L292Clickhouse 覆盖为dateDiff({unit:0}, {start:0}, {end:0})std.sql.prql#L262MySQL 覆盖为TIMESTAMPDIFF({unit:0}, {start:0}, {end:0})std.sql.prql#L344BigQuery 覆盖为DATE_DIFF({end:0}, {start:0}, {unit:0})std.sql.prql#L241注意其参数顺序为(end, start, unit)与其他方言不同。[!NOTE] Postgres 和 SQLite 没有内置的DATEDIFF等价函数因此针对这两个方言编译date.diff会报错。在 std.sql.prql#L391 与 std.sql.prql#L446 中分别体现为let diff unit start end - nullPostgres和let diff unit start end - nullSQLite。diff 与 trunc 的参数风格差异速查函数单位写法单位必须编译期已知不支持的方言date.trunc unit column字符串字面量如day是否则报错SQLitedate.diff unit start end裸关键字如day—关键字天然固定Postgres、SQLite日期与时间格式说明符完整对照表PRQL 的日期时间格式说明符是chronostrftime 说明符的一个子集。以下是当前受支持的全部说明符说明符示例描述日期说明符DATE SPECIFIERS%Y2001年份数字零填充到 4 位%y01年份数字零填充到 2 位%m07月份数字01–12零填充到 2 位%-m7月份数字1-12不填充%bJul缩写月份名固定 3 个字母%BJuly完整月份名%d08日数字01-31零填充到 2 位%-d8日数字1-31不填充%aSun缩写星期名固定 3 个字母%ASunday完整星期名%D07/08/01月-日-年格式等价于%m/%d/%y%x07/08/01语言环境的日期表示%F2001-07-08年-月-日格式ISO 8601等价于%Y-%m-%d时间说明符TIME SPECIFIERS%H00小时数字00-23零填充到 2 位%-H0小时数字0-23不填充%I1212 小时制小时数字01--12零填充到 2 位%pAM12 小时制中的AM或PM%M34分钟数字00-59零填充到 2 位%S60秒数字00-60零填充到 2 位%f264900距上一个整秒以来的微秒数¹%R00:34时-分格式等价于%H:%M%T00:34:60时-分-秒格式等价于%H:%M:%S%X00:34:60语言环境的时间表示如 23:13:48%r12:34:60 AM语言环境的 12 小时制时间如 11:11:04 PM日期与时间说明符DATE TIME SPECIFIERS%2001-07-08T00:34:60.026490ZISO 8601 / RFC 3339 日期时间格式特殊说明符SPECIAL SPECIFIERS%t字面制表符\t%n字面换行符\n%%字面百分号空格填充说明符不受支持chrono 的%_前缀形式空格填充以及%k/%l别名在任何方言中都不受支持。¹%f与 chrono 的差异在 PRQL 中%f表示微秒microseconds这与 chrono 中%f表示纳秒nanoseconds不同。这一点在上表脚注中明确标注是迁移 chrono 格式串到 PRQL 时最容易踩的坑之一。兼容性边界与限制汇总综合官方文档与源码date模块的方言支持矩阵如下函数BigQueryClickhouseDuckDBMySQLMSSQLPostgresSQLiteSnowflaketo_text✅✅✅✅✅✅❌❌默认now✅✅✅继承通用✅✅继承通用✅继承通用✅继承通用✅继承通用trunc✅特殊语法✅✅✅✅DATETRUNC✅❌✅引号列名diff✅参数序不同✅✅✅✅❌❌✅引号列名使用时的关键注意事项to_text必须显式声明方言且只支持 BigQuery、Clickhouse、DuckDB、MySQL、MSSQL、Postgres 六个目标格式参数只能是字符串字面量见 gen_expr.rs 的编译期校验。date.trunc不适用于 SQLitedate.diff不适用于 Postgres 和 SQLite否则编译报错。diff的舍入语义不归一化——部分单位如 1.5 天是否计为 2 天完全取决于目标数据库的DATEDIFF/TIMESTAMPDIFF/dateDiff/DATE_DIFF实现PRQL 不做跨库统一。diff与trunc的单位写法不同前者用裸关键字date.diff day ...后者用字符串date.trunc day混用会编译失败。trunc的单位必须编译期已知单位位置放列名或表达式属于错误用法。%f表示微秒而非纳秒且空格填充%_、%k、%l不被任何方言支持。以上约束均有仓库源码佐证读者可通过 prqlc/prqlc/src/semantic/std.prql、prqlc/prqlc/src/sql/std.sql.prql、prqlc/prqlc/src/sql/gen_expr.rs 及集成测试 prqlc/prqlc/tests/integration/queries/date_to_text.prql 进一步深入验证。当需要实现标准库未覆盖的日期逻辑时PRQL 的 s-string 仍然是最后的逃生通道见 标准库文档。赞分享后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载相关推荐PRQL 标准库stdlib完全指南内置函数、模块划分与跨数据库方言实现PRQL 标准库stdlib完全指南内置函数、模块划分与跨数据库方言实现 PRQL 标准库Standard Library是 PRQL 语言内置函数与后端PRQL 标准库 text 模块字符串处理函数的完整指南与 SQL 编译原理PRQL 标准库 text 模块字符串处理函数的完整指南与 SQL 编译原理 本指南聚焦 PRQL 标准库中的 text 模块系统讲解 lower 、 up后端internationalized/date 完全指南国际化日历、日期与时间处理库的 API 详解internationalized/date 完全指南国际化日历、日期与时间处理库的 API 详解 internationalized/date 是 re前端UI组件设计系统国际化状态管理上一篇TheAlgorithms/Python 维护脚本实战用 gh CLI 批量清理 Hacktoberfest 高峰期的 Pull Request下一篇Meteor 用户与账户系统完全指南密码登录、无密码登录、2FA 与 OAuth 集成实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表