Skip to content

Commit

Permalink
plan: remove other accessPaths if one is unique key and full matche…
Browse files Browse the repository at this point in the history
…d. (#6925) (#6966)
  • Loading branch information
winoros authored and zz-jason committed Jul 3, 2018
1 parent d341598 commit bfcea6d
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 13 deletions.
51 changes: 42 additions & 9 deletions plan/logical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,9 @@ type accessPath struct {
forced bool
}

func (ds *DataSource) deriveTablePathStats(path *accessPath) error {
// deriveTablePathStats will fulfill the information that the accessPath need.
// And it will check whether the primary key is covered only by point query.
func (ds *DataSource) deriveTablePathStats(path *accessPath) (bool, error) {
var err error
sc := ds.ctx.GetSessionVars().StmtCtx
path.countAfterAccess = float64(ds.statisticTable.Count)
Expand All @@ -339,22 +341,33 @@ func (ds *DataSource) deriveTablePathStats(path *accessPath) error {
}
if pkCol == nil {
path.ranges = ranger.FullIntNewRange(false)
return nil
return false, nil
}
path.ranges = ranger.FullIntNewRange(mysql.HasUnsignedFlag(pkCol.RetType.Flag))
if len(ds.pushedDownConds) == 0 {
return nil
return false, nil
}
path.accessConds, path.tableFilters = ranger.DetachCondsForTableRange(ds.ctx, ds.pushedDownConds, pkCol)
path.ranges, err = ranger.BuildTableRange(path.accessConds, sc, pkCol.RetType)
if err != nil {
return errors.Trace(err)
return false, errors.Trace(err)
}
path.countAfterAccess, err = ds.statisticTable.GetRowCountByIntColumnRanges(sc, pkCol.ID, path.ranges)
return errors.Trace(err)
// Check whether the primary key is covered by point query.
noIntervalRange := true
for _, ran := range path.ranges {
if !ran.IsPoint(sc) {
noIntervalRange = false
break
}
}
return noIntervalRange, errors.Trace(err)
}

func (ds *DataSource) deriveIndexPathStats(path *accessPath) error {
// deriveIndexPathStats will fulfill the information that the accessPath need.
// And it will check whether this index is full matched by point query. We will use this check to
// determine whether we remove other paths or not.
func (ds *DataSource) deriveIndexPathStats(path *accessPath) (bool, error) {
var err error
sc := ds.ctx.GetSessionVars().StmtCtx
path.ranges = ranger.FullNewRange()
Expand All @@ -363,11 +376,11 @@ func (ds *DataSource) deriveIndexPathStats(path *accessPath) error {
if len(idxCols) != 0 {
path.ranges, path.accessConds, path.indexFilters, path.eqCondCount, err = ranger.DetachCondAndBuildRangeForIndex(ds.ctx, ds.pushedDownConds, idxCols, lengths)
if err != nil {
return errors.Trace(err)
return false, errors.Trace(err)
}
path.countAfterAccess, err = ds.statisticTable.GetRowCountByIndexRanges(sc, path.index.ID, path.ranges)
if err != nil {
return errors.Trace(err)
return false, errors.Trace(err)
}
path.indexFilters, path.tableFilters = splitIndexFilterConditions(path.indexFilters, path.index.Columns, ds.tableInfo)
} else {
Expand All @@ -382,7 +395,27 @@ func (ds *DataSource) deriveIndexPathStats(path *accessPath) error {
}
path.countAfterIndex = math.Max(path.countAfterAccess*selectivity, ds.statsAfterSelect.count)
}
return nil
// Check whether there's only point query.
noIntervalRanges := true
haveNullVal := false
for _, ran := range path.ranges {
// Not point or the not full matched.
if !ran.IsPoint(sc) || len(ran.HighVal) != len(path.index.Columns) {
noIntervalRanges = false
break
}
// Check whether there's null value.
for i := 0; i < len(path.index.Columns); i++ {
if ran.HighVal[i].IsNull() {
haveNullVal = true
break
}
}
if haveNullVal {
break
}
}
return noIntervalRanges && !haveNullVal, nil
}

func (ds *DataSource) getPKIsHandleCol() *expression.Column {
Expand Down
16 changes: 14 additions & 2 deletions plan/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,28 @@ func (ds *DataSource) deriveStats() (*statsInfo, error) {
ds.statsAfterSelect = ds.getStatsByFilter(ds.pushedDownConds)
for _, path := range ds.possibleAccessPaths {
if path.isTablePath {
err := ds.deriveTablePathStats(path)
noIntervalRanges, err := ds.deriveTablePathStats(path)
if err != nil {
return nil, errors.Trace(err)
}
// If there's only point range. Just remove other possible paths.
if noIntervalRanges {
ds.possibleAccessPaths[0] = path
ds.possibleAccessPaths = ds.possibleAccessPaths[:1]
break
}
continue
}
err := ds.deriveIndexPathStats(path)
noIntervalRanges, err := ds.deriveIndexPathStats(path)
if err != nil {
return nil, errors.Trace(err)
}
// If there's only point range and this index is unique key. Just remove other possible paths.
if noIntervalRanges && path.index.Unique {
ds.possibleAccessPaths[0] = path
ds.possibleAccessPaths = ds.possibleAccessPaths[:1]
break
}
}
return ds.statsAfterSelect, nil
}
Expand Down
4 changes: 2 additions & 2 deletions statistics/selectivity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ func (s *testSelectivitySuite) TestPseudoSelectivity(c *C) {
testKit.MustExec("drop table if exists t, t1")
testKit.MustExec("create table t(a int, b int, unique key idx(a,b))")
testKit.MustQuery("explain select * from t where a = 1 and b = 1").Check(testkit.Rows(
"IndexScan_8 cop table:t, index:a, b, range:[1 1,1 1], keep order:false 1.00",
"IndexReader_9 root index:IndexScan_8 1.00"))
"IndexScan_5 cop table:t, index:a, b, range:[1 1,1 1], keep order:false 1.00",
"IndexReader_6 root index:IndexScan_5 1.00"))

testKit.MustExec("create table t1(a int, b int, primary key(a))")
testKit.MustQuery("explain select b from t1 where a = 1").Check(testkit.Rows(
Expand Down

0 comments on commit bfcea6d

Please sign in to comment.