StarRocks 中 CURRENT_TIMESTAMP 和 CURRENT_TIME 分区过滤问题

背景

本文基于Starrocks 3.3.5

最近在进行Starrocks 跑数据的时候,发现了一个SQL 扫描了所有分区的数据,简化后的SQL如下:

复制代码
select date_created from tableA where date_created=date_format(current_time(), '%Y-%m-%d %H:%i:%S') limit 20

其中建表语句如下:

复制代码
CREATE TABLE `tableA` (
  ...
  `date_created` datetime NOT NULL DEFAULT "1970-01-01 00:00:00" COMMENT "",
  ...
) ENGINE=OLAP
PRIMARY KEY(id,date_created)
PARTITION BY date_trunc("day",date_created)
DISTRIBUTED BY HASH(id) BUCKETS 50
PROPERTIES( 
  "compression" = "ZSTD"
  );

但是如果用CURRENT_TIMESTAMP 替换的话,就能实现分区下推,具体的SQL如下:

复制代码
select date_created from tableA where date_created=CURRENT_TIMESTAMP() limit 20

结论

current_time() 函数不支持常量折叠,也就是不支持在计划解析和优化阶段来计算结果。而CURRENT_TIMESTAMP 在计划优化阶段就可以计算出结果。

具体的explain对应的SQL如下:

可以看到 用 current_time 函数的 扫描了全部的分区

CURRENT_TIMESTAMP函数的 只选择了一个分区的数据

分析

先执行两个命令从感官上来感受一下:

复制代码
TRACE LOGS OPTIMIZER  SELECT CURRENT_TIMESTAMP()\G;
TRACE LOGS OPTIMIZER  SELECT (date_format(current_time(), '%Y-%m-%d %H:%i:%S'))\G;

TRACE LOGS OPTIMIZER SELECT CURRENT_TIMESTAMP()\G;`的结果如下:

复制代码
...
*************************** 34. row ***************************
Explain String: 0ms|    [MV TRACE] [PREPARE GLOBAL] There are no valid related mvs for the query plan
*************************** 35. row ***************************
Explain String: 0ms|    [MV TRACE] [PREPARE GLOBAL] MV rewrite strategy: MvRewriteStrategy{enableMaterializedViewRewrite=false, enableForceRBORewrite=false, enableViewBasedRewrite=false, enableSingleTableRewrite=false, enableMultiTableRewrite=false, mvStrategy=DEFAULT}
*************************** 36. row ***************************
Explain String: 0ms|    origin logicOperatorTree:
*************************** 37. row ***************************
Explain String: LogicalProjectOperator {projection=[2025-03-28 10:35:00]}
*************************** 38. row ***************************
Explain String: ->  LOGICAL_VALUES
*************************** 39. row ***************************
Explain String: 0ms|    [TRACE QUERY 3efdbff0-0b7d-11f0-8f6c-00163e164034] APPLY RULE TF_PRUNE_PROJECT_COLUMNS 58
*************************** 40. row ***************************
Explain String: Original Expression:
*************************** 41. row ***************************
Explain String: LogicalProjectOperator {projection=[2025-03-28 10:35:00]}
*************************** 42. row ***************************
Explain String: ->  LOGICAL_VALUES
*************************** 43. row ***************************
Explain String: 0ms|
*************************** 44. row ***************************
Explain String: New Expression:
*************************** 45. row ***************************
Explain String: 0:LogicalProjectOperator {projection=[2025-03-28 10:35:00]}
...

TRACE LOGS OPTIMIZER SELECT (date_format(current_time(), '%Y-%m-%d %H:%i:%S'))\G的结果如下:

复制代码
...
*************************** 34. row ***************************
Explain String: 0ms|    [MV TRACE] [PREPARE GLOBAL] MV rewrite strategy: MvRewriteStrategy{enableMaterializedViewRewrite=false, enableForceRBORewrite=false, enableViewBasedRewrite=false, enableSingleTableRewrite=false, enableMultiTableRewrite=false, mvStrategy=DEFAULT}
*************************** 35. row ***************************
Explain String: 0ms|    origin logicOperatorTree:
*************************** 36. row ***************************
Explain String: LogicalProjectOperator {projection=[date_format(cast(current_time() as datetime), %Y-%m-%d %H:%i:%S)]}
*************************** 37. row ***************************
Explain String: ->  LOGICAL_VALUES
*************************** 38. row ***************************
Explain String: 0ms|    [TRACE QUERY 7af2e9bb-0b7e-11f0-8f6c-00163e164034] APPLY RULE TF_PRUNE_PROJECT_COLUMNS 58
*************************** 39. row ***************************
Explain String: Original Expression:
*************************** 40. row ***************************
Explain String: LogicalProjectOperator {projection=[date_format(cast(current_time() as datetime), %Y-%m-%d %H:%i:%S)]}
*************************** 41. row ***************************
Explain String: ->  LOGICAL_VALUES
*************************** 42. row ***************************
Explain String: 0ms|
*************************** 43. row ***************************
Explain String: New Expression:
*************************** 44. row ***************************
Explain String: 0:LogicalProjectOperator {projection=[date_format(cast(current_time() as datetime), %Y-%m-%d %H:%i:%S)]}
*************************** 45. row ***************************
Explain String: ->  LOGICAL_VALUES
...

可以看到 CURRENT_TIMESTAMP 在优化算子阶段就已经计算出来了,为LogicalProjectOperator {projection=[2025-03-28 10:35:00]}

(date_format(current_time(), '%Y-%m-%d %H:%i:%S')) 并没有计算出来,为LogicalProjectOperator {projection=[date_format(cast(current_time() as datetime), %Y-%m-%d %H:%i:%S)]}

在这个案例中,主要涉及到的规则主要是:

复制代码
FoldConstantsRule
PartitionPruneRule

我们分析一下简单的SQL语句的数据流:SELECT CURRENT_TIMESTAMP()

复制代码
g4文件中querySpecification
    ||
    \/
ConnectProcessor.handleQuery
    ||
    \/
com.starrocks.sql.parser.SqlParser.parse
    ||
    \/
// 同时.g4 文件中 specialDateTimeExpression
// AstBuilder.visitSpecialDateTimeExpression 会构造 new FunctionCallExpr
// 这里最终会构建 SelectRelation(SelectList(FunctionCallExpr),ValuesRelation.newDualRelation)
AstBuilder.visitQuerySpecification 
    ||
    \/
StatementPlanner.plan
    ||
    \/
createQueryPlan
    ||
    \/
new RelationTransformer(transformerContext).transformWithSelectLimit(query) 
    ||
    \/
transform
    ||
    \/
visit(relation);
    ||
    \/
RelationTransformer.visitSelect
    ||
    \/
QueryTransformer.plan
    ||
    \/
SqlToScalarOperatorTranslator.translate => Visitor.visit => visitFunctionCall // 此时的逻辑计划为 SelectRelation(SelectList(CallOperator(CURRENT_TIMESTAMP)),ValuesRelation.newDualRelation)
                                        ||
                                        \/
                                        => scalarRewriter.rewrite(result, ScalarOperatorRewriter.DEFAULT_REWRITE_RULES) // 这里有ImplicitCastRule和FoldConstantsRule
    ||
    \/
projectForOrder  // 此时的的逻辑计划为 LogicalPlan(OptExprBuilder(LogicalProjectOperator(CallOperator(CURRENT_TIMESTAMP)))

现在来重点关注 DEFAULT_REWRITE_RULES 中涉及到的 ImplicitCastRule FoldConstantsRule 规则:

首先是 ImplicitCastRule 规则(这里主要是visitCall方法):

复制代码
这个规则主做:
1. 如果表达式需要的类型和该表达式对应的子表达式的参数输出的类型如果不一致的话,则会给表达式的子表达式加上一个CastOperator操作
2. 对每一个子表达式都递归一遍1步骤

date_format(current_time(), '%Y-%m-%d %H:%i:%S')就会命中这个规则

再次 是 FoldConstantsRule 规则(这里主要是visitCall/visitCastOperator方法):

复制代码
这个主要是做:
1. 主要是计算表达式为常量,即把CallOperator变成 ConstantOperator
2. 根据ScalarOperatorFunctions 和 MetaFunctions 函数中标注为 ConstantFunction 的函数,来看是否能够计算为常量
在这里能够找到  CURRENT_TIMESTAMP() 函数,但是找不到 current_time() 函数

CURRENT_TIMESTAMP() 就会命中这个规则

以上的 都在 "Transformer" 阶段完成的。

至于 PartitionPruneRule 则会在"Optimizer" 阶段完成 ,也就是optimizer.optimize方法中, 具体的实现,可以细看 PartitionPruneRule对应的方法,也就是在这个规则里会对涉及到的谓词来过滤出对应的分区,很显然因为CURRENT_TIMESTAMP是常量,所以能够裁剪到对应的分区中去,而date_format(current_time(), '%Y-%m-%d %H:%i:%S')不能计算出来,所以扫描了全表。

其他

trace输出信息的怎么回事

首先在g4文件中

复制代码
queryStatement
    : (explainDesc | optimizerTrace) ? queryRelation outfile?;

有对应的optimze语句 也就是 TRACE LOGS

这个在解析的时候 AstBuilder.visitQueryStatement 中会调用 queryStatement.setIsTrace 方法:

复制代码
public void setIsTrace(Tracers.Mode mode, String module) {
        this.isExplain = true;
        this.traceMode = mode;
        this.traceModule = module;
    }

此时 isExplain 设置为了true

之后在 StmtExecutor.execute方法中:

复制代码
 } else if (parsedStmt.isExplain()) {
            String explainString = buildExplainString(execPlan, ResourceGroupClassifier.QueryType.SELECT,
                    parsedStmt.getExplainLevel());
            if (executeInFe) {
                explainString = "EXECUTE IN FE\n" + explainString;
            }

这里的方法buildExplainString就会组装对应的explain信息:

复制代码
 if (parsedStmt.getTraceMode() == Tracers.Mode.TIMER) {
                explainString += Tracers.printScopeTimer();
            } else if (parsedStmt.getTraceMode() == Tracers.Mode.VARS) {
                explainString += Tracers.printVars();
            } else if (parsedStmt.getTraceMode() == Tracers.Mode.TIMING) {
                explainString += Tracers.printTiming();
            } else if (parsedStmt.getTraceMode() == Tracers.Mode.LOGS) {
                explainString += Tracers.printLogs();
            } else if (parsedStmt.getTraceMode() == Tracers.Mode.REASON) {
                explainString += Tracers.printReasons();
            } else {
                explainString += execPlan.getExplainString(explainLevel);
            }

所以在执行trace LOGS命令的时候会输出对应信息

相关推荐
陈皮糖..15 分钟前
27 届运维实习笔记|第三、四周:从流程熟练到故障排查,企业运维实战深化
运维·笔记·sql·nginx·ci/cd·云计算·jenkins
数新网络1 小时前
告别“数据沼泽”,拥抱“活水湖”:数新智能基于CyberEngine与Apache Paimon的新一代数据湖仓架构
大数据
实习僧企业版2 小时前
如何为中小企业点亮校招吸引力的灯塔
大数据·春招·雇主品牌·招聘技巧·口碑
塔能物联运维2 小时前
高密度机柜满载怎么办?热管理的“最后一厘米”:两相液冷
大数据
王苏安说钢材A4 小时前
无锡佳钛合不锈钢有限公司三通的焊接工艺
大数据
跨境卫士-小汪5 小时前
旺季前成本项变多跨境卖家如何设定更稳的备货优先级
大数据·人工智能·产品运营·跨境电商·亚马逊
地球资源数据云5 小时前
1951-2025年中国逐年1千米逐月总降水量区域统计数据集_年表_县
大数据·数据结构·数据库·数据仓库·人工智能
l1t5 小时前
DeepSeek v4辅助生成的单文件SQL查询示例页面
javascript·数据库·sql
云飞云共享云桌面5 小时前
精密机械制造工厂研发部门使用SolidWorks和ug,三维设计云桌面如何选择?
大数据·运维·服务器·网络·数据库·人工智能·制造
互联网推荐官5 小时前
定制开发落地实践:D-coding 销售采购系统赋能上海多终端软件项目建设
大数据