代码之家  ›  专栏  ›  技术社区  ›  waxwing

在Oracle中添加冗余联接条件会导致不同的计划

  •  5
  • waxwing  · 技术社区  · 15 年前

    我有一个常见的数据库连接情况,涉及三个表。一个表A是主键为的主表 id . 表B和C包含条目和A的辅助数据,并且每个都有一个名为 身份证件 它是指向a的外键。 身份证件

    SELECT *
    FROM A
    INNER JOIN B
    ON B.id = A.id
    INNER JOIN C
    ON C.id = A.id
    

    这当然非常有效。

    最近,我们的DBA告诉我们,这在Oracle中效率低下,您还需要在C和B之间加入条件,如下所示:

    SELECT *
    FROM A
    INNER JOIN B
    ON B.id = A.id
    INNER JOIN C
    ON C.id = A.id AND C.id = B.id
    

    有人能解释为什么这个额外的条件会产生不同吗?对我来说,C和B甚至没有关系。还要注意的是,如果去掉另一个连接条件,它同样糟糕——它们都需要存在。

    4 回复  |  直到 15 年前
        1
  •  3
  •   Thilo    15 年前

    有趣的

    Oracle似乎只有在某些情况下才能推断出这种传递等式:他们称之为 Transitive Closure 当启用查询重写时,您应该能够从中受益。

    但为了安全起见,最好自己解释一下多余的谓词。

        2
  •  2
  •   Steve Broberg    15 年前

    Oracle的optimizer没有对平等性做出可传递的假设。虽然我们知道如果A=B和A=C,那么B=C,Oracle并不认为B和C之间存在关系;C除非WHERE条款或连接条件中明确规定。

    我假设您对A、B和/或C有其他约束(而不是只选择表的全部内容-否则您的I/O不会太低,除非您的表很小,在这一点上优化是没有意义的)。因此,对A、B&的限制确实更多;C比你指定的要多。Oracle的优化器将查看FROM子句中的所有表,在WHERE子句中列出针对这些表的约束,然后根据这些表的索引确定约束的选择性。然后,它将检查各种攻击计划的排列,并确定哪种计划产生的希望最大(这些是您在计划中看到的基数值)。如果没有B=C条件,它将排除从B开始到C的计划(反之亦然),这些可能是最好的计划。

        3
  •  2
  •   Gary Myers    15 年前

    你有两个问题。

    首先,使用原始SQL,优化器估计A中与B中的ID匹配的行数,而B中也有与C中的行匹配的行数。该估计不准确,并且选择了错误的计划。

    现在,添加冗余条件。Oracle假定没有任何条件是真正冗余的(如果它们是冗余的,智能开发人员就不会包含它们)。它还假设每个条件独立于其他条件。例如,一个select where hair='BLAD'可能获得表格的10%,一个select where SEQUENCE='F'可能获得表格的50%。甲骨文会假设,如果选择头发=‘秃头’和性别=‘F’,则会给出5%(而事实上,秃头主要限于男性)。

    通过添加“冗余”谓词,Oracle将高估要排除的数量或行,并相应地选择计划。

    如果使用冗余谓词,Oracle选择了更好的计划,则表明原始查询的估计值高估了匹配的行数。冗余谓词是用低估来对抗这一点。在这种情况下,两个错误就是一个正确。

    另外,我假设所有ID的数据类型都是一致的。如果B.ID和C.ID是日期,A.ID是字符,反之亦然,则可能有一些行,其中A.ID=B.ID和A.ID=C.ID,但B.ID!=C.ID,因为隐式转换可能会丢失时间戳。

        4
  •  1
  •   mechanical_meat    15 年前

    这两个问题在我看来根本不一样。
    再说一次,我不是Oracle优化器。

    因为当你这样做的时候,B和C都有A的外键

    INNER JOIN B
    ON B.id = A.id
    

    唯一地 如第一个查询中所示,创建表A。

    在第二个查询中,通过将表C连接到更小的数据集(即A和B的交集),对问题进行了优化, 较小的数据集,即A和C的交集。