Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plan: remove other accessPaths if one is unique key and full matched. (#6925) #6966

Merged
merged 4 commits into from
Jul 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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