【问题标题】:Converting SQL Query with Aggregate Function to Relational Algebra Expression in Apache Calcite - No match found for function signature在 Apache Calcite 中将带有聚合函数的 SQL 查询转换为关系代数表达式 - 找不到函数签名的匹配项
【发布时间】:2019-03-27 00:13:36
【问题描述】:

我正在尝试使用 Apache Calcite SqlToRelConverter 将 SQL 查询转换为关系代数表达式。

这个查询很好用(引号是为了确保小写):

queryToRelationalAlgebraRoot("SELECT \"country\" FROM \"mytable\"")

但是在这个查询中它失败了:

queryToRelationalAlgebraRoot("SELECT \"country\", SUM(\"salary\") FROM \"mytable\" GROUP BY \"country\"")

出现此错误:

org.apache.calcite.sql.validate.SqlValidatorException: No match found for function signature SUM(<NUMERIC>)

似乎不知何故,SQL 验证器没有注册 sum 或 count 之类的聚合函数。

case class Income(id: Int, salary: Double, country: String)

class SparkDataFrameTable(df: DataFrame) extends AbstractTable {

  def getRowType(typeFactory: RelDataTypeFactory): RelDataType = {
    val typeList = df.schema.fields.map {
      field => field.dataType match {
        case t: StringType => typeFactory.createSqlType(SqlTypeName.VARCHAR)
        case t: IntegerType => typeFactory.createSqlType(SqlTypeName.INTEGER)
        case t: DoubleType => typeFactory.createSqlType(SqlTypeName.DOUBLE)
      }
    }.toList.asJava
    val fieldNameList = df.schema.fieldNames.toList.asJava
    typeFactory.createStructType(typeList, fieldNameList)
  }

}

object RelationalAlgebra {

  def queryToRelationalAlgebraRoot(query: String): RelRoot = {
    val sqlParser = SqlParser.create(query)
    val sqlParseTree = sqlParser.parseQuery()

    val frameworkConfig = Frameworks.newConfigBuilder().build()
    val planner = new PlannerImpl(frameworkConfig)

    val rootSchema = CalciteSchema.createRootSchema(true, true)

    // some sample data for testing
    val inc1 = new Income(1, 100000, "USA")
    val inc2 = new Income(2, 110000, "USA")
    val inc3 = new Income(3, 80000, "Canada")
    val spark = SparkSession.builder().master("local").getOrCreate()
    import spark.implicits._
    val df = Seq(inc1, inc2, inc3).toDF()
    rootSchema.add("mytable", new SparkDataFrameTable(df))

    val defaultSchema = List[String]().asJava
    val calciteConnectionConfigProperties = new Properties()
    val calciteConnectionConfigImpl = new CalciteConnectionConfigImpl(calciteConnectionConfigProperties)
    val sqlTypeFactoryImpl = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT)
    val calciteCatelogReader = new CalciteCatalogReader(rootSchema, defaultSchema, sqlTypeFactoryImpl, calciteConnectionConfigImpl)
    val defaultValidator = SqlValidatorUtil.newValidator(new SqlStdOperatorTable(), calciteCatelogReader, sqlTypeFactoryImpl, SqlConformanceEnum.LENIENT)

    val relExpressionOptimizationCluster = RelOptCluster.create(new VolcanoPlanner(), new RexBuilder(sqlTypeFactoryImpl))

    val sqlToRelConfig = SqlToRelConverter.configBuilder().build()

    val sqlToRelConverter = new SqlToRelConverter(planner, defaultValidator, calciteCatelogReader, relExpressionOptimizationCluster, StandardConvertletTable.INSTANCE, sqlToRelConfig)

    sqlToRelConverter.convertQuery(sqlParseTree, true, true)
  }

}

【问题讨论】:

    标签: sql relational-algebra apache-calcite sql-parser


    【解决方案1】:

    代码的问题是new SqlStdOperatorTable() 创建了一个未初始化的验证器。使用SqlStdOperatorTable的正确方法是使用SqlStdOperatorTable.instance()

    我在通过电子邮件发送到 dev@calcite.apache.org 邮件列表后找到了解决方案。我要感谢 Yuzhao Chen 调查我的问题并指出我的代码存在的问题。

    【讨论】:

      【解决方案2】:

      我不熟悉 api,但您的 SQL 需要按国家/地区分组。如果一个工具要获取这个输出并使用它,它可能需要你用别名来命名列。

      【讨论】:

      • 你是对的!我犯了复制和粘贴错误。当我遇到与 GROUP BY 相同的错误时,将更新问题。
      • 并命名 sum(sakary)
      • 不需要别名
      • @tuzhucheng & SaadAhmad 标准 & 典型 SQL 允许在没有显式分组的情况下进行聚合。 (意思是按所有列分组。)(也是默认选择列别名。)我不知道这个SQL是否有。
      • 那是标准 SQL 吗?因为 Oracle 和 sqlserver 都需要 group by。在这种情况下,哪个数据库不需要 group by?当然会很好
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多