Skip to content

Commit 5b21261

Browse files
committed
A better selectivity estimation for compound index matches in the case of missing statistics
1 parent b0af516 commit 5b21261

File tree

1 file changed

+61
-61
lines changed

1 file changed

+61
-61
lines changed

src/jrd/optimizer/Retrieval.cpp

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,8 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
827827
unsigned scope) const
828828
{
829829
const double cardinality = csb->csb_rpt[stream].csb_cardinality;
830+
fb_assert(cardinality);
831+
const double minSelectivity = MIN(MAXIMUM_SELECTIVITY / cardinality, DEFAULT_SELECTIVITY);
830832

831833
// Walk through indexes to calculate selectivity / candidate
832834
MatchedBooleanList matches;
@@ -885,6 +887,15 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
885887
}
886888
}
887889

890+
auto selectivity = idx->idx_rpt[j].idx_selectivity;
891+
const auto useDefaultSelectivity = (selectivity <= 0);
892+
893+
// When the index selectivity is zero then the statement is prepared
894+
// on an empty table or the statistics aren't updated. So assume every
895+
// match to represent 1/10 of the maximum selectivity.
896+
if (useDefaultSelectivity)
897+
selectivity = MAX(scratch.selectivity * DEFAULT_SELECTIVITY, minSelectivity);
898+
888899
if (segment.scanType == segmentScanList)
889900
{
890901
if (listCount) // we cannot have more than one list matched to an index
@@ -907,10 +918,10 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
907918
// This is a perfect usable segment thus update root selectivity
908919
scratch.lowerCount++;
909920
scratch.upperCount++;
910-
scratch.selectivity = idx->idx_rpt[j].idx_selectivity;
911921
scratch.nonFullMatchedSegments = idx->idx_count - (j + 1);
912922
// Add matches for this segment to the main matches list
913923
matches.join(segment.matches);
924+
scratch.selectivity = selectivity;
914925

915926
// An equality scan for any unique index cannot retrieve more
916927
// than one row. The same is true for an equivalence scan for
@@ -931,6 +942,12 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
931942
// so we can stop looking further, because this is the best
932943
// one we can get.
933944
unique = true;
945+
946+
// If selectivity is assumed, a better guess for the unique match
947+
// would be 1 / cardinality
948+
if (useDefaultSelectivity)
949+
scratch.selectivity = minSelectivity;
950+
934951
break;
935952
}
936953

@@ -942,58 +959,53 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
942959
}
943960
else
944961
{
945-
// This is our last segment that we can use,
946-
// estimate the selectivity
947-
auto selectivity = scratch.selectivity;
948-
double factor = 1;
949-
950-
switch (segment.scanType)
962+
if (segment.scanType != segmentScanNone)
951963
{
952-
case segmentScanBetween:
953-
scratch.lowerCount++;
954-
scratch.upperCount++;
955-
selectivity = idx->idx_rpt[j].idx_selectivity;
956-
factor = REDUCE_SELECTIVITY_FACTOR_BETWEEN;
957-
break;
958-
959-
case segmentScanLess:
960-
scratch.upperCount++;
961-
selectivity = idx->idx_rpt[j].idx_selectivity;
962-
factor = REDUCE_SELECTIVITY_FACTOR_LESS;
963-
break;
964-
965-
case segmentScanGreater:
966-
scratch.lowerCount++;
967-
selectivity = idx->idx_rpt[j].idx_selectivity;
968-
factor = REDUCE_SELECTIVITY_FACTOR_GREATER;
969-
break;
970-
971-
case segmentScanStarting:
972-
case segmentScanEqual:
973-
case segmentScanEquivalent:
974-
scratch.lowerCount++;
975-
scratch.upperCount++;
976-
selectivity = idx->idx_rpt[j].idx_selectivity;
977-
factor = REDUCE_SELECTIVITY_FACTOR_STARTING;
978-
break;
964+
// This is our last segment that we can use,
965+
// estimate the selectivity
966+
double factor = 1;
979967

980-
default:
981-
fb_assert(segment.scanType == segmentScanNone);
982-
break;
983-
}
968+
switch (segment.scanType)
969+
{
970+
case segmentScanBetween:
971+
scratch.lowerCount++;
972+
scratch.upperCount++;
973+
factor = REDUCE_SELECTIVITY_FACTOR_BETWEEN;
974+
break;
975+
976+
case segmentScanLess:
977+
scratch.upperCount++;
978+
factor = REDUCE_SELECTIVITY_FACTOR_LESS;
979+
break;
980+
981+
case segmentScanGreater:
982+
scratch.lowerCount++;
983+
factor = REDUCE_SELECTIVITY_FACTOR_GREATER;
984+
break;
985+
986+
case segmentScanStarting:
987+
case segmentScanEqual:
988+
case segmentScanEquivalent:
989+
scratch.lowerCount++;
990+
scratch.upperCount++;
991+
factor = REDUCE_SELECTIVITY_FACTOR_STARTING;
992+
break;
993+
994+
default:
995+
fb_assert(false);
996+
break;
997+
}
984998

985-
// Adjust the compound selectivity using the reduce factor.
986-
// It should be better than the previous segment but worse
987-
// than a full match.
988-
const double diffSelectivity = scratch.selectivity - selectivity;
989-
selectivity += (diffSelectivity * factor);
990-
fb_assert(selectivity <= scratch.selectivity);
991-
scratch.selectivity = selectivity;
999+
// Adjust the compound selectivity using the reduce factor.
1000+
// It should be better than the previous segment but worse
1001+
// than a full match.
1002+
const double diffSelectivity = scratch.selectivity - selectivity;
1003+
selectivity += (diffSelectivity * factor);
1004+
fb_assert(selectivity <= scratch.selectivity);
1005+
scratch.selectivity = selectivity;
9921006

993-
if (segment.scanType != segmentScanNone)
994-
{
995-
matches.join(segment.matches);
9961007
scratch.nonFullMatchedSegments = idx->idx_count - j;
1008+
matches.join(segment.matches);
9971009
}
9981010

9991011
break;
@@ -1003,19 +1015,7 @@ void Retrieval::getInversionCandidates(InversionCandidateList& inversions,
10031015
if (scratch.scopeCandidate)
10041016
{
10051017
double selectivity = scratch.selectivity;
1006-
1007-
// When the index selectivity is zero then the statement is prepared
1008-
// on an empty table or the statistics aren't updated. The priorly
1009-
// calculated selectivity is meaningless in this case. So instead:
1010-
// - for an unique match, estimate the selectivity via the stream cardinality;
1011-
// - for a non-unique one, assume 1/10 of the maximum selectivity, so that
1012-
// at least some indexes could be utilized by the optimizer.
1013-
if (idx->idx_selectivity <= 0)
1014-
{
1015-
selectivity = unique ?
1016-
MIN(MAXIMUM_SELECTIVITY / cardinality, DEFAULT_SELECTIVITY) :
1017-
DEFAULT_SELECTIVITY;
1018-
}
1018+
fb_assert(selectivity);
10191019

10201020
// Calculate the cost (only index pages) for this index
10211021
auto cost = DEFAULT_INDEX_COST + selectivity * scratch.cardinality;

0 commit comments

Comments
 (0)