在 MySQL 中,如何为嵌套 WHERE IN 指定优化器提示?

问题描述 投票:0回答:1

我一直在学习 MySQL 中的优化器提示(我的版本是 8.0.36),并且我已经能够在非常简单的场景中尝试它们并且它有效。然而,一旦我添加嵌套级别,一切都会崩溃。

例如,假设我有 4 张桌子:

  • activities
    id
    rubric_id
    group_id
  • rubric_templates
    id
    title
  • rubric_sessions
    id
    template_id
    name
  • rubric_session_elements
    id
    rubric_session_id
    value

所有 id 都已建立外键。对于给定的评估规会话有许多评估规会话元素,并且对于给定的评估规模板有许多评估规会话。许多活动可以使用相同的标题,并且一个小组中有许多活动。所以任何地方都没有唯一的键。一切都是多对一。

如果我指示优化器在只有两个表的情况下使用

DUPSWEEDOUT
MATERIALIZATION
,它会起作用:

-- Get all rubric sessions for a group
EXPLAIN FORMAT=TREE
SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) */ rs.*
FROM rubric_sessions rs
WHERE rs.template_id IN (
    SELECT /*+ QB_NAME(subq1) */ a.rubric_template_id
    FROM activities a
    WHERE group_id = 123
);

/*
-> Nested loop inner join  (cost=24.6 rows=48)
    -> Filter: (`<subquery2>`.rubric_template_id is not null)  (cost=0.00417..0.00417 rows=1)
        -> Table scan on <subquery2>  (cost=3.46..3.46 rows=1)
            -> Materialize with deduplication  (cost=0.951..0.951 rows=1)
                -> Filter: (a.rubric_template_id is not null)  (cost=0.851 rows=1)
                    -> Index lookup on a using group_id (group_id=123)  (cost=0.851 rows=1)
    -> Index lookup on rs using template_id (template_id=`<subquery2>`.rubric_template_id), with index condition: (rs.template_id = `<subquery2>`.rubric_template_id)  (cost=24.6 rows=48)
*/

-- Replace MATERIALIZATION with DUPSWEEDOUT changes the plan to this:

/*
-> Remove duplicate rs rows using temporary table (weedout)  (cost=25.4 rows=48)
    -> Nested loop inner join  (cost=25.4 rows=48)
        -> Filter: (a.rubric_template_id is not null)  (cost=0.851 rows=1)
            -> Index lookup on a using group_id (group_id=123)  (cost=0.851 rows=1)
        -> Index lookup on rs using template_id (template_id=a.rubric_template_id), with index condition: (rs.template_id = a.rubric_template_id)  (cost=24.5 rows=48)
*/

但是,如果我想更进一步,它只会进行除草。它拒绝实现。

-- Get all rubric session elements for a group
EXPLAIN FORMAT=TREE
SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) */ rse.*
FROM rubric_session_elements rse
WHERE rse.rubric_session_id IN (
    SELECT rs.id
    FROM rubric_sessions rs
    WHERE rs.template_id IN (
        SELECT /*+ QB_NAME(subq1) */ a.rubric_template_id
        FROM activities a
        WHERE group_id = 123
    )
);

/*
-> Remove duplicate (rs, rse) rows using temporary table (weedout)  (cost=527 rows=572)
    -> Nested loop inner join  (cost=527 rows=572)
        -> Nested loop inner join  (cost=5.92 rows=48)
            -> Filter: (a.rubric_template_id is not null)  (cost=0.851 rows=1)
                -> Index lookup on a using group_id (group_id=123)  (cost=0.851 rows=1)
            -> Filter: (rs.template_id = a.rubric_template_id)  (cost=5.07 rows=48)
                -> Covering index lookup on rs using template_id (template_id=a.rubric_template_id)  (cost=5.07 rows=48)
        -> Index lookup on rse using rubric_session_elements_rubric_session_id_foreign (rubric_session_id=rs.id)  (cost=9.68 rows=11.9)
*/

无论我如何更改它,我都无法说服优化器使用 MATERIALIZATION。我已经在外部和中间选择上放置了半连接提示。我已将 QB_NAME 放在中间和内部选择上。我什至尝试过:

SET SESSION optimizer_switch = 'duplicateweedout=off';

但它仍然显示为“除草”。如果我指定

NO_SEMIJOIN(DUPSWEEDOUT)
那么它似乎会恢复到散列连接,但这不是物化。看起来我并没有违反https://dev.mysql.com/doc/refman/8.4/en/subquery-materialization.html中指定的规则,那么我做错了什么?

mysql query-optimization optimizer-hints
1个回答
0
投票

即使我们强制执行,MYSQL也会根据成本决定相关策略:MATERIALIZATION(或)DUPSWEEDOUT(根据MySQL手册中的SEMIJOIN Hint)。另一方面,还有另一个提示 SUBQUERY,您可以使用它,因为查询中存在嵌套子查询,它使用物化策略。

下面修改后的代码将使用 MATERILIZATION 策略:

EXPLAIN FORMAT=TREE

选择 /+ 子查询(@subq2 物化) / rse。 来自 rubric_session_elements rse WHERE rse.rubric_session_id IN ( 选择 /+ QB_NAME(subq2) / rs.id 来自 rubric_sessions rs WHERE rs.template_id IN ( SELECT /+ QB_NAME(subq1) */ a.rubric_template_id 来自活动a 其中 a.group_id = 123 ) );

如果您需要更多信息,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.