如何使用calcite构建SQL并执行查询

2023-08-28 15:37:24 浏览数 (1)

大家好,这是 Calcite 的第二篇文章了,我一直毫不掩饰对她的喜爱,而且一直在致力于为社区做一些贡献,如果你也喜欢这个项目的话,欢迎评论,转发,如果没看过第一篇的话,也欢迎移步去看看(手把手教你使用Calcite查看SQL执行计划)。如果你还不了解这个项目的话,我也希望能通过我,让你知道这个优秀的项目。

今天我要分享的主题是关于 Calcite 关系代数 以及 SQL 的那些事,Let's go !!!

关系代数

首先关系代数是 Calcite 的核心。每个查询都可以表示为一个 关系运算符树。你可以将 SQL 转换为关系代数,也可以直接构建关系运算符树。

优化器规则使用保持 相同语义 的 数学恒等式 来变换表达式树。例如,如果过滤器没有引用其他输入中的列,那么将过滤器推入到内部关联的输入则是有效的。

Calcite 通过反复地将优化器规则应用于关系表达式来优化查询。成本模型指导该过程,优化器引擎生成与原始语义相同,但成本较低的替代表达式。

优化过程是可扩展的。你可以添加自己的 关系运算符、优化器规则、成本模型 和 统计信息。

代数构建器

构建关系表达式的最简单方法是使用代数构建器 RelBuilder。下面是一个例子:

扫描表

代码语言:javascript复制
FrameworkConfig config;
RelBuilder builder = RelBuilder.create(config);
RelNode cnode = relBuilder.scan("consumers").build();
System.out.println("==> " RelOptUtil.toString(cnode));

其执行结果如下:

代码语言:javascript复制
==> LogicalTableScan(table=[[consumers]])

等价与SQL

代码语言:javascript复制
SELECT * FROM consumers ;

添加投影

现在,让我们添加一个投影,相当于如下 SQL:

代码语言:javascript复制
SELECT firstname,lastname FROM consumers ;

我们只需要在调用 build 方法前,添加一个 project 方法调用:

代码语言:javascript复制
cnode = relBuilder.scan("consumers").project(relBuilder.field("firstname"),
        relBuilder.field("lastname")).build();
System.out.println("==> " RelOptUtil.toString(cnode));

其执行结果如下:

代码语言:javascript复制
==> LogicalProject(firstname=[$1], lastname=[$2])
  LogicalTableScan(table=[[consumers]])

添加过滤聚合

下面是一个包含聚合和过滤的查询语句:

代码语言:javascript复制
cnode = relBuilder.scan("orders")
            .aggregate(
                    relBuilder.groupKey("goods"),
                    relBuilder.count(false, "c"),
                    relBuilder.sum(false, "p", relBuilder.field("price")))
            .filter(
                    relBuilder.call(
                        SqlStdOperatorTable.GREATER_THAN,
                                relBuilder.field("c"),
                                relBuilder.literal(1))
                    ).build();

System.out.println(RelOptUtil.toString(cnode));

相当于如下 SQL:

代码语言:javascript复制
SELECT goods, count(*) AS count, sum(price) AS p FROM orders GROUP BY goods HAVING count(*) > 1

其执行结果如下:

代码语言:javascript复制
LogicalFilter(condition=[>($1, 1)])
  LogicalAggregate(group=[{1}], c=[COUNT()], p=[SUM($2)])
    LogicalTableScan(table=[[orders]])

好了,接下来我们来看进一步的实例。

实例 CalciteRelBuilderCase 完整代码

代码语言:javascript复制
package com.dafei1288;

import org.apache.calcite.adapter.csv.CsvSchema;
import org.apache.calcite.adapter.csv.CsvTable;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Frameworks;
import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.tools.RelRunners;

import java.io.File;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

public class CalciteRelBuilderCase {
    public static void main(String[] args) throws SQLException {
        SchemaPlus rootSchema = Frameworks.createRootSchema(true);
        String csvPath = "D:\study\codespace\testcalcite\src\main\resources\db";
        CsvSchema csvSchema = new CsvSchema(new File(csvPath), CsvTable.Flavor.SCANNABLE);
        rootSchema.add("consumers", csvSchema.getTable("consumers"));
        rootSchema.add("orders", csvSchema.getTable("orders"));

        FrameworkConfig frameworkConfig = Frameworks.newConfigBuilder()
                .parserConfig(SqlParser.Config.DEFAULT)
                .defaultSchema(rootSchema)
                .build();

        RelBuilder relBuilder = RelBuilder.create(frameworkConfig);
//        id,goods,price,amount,user_id               orders
//        id,firstname,lastname,birth                 consumers
        RelNode node = relBuilder
                .scan("consumers")
                .scan("orders")
                .join(JoinRelType.INNER,
                        relBuilder.call(SqlStdOperatorTable.EQUALS,
                                relBuilder.field("id"),
                                relBuilder.field("user_id")))
                .filter(
                        relBuilder.call(SqlStdOperatorTable.EQUALS,
                                relBuilder.field("lastname"),
                                relBuilder.literal("jacky")))
                .project(
                        relBuilder.field("id"),
                        relBuilder.field("goods"),
                        relBuilder.field("price"),
                        relBuilder.field("firstname"),
                        relBuilder.field("lastname"))
                .sortLimit(0, 5, relBuilder.field("id"))
                .build();

        System.out.println(RelOptUtil.toString(node));




        System.out.println("[result:]");
        PreparedStatement run = RelRunners.run(node);
        ResultSet resultSet = run.executeQuery();

        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        int colCount = resultSetMetaData.getColumnCount();
        for(int i = 1; i <= colCount ; i  ){
            System.out.print(resultSetMetaData.getColumnName(i) "t");
        }
        System.out.println();

        while(resultSet.next()){
            for(int i = 1; i <= colCount ; i  ){
                System.out.print(resultSet.getString(i) "t");
            }
            System.out.println();
        }
    }
}

执行结果

代码语言:javascript复制
LogicalSort(sort0=[$0], dir0=[ASC], fetch=[5])
  LogicalProject(id=[$0], goods=[$5], price=[$6], firstname=[$1], lastname=[$2])
    LogicalFilter(condition=[=($2, 'jacky')])
      LogicalJoin(condition=[=($0, $4)], joinType=[inner])
        LogicalTableScan(table=[[consumers]])
        LogicalTableScan(table=[[orders]])

[result:]
id goods price firstname lastname 
1 book 100 li jacky 

好了,今天的分享就到这里,下次我们来看看如果进行SQL改写。

参考连接

https://calcite.apache.org/docs/algebra.html

https://strongduanmu.com/wiki/calcite/algebra.html

https://zhuanlan.zhihu.com/p/623062311

https://blog.csdn.net/skyyws/article/details/124828049

0 人点赞