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命令的时候会输出对应信息

相关推荐
我叫张小白。1 分钟前
MySQL架构与SQL执行完全解析
sql·mysql·架构
顾凌陵3 分钟前
SQL注入漏洞
数据库·sql·oracle
清平乐的技术专栏16 分钟前
【Flink学习】(三)Flink 数据源详解,主流 Source 数据读取
大数据·flink·linq
数智前线17 分钟前
腾讯云融合创新产品矩阵全面升级,首次发布专有云版“龙虾”
大数据·人工智能
Luminbox紫创测控19 分钟前
基于环境舱的新能源汽车三高试验方法与热响应评估
大数据·人工智能·测试工具·汽车·安全性测试·测试标准
敲上瘾19 分钟前
LangChain 消息机制与提示词模板指南
大数据·python·langchain
枫叶林FYL22 分钟前
【机器学习与智慧医疗】糖尿病视网膜病变视力丧失预测:贝叶斯估计与威布尔分布
大数据·人工智能·机器学习
十六年开源服务商25 分钟前
2026网站建设方案内容审批避坑指南
大数据·人工智能
团象科技25 分钟前
跨境业务频繁卡顿遇瓶颈?谷歌云AI算力补齐链路短板破局增收
大数据·人工智能·深度学习
Bechamz29 分钟前
大数据开发学习Day37
大数据·学习