diff --git a/plan/match_property.go b/plan/match_property.go index 98c594141d2f9..4c5a4e59c2c23 100644 --- a/plan/match_property.go +++ b/plan/match_property.go @@ -38,6 +38,10 @@ func (ts *PhysicalTableScan) matchProperty(prop *requiredProperty, infos ...*phy sortedTS.Desc = prop.props[0].desc sortedTS.KeepOrder = true sortedTS.addLimit(prop.limit) + // If there exists a table filter, we should calculate the filter scan cost. + if len(sortedTS.tableFilterConditions) > 0 { + cost += rowCount * cpuFactor + } p := sortedTS.tryToAddUnionScan(&sortedTS) return enforceProperty(&requiredProperty{limit: prop.limit}, &physicalPlanInfo{ p: p, diff --git a/plan/physical_plan_builder.go b/plan/physical_plan_builder.go index f2650d784827e..dc1cd4dcbc705 100644 --- a/plan/physical_plan_builder.go +++ b/plan/physical_plan_builder.go @@ -937,8 +937,9 @@ func (p *Selection) convert2PhysicalPlanPushOrder(prop *requiredProperty) (*phys if limit != nil && info.p != nil { if np, ok := info.p.(physicalDistSQLPlan); ok { np.addLimit(limit) + scanCount := info.count info.count = limit.Count - info.cost = np.calculateCost(info.count) + info.cost = np.calculateCost(info.count, scanCount) } else { info = enforceProperty(&requiredProperty{limit: limit}, info) } diff --git a/plan/physical_plan_test.go b/plan/physical_plan_test.go index 7f0f3872c4154..2716934719987 100644 --- a/plan/physical_plan_test.go +++ b/plan/physical_plan_test.go @@ -340,13 +340,17 @@ func (s *testPlanSuite) TestCBO(c *C) { sql: "select sum(t.a) from t where t.c in (1,2) and t.d in (1,3) group by t.d order by t.d", best: "Index(t.c_d_e)[[1 1,1 1] [1 3,1 3] [2 1,2 1] [2 3,2 3]]->HashAgg->Sort->Trim", }, + { + sql: "select * from t where t.c = 1 and t.e = 1 order by t.a limit 1", + best: "Index(t.c_d_e)[[1,1]]->Sort + Limit(1) + Offset(0)", + }, { sql: "select * from t t1 ignore index(e) where c < 0", best: "Index(t.c_d_e)[[-inf,0)]", }, { sql: "select * from t t1 ignore index(c_d_e) where c < 0", - best: "Table(t)->Selection", + best: "Table(t)", }, { sql: "select * from t where f in (1,2) and g in(1,2,3,4,5)", @@ -373,28 +377,28 @@ func (s *testPlanSuite) TestCBO(c *C) { best: "LeftHashJoin{Table(t)->Table(t)}(a.c,b.c)->HashAgg->Sort->Trim", }, { - sql: "select count(*) from t group by c", - best: "Index(t.c_d_e)[[,+inf]]->StreamAgg", + sql: "select count(*) from t where concat(a,b) = 'abc' group by c", + best: "Index(t.c_d_e)[[,+inf]]->Selection->StreamAgg", }, { - sql: "select sum(b.a) from t a , t b where a.c = b.c group by b.d", - best: "LeftHashJoin{Table(t)->Index(t.c_d_e)[[,+inf]]->StreamAgg}(a.c,b.c)->HashAgg", + sql: "select sum(b.a) from t a, t b where a.c = b.c and cast(b.d as char) group by b.d", + best: "RightHashJoin{Index(t.c_d_e)[[,+inf]]->Selection->StreamAgg->Table(t)}(b.c,a.c)->HashAgg", }, { sql: "select count(*) from t group by e order by d limit 1", best: "Table(t)->HashAgg->Sort + Limit(1) + Offset(0)->Trim", }, { - sql: "select count(*) from t group by a", - best: "Table(t)->StreamAgg", + sql: "select count(*) from t where concat(a,b) = 'abc' group by a", + best: "Table(t)->Selection->StreamAgg", }, { - sql: "select count(*) from t group by a order by a", - best: "Table(t)->StreamAgg->Trim", + sql: "select count(*) from t where concat(a,b) = 'abc' group by a order by a", + best: "Table(t)->Selection->StreamAgg->Trim", }, { - sql: "select count(distinct e) from t where c = 1 group by d", - best: "Index(t.c_d_e)[[1,1]]->StreamAgg", + sql: "select count(distinct e) from t where c = 1 and concat(c,d) = 'abc' group by d", + best: "Index(t.c_d_e)[[1,1]]->Selection->StreamAgg", }, { sql: "select count(distinct e) from t group by d", @@ -423,7 +427,7 @@ func (s *testPlanSuite) TestCBO(c *C) { }, { sql: "select * from t a where a.c < 10000 order by a.a limit 2", - best: "Index(t.c_d_e)[[-inf,10000)]->Sort + Limit(2) + Offset(0)", + best: "Table(t)", }, { sql: "select * from t a where a.c < 10000 and a.d in (1000, a.e) order by a.a limit 2", @@ -476,7 +480,7 @@ func (s *testPlanSuite) TestCBO(c *C) { builder := &planBuilder{ allocator: new(idAllocator), - ctx: mock.NewContext(), + ctx: mockContext(), colMapper: make(map[*ast.ColumnNameExpr]int), } p := builder.build(stmt) diff --git a/plan/physical_plans.go b/plan/physical_plans.go index 619f5a8d6909b..35175a6a777e4 100644 --- a/plan/physical_plans.go +++ b/plan/physical_plans.go @@ -65,11 +65,12 @@ type physicalDistSQLPlan interface { addAggregation(ctx context.Context, agg *PhysicalAggregation) expression.Schema addTopN(ctx context.Context, prop *requiredProperty) bool addLimit(limit *Limit) - calculateCost(count uint64) float64 + calculateCost(count uint64, scanCount uint64) float64 } -func (p *PhysicalIndexScan) calculateCost(count uint64) float64 { - cnt := float64(count) +func (p *PhysicalIndexScan) calculateCost(resultCount uint64, scanCount uint64) float64 { + // TODO: Eliminate index cost more precisely. + cnt := float64(resultCount) // network cost cost := cnt * netWorkFactor if p.DoubleRead { @@ -77,14 +78,17 @@ func (p *PhysicalIndexScan) calculateCost(count uint64) float64 { } // sort cost if !p.OutOfOrder && p.DoubleRead { - cost += float64(count) * cpuFactor + cost += cnt * cpuFactor } return cost } -func (p *PhysicalTableScan) calculateCost(count uint64) float64 { - cnt := float64(count) - return cnt * netWorkFactor +func (p *PhysicalTableScan) calculateCost(resultCount uint64, scanCount uint64) float64 { + cost := float64(resultCount) * netWorkFactor + if len(p.tableFilterConditions) > 0 { + cost += float64(scanCount) * cpuFactor + } + return cost } type physicalTableSource struct {