我有一个涉及三个表的公共数据库连接情况.一个表A是主表,主键名为id.表B和C包含条目和A的辅助数据,并且每个表还有一个名为的列id,它是指向A.的外键id.现在,如果我想在一个查询中获得A,B和C中的所有数据,我会写:
SELECT *
FROM A
INNER JOIN B
ON B.id = A.id
INNER JOIN C
ON C.id = A.id
Run Code Online (Sandbox Code Playgroud)
这当然是完美的.
最近,我们的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
Run Code Online (Sandbox Code Playgroud)
这对我来说看起来多余,所以我自然不相信这里.直到我遇到一个执行计划很糟糕的慢查询,并设法通过精确添加缺少的连接条件来修复它.我在两个版本上运行了解释计划:没有"冗余"查询条件的那个版本的成本为1 035,而"改进的"版本的成本为389(并且基数和字节也存在巨大差异).两个查询都产生完全相同的结果.
谁能解释为什么这个额外的条件有所作为?对我来说,C和B甚至没有关系.还要注意,如果你带走其他连接条件,它同样糟糕 - 它们都需要在那里.
你遇到的是两个问题。
首先,使用原始 SQL,优化器对 A 中与 B 中的 ID 匹配的行数进行估计,并且在 C 中也有匹配的行。估计不准确,并且选择了错误的计划。
现在,您添加冗余条件。Oracle 假设没有任何条件是真正多余的(因为如果有的话,聪明的开发人员不会包含它们)。它还假设每个条件独立于其他条件。例如,头发 = '秃头' 的选择可能会获得表的 10%,性别 = 'F' 的选择可能会获得 50%。Oracle 会假设头发 = '秃头' 且性别 = 'F' 的选择将给出 5%(而实际上秃头主要限于男性)。
通过添加“冗余”谓词,Oracle 将高估要排除的数量或行,并相应地选择计划。
如果 Oracle 使用冗余谓词选择更好的计划,则表明原始查询的估计高估了匹配的行数。冗余谓词通过低估来抵消这一点。在这种情况下,两个错误正在形成一个正确。
这不是我推荐的解决方案,但如果它有效......
附言。我假设所有 ID 的数据类型都是一致的。如果 B.ID 和 C.ID 是日期,A.ID 是字符,反之亦然,则可能有一些行 A.ID = B.ID 且 A.ID = C.ID 但 B.ID ! = C.ID,因为隐式转换可能会丢失时间戳。
| 归档时间: |
|
| 查看次数: |
1235 次 |
| 最近记录: |