为什么Apache Calcite会为查询包含的所有表估算100行?

时间:2019-02-16 17:55:09

标签: csv relational-algebra sql-optimization apache-calcite

我最近尝试使用三个CSV文件作为表在Apache Calcite中执行查询

  • TTLA_ONE包含59行
  • TTLR_ONE包含61390行
  • EMPTY_T包含0行

这是执行的查询:

EXPLAIN PLAN FOR SELECT COUNT(*) as NUM 
FROM TTLA_ONE A 
INNER JOIN TTLR_ONE B1 ON A.X = B1.X
INNER JOIN TTLR_ONE B2 ON B2.X = B1.X
INNER JOIN EMPTY_T C1 ON C1.X = B2.Y
INNER JOIN EMPTY_T C2 ON C2.X = C2.X

查询的结果始终为零,因为我们正在连接一个空表。 获得的计划是:

EnumerableAggregate(group=[{}], NUM=[COUNT()])
  EnumerableJoin(condition=[=($1, $4)], joinType=[inner])
    EnumerableJoin(condition=[=($0, $1)], joinType=[inner])
      EnumerableInterpreter
        BindableTableScan(table=[[STYPES, TTLA_ONE]])
      EnumerableCalc(expr#0..1=[{inputs}], X=[$t0])
        EnumerableInterpreter
          BindableTableScan(table=[[STYPES, TTLR_ONE]])
    EnumerableJoin(condition=[=($1, $3)], joinType=[inner])
      EnumerableJoin(condition=[true], joinType=[inner])
        EnumerableCalc(expr#0=[{inputs}], expr#1=[IS NOT NULL($t0)], X=[$t0], $condition=[$t1])
          EnumerableInterpreter
            BindableTableScan(table=[[STYPES, EMPTY_T]])
        EnumerableInterpreter
          BindableTableScan(table=[[STYPES, EMPTY_T]])
      EnumerableInterpreter
        BindableTableScan(table=[[STYPES, TTLR_ONE]])

可能会注意到在计划的最后使用了空表。

我为此test code添加了一个示例。

我进一步研究了代码,并打开日志进行调试,我发现所有表行估计为100,但这不是事实。

下面,可以通过在调试模式下设置的日志来找到计划估算值:

  EnumerableJoin(condition=[=($1, $4)], joinType=[inner]): rowcount = 3.0375E7, cumulative cost = {3.075002214917643E7 rows, 950.0 cpu, 0.0 io}, id = 26284
EnumerableJoin(condition=[=($0, $1)], joinType=[inner]): rowcount = 1500.0, cumulative cost = {2260.517018598809 rows, 400.0 cpu, 0.0 io}, id = 26267
  EnumerableInterpreter: rowcount = 100.0, cumulative cost = {50.0 rows, 50.0 cpu, 0.0 io}, id = 26260
    BindableTableScan(table=[[STYPES, TTLA_ONE]]): rowcount = 100.0, cumulative cost = {1.0 rows, 1.01 cpu, 0.0 io}, id = 7789
  EnumerableCalc(expr#0..1=[{inputs}], X=[$t0]): rowcount = 100.0, cumulative cost = {150.0 rows, 350.0 cpu, 0.0 io}, id = 26290
    EnumerableInterpreter: rowcount = 100.0, cumulative cost = {50.0 rows, 50.0 cpu, 0.0 io}, id = 26263
      BindableTableScan(table=[[STYPES, TTLR_ONE]]): rowcount = 100.0, cumulative cost = {1.0 rows, 1.01 cpu, 0.0 io}, id = 7791
EnumerableJoin(condition=[=($1, $3)], joinType=[inner]): rowcount = 135000.0, cumulative cost = {226790.8015771949 rows, 550.0 cpu, 0.0 io}, id = 26282
  EnumerableJoin(condition=[true], joinType=[inner]): rowcount = 9000.0, cumulative cost = {9695.982870329724 rows, 500.0 cpu, 0.0 io}, id = 26277
    EnumerableCalc(expr#0=[{inputs}], expr#1=[IS NOT NULL($t0)], X=[$t0], $condition=[$t1]): rowcount = 90.0, cumulative cost = {140.0 rows, 450.0 cpu, 0.0 io}, id = 26288
      EnumerableInterpreter: rowcount = 100.0, cumulative cost = {50.0 rows, 50.0 cpu, 0.0 io}, id = 26270
        BindableTableScan(table=[[STYPES, EMPTY_T]]): rowcount = 100.0, cumulative cost = {1.0 rows, 1.01 cpu, 0.0 io}, id = 7787
    EnumerableInterpreter: rowcount = 100.0, cumulative cost = {50.0 rows, 50.0 cpu, 0.0 io}, id = 26275
      BindableTableScan(table=[[STYPES, EMPTY_T]]): rowcount = 100.0, cumulative cost = {1.0 rows, 1.01 cpu, 0.0 io}, id = 7787
  EnumerableInterpreter: rowcount = 100.0, cumulative cost = {50.0 rows, 50.0 cpu, 0.0 io}, id = 26280
    BindableTableScan(table=[[STYPES, TTLR_ONE]]): rowcount = 100.0, cumulative cost = {1.0 rows, 1.01 cpu, 0.0 io}, id = 7791

我们绝对可以看到,对于每个表,估计始终为100 rowcount = 100.0

查询已正确执行,但未优化计划。有人知道为什么表统计信息未正确评估吗?

2 个答案:

答案 0 :(得分:1)

这里的答案似乎与已经通过评论链接的问题相同。

  

Flink尚未(尚未)重新排序联接

     

在当前版本(2019年1月1.7.1)中,...方解石使用其默认值100。

因此,执行计划不会查找零行的表。特别是,我从那些答案中怀疑,即使您在FROM子句中对表进行了重新排序,它仍然不会引起注意。

通常,SQL优化是由索引的可用性以及表的基数来驱动的。

  

注入表基数估计的唯一方法是通过ExternalCatalog

您正在这样做吗?

如果将这些表作为CSV文件加载,是否要声明键和索引以及目录所需的其他内容?

听起来方解石不是成熟产品。如果您正在寻找测试SQL优化/查询计划的测试平台,请使用其他产品。

答案 1 :(得分:0)

问题在于,在类CsvTable中,有必要通过执行以下操作来覆盖getStatistic属性方法:

 private Statistic statistic;
 // todo: assign statistics  

  @Override
  public Statistic getStatistic() {
    return statistic;
  }

可以从构造函数传递这些统计信息,也可以注入一些生成它们的对象。

目前,它仅返回Statistics.UNKNOWN,位于超类实现AbstractTable中。 当然,没有统计信息,该计划的估计成本是不正确的。