mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
Improve estimation of OR clauses using extended statistics.
Formerly we only applied extended statistics to an OR clause as part of the clauselist_selectivity() code path for an OR clause appearing in an implicitly-ANDed list of clauses. This meant that it could only use extended statistics if all sub-clauses of the OR clause were covered by a single extended statistics object. Instead, teach clause_selectivity() how to apply extended statistics to an OR clause by handling its ORed list of sub-clauses in a similar manner to an implicitly-ANDed list of sub-clauses, but with different combination rules. This allows one or more extended statistics objects to be used to estimate all or part of the list of sub-clauses. Any remaining sub-clauses are then treated as if they are independent. Additionally, to avoid double-application of extended statistics, this introduces "extended" versions of clause_selectivity() and clauselist_selectivity(), which include an option to ignore extended statistics. This replaces the old clauselist_selectivity_simple() function which failed to completely ignore extended statistics when called from the extended statistics code. A known limitation of the current infrastructure is that an AND clause under an OR clause is not treated as compatible with extended statistics (because we don't build RestrictInfos for such sub-AND clauses). Thus, for example, "(a=1 AND b=1) OR (a=2 AND b=2)" will currently be treated as two independent AND clauses (each of which may be estimated using extended statistics), but extended statistics will not currently be used to account for any possible overlap between those clauses. Improving that is left as a task for the future. Original patch by Tomas Vondra, with additional improvements by me. Discussion: https://postgr.es/m/20200113230008.g67iyk4cs3xbnjju@development
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/fmgrprotos.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/selfuncs.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "utils/typcache.h"
|
||||
|
||||
@@ -1888,16 +1889,80 @@ mcv_get_match_bitmap(PlannerInfo *root, List *clauses,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* mcv_combine_selectivities
|
||||
* Combine per-column and multi-column MCV selectivity estimates.
|
||||
*
|
||||
* simple_sel is a "simple" selectivity estimate (produced without using any
|
||||
* extended statistics, essentially assuming independence of columns/clauses).
|
||||
*
|
||||
* mcv_sel and mcv_basesel are sums of the frequencies and base frequencies of
|
||||
* all matching MCV items. The difference (mcv_sel - mcv_basesel) is then
|
||||
* essentially interpreted as a correction to be added to simple_sel, as
|
||||
* described below.
|
||||
*
|
||||
* mcv_totalsel is the sum of the frequencies of all MCV items (not just the
|
||||
* matching ones). This is used as an upper bound on the portion of the
|
||||
* selectivity estimates not covered by the MCV statistics.
|
||||
*
|
||||
* Note: While simple and base selectivities are defined in a quite similar
|
||||
* way, the values are computed differently and are not therefore equal. The
|
||||
* simple selectivity is computed as a product of per-clause estimates, while
|
||||
* the base selectivity is computed by adding up base frequencies of matching
|
||||
* items of the multi-column MCV list. So the values may differ for two main
|
||||
* reasons - (a) the MCV list may not cover 100% of the data and (b) some of
|
||||
* the MCV items did not match the estimated clauses.
|
||||
*
|
||||
* As both (a) and (b) reduce the base selectivity value, it generally holds
|
||||
* that (simple_sel >= mcv_basesel). If the MCV list covers all the data, the
|
||||
* values may be equal.
|
||||
*
|
||||
* So, other_sel = (simple_sel - mcv_basesel) is an estimate for the part not
|
||||
* covered by the MCV list, and (mcv_sel - mcv_basesel) may be seen as a
|
||||
* correction for the part covered by the MCV list. Those two statements are
|
||||
* actually equivalent.
|
||||
*/
|
||||
Selectivity
|
||||
mcv_combine_selectivities(Selectivity simple_sel,
|
||||
Selectivity mcv_sel,
|
||||
Selectivity mcv_basesel,
|
||||
Selectivity mcv_totalsel)
|
||||
{
|
||||
Selectivity other_sel;
|
||||
Selectivity sel;
|
||||
|
||||
/* estimated selectivity of values not covered by MCV matches */
|
||||
other_sel = simple_sel - mcv_basesel;
|
||||
CLAMP_PROBABILITY(other_sel);
|
||||
|
||||
/* this non-MCV selectivity cannot exceed 1 - mcv_totalsel */
|
||||
if (other_sel > 1.0 - mcv_totalsel)
|
||||
other_sel = 1.0 - mcv_totalsel;
|
||||
|
||||
/* overall selectivity is the sum of the MCV and non-MCV parts */
|
||||
sel = mcv_sel + other_sel;
|
||||
CLAMP_PROBABILITY(sel);
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* mcv_clauselist_selectivity
|
||||
* Return the selectivity estimate computed using an MCV list.
|
||||
* Use MCV statistics to estimate the selectivity of an implicitly-ANDed
|
||||
* list of clauses.
|
||||
*
|
||||
* First builds a bitmap of MCV items matching the clauses, and then sums
|
||||
* the frequencies of matching items.
|
||||
* This determines which MCV items match every clause in the list and returns
|
||||
* the sum of the frequencies of those items.
|
||||
*
|
||||
* It also produces two additional interesting selectivities - total
|
||||
* selectivity of all the MCV items (not just the matching ones), and the
|
||||
* base frequency computed on the assumption of independence.
|
||||
* In addition, it returns the sum of the base frequencies of each of those
|
||||
* items (that is the sum of the selectivities that each item would have if
|
||||
* the columns were independent of one another), and the total selectivity of
|
||||
* all the MCV items (not just the matching ones). These are expected to be
|
||||
* used together with a "simple" selectivity estimate (one based only on
|
||||
* per-column statistics) to produce an overall selectivity estimate that
|
||||
* makes use of both per-column and multi-column statistics --- see
|
||||
* mcv_combine_selectivities().
|
||||
*/
|
||||
Selectivity
|
||||
mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat,
|
||||
@@ -1928,7 +1993,6 @@ mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat,
|
||||
|
||||
if (matches[i] != false)
|
||||
{
|
||||
/* XXX Shouldn't the basesel be outside the if condition? */
|
||||
*basesel += mcv->items[i].base_frequency;
|
||||
s += mcv->items[i].frequency;
|
||||
}
|
||||
@@ -1936,3 +2000,94 @@ mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat,
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* mcv_clause_selectivity_or
|
||||
* Use MCV statistics to estimate the selectivity of a clause that
|
||||
* appears in an ORed list of clauses.
|
||||
*
|
||||
* As with mcv_clauselist_selectivity() this determines which MCV items match
|
||||
* the clause and returns both the sum of the frequencies and the sum of the
|
||||
* base frequencies of those items, as well as the sum of the frequencies of
|
||||
* all MCV items (not just the matching ones) so that this information can be
|
||||
* used by mcv_combine_selectivities() to produce a selectivity estimate that
|
||||
* makes use of both per-column and multi-column statistics.
|
||||
*
|
||||
* Additionally, we return information to help compute the overall selectivity
|
||||
* of the ORed list of clauses assumed to contain this clause. This function
|
||||
* is intended to be called for each clause in the ORed list of clauses,
|
||||
* allowing the overall selectivity to be computed using the following
|
||||
* algorithm:
|
||||
*
|
||||
* Suppose P[n] = P(C[1] OR C[2] OR ... OR C[n]) is the combined selectivity
|
||||
* of the first n clauses in the list. Then the combined selectivity taking
|
||||
* into account the next clause C[n+1] can be written as
|
||||
*
|
||||
* P[n+1] = P[n] + P(C[n+1]) - P((C[1] OR ... OR C[n]) AND C[n+1])
|
||||
*
|
||||
* The final term above represents the overlap between the clauses examined so
|
||||
* far and the (n+1)'th clause. To estimate its selectivity, we track the
|
||||
* match bitmap for the ORed list of clauses examined so far and examine its
|
||||
* intersection with the match bitmap for the (n+1)'th clause.
|
||||
*
|
||||
* We then also return the sums of the MCV item frequencies and base
|
||||
* frequencies for the match bitmap intersection corresponding to the overlap
|
||||
* term above, so that they can be combined with a simple selectivity estimate
|
||||
* for that term.
|
||||
*
|
||||
* The parameter "or_matches" is an in/out parameter tracking the match bitmap
|
||||
* for the clauses examined so far. The caller is expected to set it to NULL
|
||||
* the first time it calls this function.
|
||||
*/
|
||||
Selectivity
|
||||
mcv_clause_selectivity_or(PlannerInfo *root, StatisticExtInfo *stat,
|
||||
MCVList *mcv, Node *clause, bool **or_matches,
|
||||
Selectivity *basesel, Selectivity *overlap_mcvsel,
|
||||
Selectivity *overlap_basesel, Selectivity *totalsel)
|
||||
{
|
||||
Selectivity s = 0.0;
|
||||
bool *new_matches;
|
||||
int i;
|
||||
|
||||
/* build the OR-matches bitmap, if not built already */
|
||||
if (*or_matches == NULL)
|
||||
*or_matches = palloc0(sizeof(bool) * mcv->nitems);
|
||||
|
||||
/* build the match bitmap for the new clause */
|
||||
new_matches = mcv_get_match_bitmap(root, list_make1(clause), stat->keys,
|
||||
mcv, false);
|
||||
|
||||
/*
|
||||
* Sum the frequencies for all the MCV items matching this clause and also
|
||||
* those matching the overlap between this clause and any of the preceding
|
||||
* clauses as described above.
|
||||
*/
|
||||
*basesel = 0.0;
|
||||
*overlap_mcvsel = 0.0;
|
||||
*overlap_basesel = 0.0;
|
||||
*totalsel = 0.0;
|
||||
for (i = 0; i < mcv->nitems; i++)
|
||||
{
|
||||
*totalsel += mcv->items[i].frequency;
|
||||
|
||||
if (new_matches[i])
|
||||
{
|
||||
s += mcv->items[i].frequency;
|
||||
*basesel += mcv->items[i].base_frequency;
|
||||
|
||||
if ((*or_matches)[i])
|
||||
{
|
||||
*overlap_mcvsel += mcv->items[i].frequency;
|
||||
*overlap_basesel += mcv->items[i].base_frequency;
|
||||
}
|
||||
}
|
||||
|
||||
/* update the OR-matches bitmap for the next clause */
|
||||
(*or_matches)[i] = (*or_matches)[i] || new_matches[i];
|
||||
}
|
||||
|
||||
pfree(new_matches);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user