diff --git a/plan/cbo_test.go b/plan/cbo_test.go index efceb0ea0fb0..b07f900e6ef2 100644 --- a/plan/cbo_test.go +++ b/plan/cbo_test.go @@ -15,6 +15,7 @@ package plan_test import ( "fmt" + "time" "github.com/juju/errors" . "github.com/pingcap/check" @@ -34,6 +35,32 @@ var _ = Suite(&testAnalyzeSuite{}) type testAnalyzeSuite struct { } +// CBOWithoutAnalyze tests the plan with stats that only have count info. +func (s *testAnalyzeSuite) TestCBOWithoutAnalyze(c *C) { + defer testleak.AfterTest(c)() + store, dom, err := newStoreWithBootstrapWithStatsLease(10 * time.Millisecond) + c.Assert(err, IsNil) + testKit := testkit.NewTestKit(c, store) + defer func() { + dom.Close() + store.Close() + }() + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t1 (a int)") + testKit.MustExec("create table t2 (a int)") + testKit.MustExec("insert into t1 values (1), (2), (3), (4), (5), (6)") + testKit.MustExec("insert into t2 values (1), (2), (3), (4), (5), (6)") + time.Sleep(1 * time.Second) + testKit.MustQuery("explain select * from t1, t2 where t1.a = t2.a").Check(testkit.Rows( + "TableScan_9 cop table:t1, range:(-inf,+inf), keep order:false 6", + "TableReader_10 HashLeftJoin_7 root data:TableScan_9 6", + "TableScan_11 cop table:t2, range:(-inf,+inf), keep order:false 6", + "TableReader_12 HashLeftJoin_7 root data:TableScan_11 6", + "HashLeftJoin_7 TableReader_10,TableReader_12 root inner join, small:TableReader_12, equal:[eq(test.t1.a, test.t2.a)] 7.499999999999999", + )) +} + func constructInsertSQL(i, n int) string { sql := "insert into t (a,b,c,e)values " for j := 0; j < n; j++ { @@ -307,3 +334,15 @@ func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) { dom, err := tidb.BootstrapSession(store) return store, dom, errors.Trace(err) } + +func newStoreWithBootstrapWithStatsLease(lease time.Duration) (kv.Storage, *domain.Domain, error) { + store, err := tikv.NewMockTikvStore() + if err != nil { + return nil, nil, errors.Trace(err) + } + tidb.SetSchemaLease(0) + tidb.SetStatsLease(lease) + domain.RunAutoAnalyze = false + dom, err := tidb.BootstrapSession(store) + return store, dom, errors.Trace(err) +} diff --git a/plan/new_physical_plan_builder.go b/plan/new_physical_plan_builder.go index a0f59658849c..84ca0fc0d671 100644 --- a/plan/new_physical_plan_builder.go +++ b/plan/new_physical_plan_builder.go @@ -1040,6 +1040,7 @@ func (p *basePhysicalPlan) getChildrenPossibleProps(prop *requiredProp) [][]*req } func (p *PhysicalHashJoin) getChildrenPossibleProps(prop *requiredProp) [][]*requiredProp { + p.expectedCnt = prop.expectedCnt if !prop.isEmpty() { return nil } diff --git a/plan/stats.go b/plan/stats.go index a81a69c634e2..e36c4b9cc3b9 100644 --- a/plan/stats.go +++ b/plan/stats.go @@ -74,7 +74,7 @@ func (p *DataSource) getStatsProfileByFilter(conds expression.CNFExprs) *statsPr } for i, col := range p.Columns { hist, ok := p.statisticTable.Columns[col.ID] - if ok { + if ok && hist.NDV > 0 { profile.cardinality[i] = float64(hist.NDV) } else { profile.cardinality[i] = profile.count * distinctFactor @@ -242,7 +242,7 @@ func (p *LogicalJoin) prepareStatsProfile() *statsProfile { } leftKeyCardinality := getCardinality(leftKeys, p.children[0].Schema(), leftProfile) rightKeyCardinality := getCardinality(rightKeys, p.children[1].Schema(), rightProfile) - count := (leftProfile.count * rightProfile.count / leftKeyCardinality / rightKeyCardinality) * math.Min(leftKeyCardinality, rightKeyCardinality) + count := leftProfile.count * rightProfile.count / math.Max(leftKeyCardinality, rightKeyCardinality) if p.JoinType == LeftOuterJoin { count = math.Max(count, leftProfile.count) } else if p.JoinType == RightOuterJoin {