All Products
Search
Document Center

PolarDB:Cost-based query transformation

Last Updated:Mar 28, 2026

PolarDB for MySQL uses cost-based query transformation (CBQT) to rewrite complex queries into equivalent forms with lower execution cost. Unlike rule-based transformations that apply unconditionally, CBQT evaluates multiple rewriting strategies and selects the one with the lowest estimated cost—taking into account cardinality, access methods, and join order.

Prerequisites

Before using CBQT, verify your cluster version:

  • Old version of CBQT: PolarDB for MySQL 8.0.2, revision 8.0.2.2.0 or later

  • New version of CBQT: PolarDB for MySQL 8.0.2, revision 8.0.2.2.19 or later

To check your revision version, see Query the engine version.

How it works

Query transformation rewrites one SQL statement into an equivalent SQL statement. For example:

SELECT *
FROM d1
    JOIN f1 ON d1.c1 = f1.c1
    LEFT JOIN (
        SELECT d2.c2 AS d2_c2, f2.c3 AS f2_c3
        FROM d2, f2
        WHERE d2.c1 = f2.c1
    ) derived
    ON derived.d2_c2 = d1.c2
        AND derived.f2_c3 = f1.c3;

After materialized table merging, this becomes:

SELECT *
FROM d1
    JOIN f1 ON d1.c1 = f1.c1
    LEFT JOIN (d2
        JOIN f2 ON TRUE)
    ON d2.c1 = f2.c1
        AND f2.c3 = f1.c3
        AND d2.c2 = d1.c2;

MySQL Community Edition applies this transformation unconditionally based on equivalence rules—without checking whether it actually reduces cost. If the indexes of d1/f1 and d2/f2 are not correlated, merging forces each row from the d1-f1 join to trigger a separate d2-f2 join, which can significantly increase execution time.

CBQT solves this by building a state space of all possible transformation combinations before committing to any. For two candidate transformations A and B, the state space contains four combinations: None, A only, B only, and AB. CBQT estimates the execution cost of each combination and selects the lowest-cost plan.

image

For the materialized table merging example, the execution plan after merging looks like this:

image

The execution plan before merging looks like this:

image

Whether the merged plan is better depends on whether the indexes of d2/f2 and d1/f1 are correlated, and on the size of the join result set. CBQT calculates both costs and applies the transformation only if it reduces cost.

Transformations that always reduce cost are treated as rule-based transformations and applied whenever the relevant conditions are met.

Key concepts

Nested query and nesting depth

A nested query is a query block nested inside another query. The inner query block is the subquery (also called the inner query); the enclosing block is the parent query (also called the outer query). Nesting depth counts the number of nested layers.

Example with nesting depth 2:

SELECT * FROM t1 WHERE t1.a IN (SELECT t2.b FROM t2 WHERE t2.c = t1.b);

The UNION operator combines statements at the same nesting level. The following query has nesting depth 3:

SELECT *
FROM t1
WHERE t1.a IN (
    SELECT dt.b
    FROM (
        SELECT b, c FROM t2
        UNION
        SELECT b, c FROM t3
    ) dt
    WHERE dt.c = t1.b
);

Transformation

The process of rewriting a query into an equivalent form, such as the materialized table merging shown above.

Transformation object

The query element that a transformation rule applies to. For materialized table merging, the transformation object is the materialized table. For the subquery-to-SEMI JOIN transformation, it is the subquery.

Iteration

Each pass over all transformation objects is one iteration. After a transformation is applied, new objects may become eligible for further transformation—for example, a subquery that becomes a materialized table can then be merged. CBQT repeats until no new transformations are possible or until the iteration limit is reached.

Enable and configure CBQT

Two parameters control CBQT globally:

ParameterLevelDefaultDescription
cbqt_enabledGlobal and sessionONEnables or disables CBQT. Valid values: ON, OFF, REPLICA_ON (enables CBQT on read-only nodes only)
cbqt_versionGlobal and session1Selects the CBQT version. 1 = old version, 2 = new version

Old version of CBQT

The old version is available from revision 8.0.2.2.0.

CBQT can take time to evaluate all combinations in the state space. To protect short-lived queries, set a cost threshold: CBQT only activates when a query's estimated cost exceeds cbqt_cost_threshold.

For correlated subqueries that MySQL Community Edition does not support unnesting, the old version decorrelates them using GROUP BY clauses.

For transformations that MySQL Community Edition applies unconditionally, you can opt into cost-based evaluation. The derived merge feature is controlled by the derived_merge_cost_based flag in polar_optimizer_switch.

Parameters

ParameterLevelDefaultDescription
cbqt_cost_thresholdGlobal and session100000The minimum query cost that triggers CBQT. CBQT is skipped for queries with lower estimated costs. Valid values: 0–18446744073709551615
cbqt_timeoutGlobal and session200 msThe maximum time CBQT spends searching for the optimal plan. If the timeout expires, CBQT uses the best plan found so far. Set to 0 to disable the timeout. Valid values: 0–18446744073709551615
polar_optimizer_switchGlobal and sessionA set of flags that control individual optimization features. See the following table for valid flags

`polar_optimizer_switch` flags:

FlagDefaultDescription
unnest_use_window_functionONDecorrelates subqueries using window functions
unnest_use_group_byONDecorrelates subqueries using GROUP BY clauses (cost-based)
derived_merge_cost_basedOFFApplies derived merge based on cost rather than unconditionally

New version of CBQT

The new version is available from revision 8.0.2.2.19 and introduces finer-grained control over iteration count, nesting depth, search strategy, and individual transformation rules.

Parameters

ParameterLevelDefaultDescription
cbqt_iteration_limitGlobal1The maximum number of CBQT iterations. Higher values increase the chance of finding the optimal plan but consume more time. Valid values: 1–10
cbqt_max_nested_levelGlobal and session5The maximum nesting depth CBQT processes. Queries deeper than this value are not transformed. Valid values: 1–64
cbqt_search_strategyGlobalautoThe strategy for selecting the optimal plan. See the following table for valid values
cbqt_rule_switchGlobal and sessionA set of flags that enable or disable individual transformation rules. See the following table for valid flags

`cbqt_search_strategy` values:

ValueDescription
autoAdaptive search (default). PolarDB selects linear or twoPass based on the query
linearCompares execution cost before and after each transformation independently and keeps the better plan
twoPassCompares the cost of applying all transformations uniformly versus applying none, then selects the better approach

`cbqt_rule_switch` flags (all default `ON`):

FlagDescription
merge_derivedCost-based materialized table merging
subquery_to_derivedCost-based transformation from subquery to materialized table
unnest_subquery_by_groupbyCost-based decorrelation using GROUP BY clauses
unnest_subquery_by_windowsDecorrelation using window functions
derived_projection_pruningMaterialized table projection pruning
cond_pushdownCondition pushdown
heuristic_merge_derivedHeuristic materialized table merging
subquery_to_semijoinTransformation from subquery to SEMI JOIN
heuristic_coalesce_subqueryHeuristic subquery folding
coalesce_subqueryCost-based subquery folding