diff --git a/statistics/histogram.go b/statistics/histogram.go index a7730d07d975d..f56072ffb29f1 100644 --- a/statistics/histogram.go +++ b/statistics/histogram.go @@ -682,9 +682,8 @@ func (hg *Histogram) outOfRange(val types.Datum) bool { if hg.Bounds == nil { return true } - len := hg.Bounds.NumRows() return chunk.Compare(hg.Bounds.GetRow(0), 0, &val) > 0 || - chunk.Compare(hg.Bounds.GetRow(len-1), 0, &val) < 0 + chunk.Compare(hg.Bounds.GetRow(hg.Bounds.NumRows()-1), 0, &val) < 0 } // ErrorRate is the error rate of estimate row count by bucket and cm sketch. @@ -857,3 +856,22 @@ func (idx *Index) getRowCount(sc *stmtctx.StatementContext, indexRanges []*range } return totalCount, nil } + +func (idx *Index) outOfRange(val types.Datum) bool { + if idx.Bounds == nil { + return true + } + withInLowBoundOrPrefixMatch := chunk.Compare(idx.Bounds.GetRow(0), 0, &val) <= 0 || + matchPrefix(idx.Bounds.GetRow(0), 0, &val) + withInHighBound := chunk.Compare(idx.Bounds.GetRow(idx.Bounds.NumRows()-1), 0, &val) >= 0 + return !withInLowBoundOrPrefixMatch || !withInHighBound +} + +// matchPrefix checks whether ad is the prefix of value +func matchPrefix(row chunk.Row, colIdx int, ad *types.Datum) bool { + switch ad.Kind() { + case types.KindString, types.KindBytes, types.KindBinaryLiteral, types.KindMysqlBit: + return strings.HasPrefix(row.GetString(colIdx), ad.GetString()) + } + return false +} diff --git a/statistics/selectivity_test.go b/statistics/selectivity_test.go index 09221d6615240..ed24d0b97f326 100644 --- a/statistics/selectivity_test.go +++ b/statistics/selectivity_test.go @@ -229,6 +229,18 @@ func (s *testSelectivitySuite) TestDiscreteDistribution(c *C) { "└─IndexScan_8 0.00 cop table:t, index:a, b, range:[\"tw\" -inf,\"tw\" 0), keep order:false")) } +func (s *testSelectivitySuite) TestSelectCombinedLowBound(c *C) { + testKit := testkit.NewTestKit(c, s.store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t(id int auto_increment, kid int, pid int, primary key(id), key(kid, pid))") + testKit.MustExec("insert into t (kid, pid) values (1,2), (1,3), (1,4),(1, 11), (1, 12), (1, 13), (1, 14), (2, 2), (2, 3), (2, 4)") + testKit.MustExec("analyze table t") + testKit.MustQuery("explain select * from t where kid = 1").Check(testkit.Rows( + "IndexReader_9 7.00 root index:IndexScan_8", + "└─IndexScan_8 7.00 cop table:t, index:kid, pid, range:[1,1], keep order:false")) +} + func getRange(start, end int64) []*ranger.Range { ran := &ranger.Range{ LowVal: []types.Datum{types.NewIntDatum(start)},