4

查询:

SELECT tbl1.*
   FROM tbl1 
JOIN tbl2
     ON (tbl1.t1_pk  = tbl2.t2_fk_t1_pk
AND tbl2.t2_strt_dt <= sysdate
AND tbl2.t2_end_dt  >= sysdate)
JOIN tbl3 on (tbl3.t3_pk = tbl2.t2_fk_t3_pk
AND tbl3.t3_lkup_1 = 2577304
AND tbl3.t3_lkup_2 = 1220833)
where tbl2.t2_lkup_1   = 1020000002981587;

事实:

  • 甲骨文快捷版
  • tbl1.t1_pk 是主键。
  • tbl2.t2_fk_t1_pk 是该 t1_pk 列上的外键。
  • tbl2.t2_lkup_1 已编入索引。
  • tbl3.t3_pk 是主键。
  • tbl2.t2_fk_t3_pk 是该 t3_pk 列上的外键。

对 tbl1 中 11,000 行和 tbl2 中 3500 行的数据库解释计划表明它正在对 tbl1 进行全表扫描。在我看来,如果它可以在 tbl1 上进行索引查询,它应该会更快。

对 tbl1 中 11,000 行和 tbl2 中 3500 行的数据库解释计划表明它正在对 tbl1 进行全表扫描。在我看来,如果它可以在 tbl1 上进行索引查询,它应该会更快。

更新:我尝试了你们中的一些人建议的提示,但解释成本变得更糟了!现在我真的很困惑。

进一步更新:我终于可以访问生产数据库的副本,“解释计划”使用索引和成本低得多的查询显示它。我猜想拥有更多数据(tbl1 中超过 100,000 行,tbl2 中超过 50,000 行)是它决定索引值得的原因。感谢所有帮助过的人。我仍然认为 Oracle 性能调优是一门魔法,但我很高兴你们中的一些人理解它。

进一步更新:我应前雇主的要求更新了这个问题。他们不喜欢他们的表名出现在谷歌查询中。我应该知道的更好。

4

8 回答 8

5

简单的答案:因为优化器希望找到比实际找到的更多的行。

检查统计数据,它们是最新的吗?检查解释计划中的预期基数是否与实际结果相符?如果不修复与该步骤相关的统计信息。

连接列的直方图可能会有所帮助。Oracle 将使用这些来估计连接产生的基数。

当然,您始终可以通过提示强制使用索引

于 2009-02-26T21:27:03.987 回答
3

查看优化器的行数估计值会很有用,这些估计值不在您发布的 SQL Developer 输出中。

我注意到它正在执行的两个索引查找是 RANGE SCAN 而不是 UNIQUE SCAN。因此,它对返回多少行的估计可能很容易相距甚远(统计数据是否是最新的)。

我的猜测是它对 TBL2 的 TABLE ACCESS 的最终行数的估计相当高,因此它认为它会在 TBL1 中找到大量匹​​配项,因此决定进行全扫描/散列连接而不是嵌套循环/索引扫描。

为了获得一些真正的乐趣,您可以在启用事件 10053 的情况下运行查询,并获得显示优化器执行的计算的跟踪。

于 2009-02-26T21:37:02.277 回答
2

Oracle 尝试返回所需 I/O 最少的结果集(通常,这是有道理的,因为 I/O 很慢)。索引至少需要 2 个 I/O 调用。一个到索引,一个到表。通常更多,取决于索引的大小和表的大小以及返回的记录数,它们在数据文件中的位置,...

这就是统计数据的来源。假设您的查询估计返回 10 条记录。优化器可能会计算出使用索引将需要 10 次 I/O 调用。假设您的表,根据它的统计信息,驻留在数据文件中的 6 个块中。Oracle 执行完整扫描(6 I/O)然后读取索引、读取表、读取然后索引下一个匹配键、读取表等会更快。

所以在你的情况下,桌子可能真的很小。统计数据可能已关闭。

我使用以下内容收集统计数据并根据我的确切需求对其进行自定义:

begin

 DBMS_STATS.GATHER_TABLE_STATS(ownname
=> '&owner' ,tabname => '&table_name', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE,granularity
=> 'ALL', cascade  => TRUE); 

 -- DBMS_STATS.GATHER_TABLE_STATS(ownname
=> '&owner' ,tabname => '&table_name',partname => '&partion_name',granularity => 'PARTITION', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE, cascade 
=> TRUE);

 -- DBMS_STATS.GATHER_TABLE_STATS(ownname
=> '&owner' ,tabname => '&table_name',partname => '&partion_name',granularity => 'PARTITION', estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE, cascade 
=> TRUE,method_opt  => 'for all indexed columns size 254');

end;
于 2009-02-27T03:01:39.283 回答
1

您只能通过查看 SQL 优化器/执行器创建的查询计划来判断。它将至少部分基于无法仅从定义中预测的索引统计信息(因此可以随时间变化)。

适用于 SQL Server 2005/2008 的 SQL Management Studio,适用于早期版本的查询分析器。

(不记得 Oracle 的正确工具名称。)

于 2009-02-26T21:21:08.757 回答
1

尝试添加索引提示。

SELECT /*+ index(tbl1 tbl1_index_name) */ .....

有时 Oracle 只是不知道要使用哪个索引。

于 2009-02-26T21:34:41.003 回答
0

显然这个查询给出了相同的计划:

SELECT tbl1.*   
FROM tbl1 
JOIN tbl2 ON (tbl1.t1_pk  = tbl2.t2_fk_t1_pk)
JOIN tbl3 on (tbl3.t3_pk = tbl2.t2_fk_t3_pk)
where tbl2.t2_lkup_1   = 1020000002981587
AND tbl2.t2_strt_dt <= sysdate
AND tbl2.t2_end_dt  >= sysdate
AND tbl3.t3_lkup_1 = 2577304
AND tbl3.t3_lkup_2 = 1220833;

如果将此查询重写为:

SELECT tbl1.*    
FROM  tbl1 
,     tbl2
,     tbl3  
where tbl2.t2_lkup_1   = 1020000002981587 
AND   tbl1.t1_pk  = tbl2.t2_fk_t1_pk 
AND   tbl3.t3_pk = tbl2.t2_fk_t3_pk 
AND   tbl2.t2_strt_dt <= sysdate 
AND   tbl2.t2_end_dt  >= sysdate 
AND   tbl3.t3_lkup_1 = 2577304 
AND   tbl3.t3_lkup_2 = 1220833;
于 2009-02-26T22:07:51.733 回答
0

看起来 tbl1 表的索引没有被拾取。确保您有 t2_lkup_1 列的索引,并且它不应该是多列,否则该索引不适用。

(除了马特的评论)从您的查询中,我相信您正在加入,因为您想过滤掉不执行 JOIN 的记录,如果来自 . 见杰夫阿特伍德评论

试试这个,它使用exist函数和join(在oracle上真的很快)

选择 *
  从tbl1
 其中 tbl2.t2_lkup_1 = 1020000002981587 和
       存在(
         选择 *
           从tbl2,tbl3
          其中 tbl2.t2_fk_t1_pk = tbl1.t1_pk 和
                tbl2.t2_fk_t3_pk = tbl3.t3_pk 和
                tbl2.t2_strt_dt 和 tbl2.t2_end_dt 之间的 sysdate 和
                tbl3.t3_lkup_1 = 2577304 和
                tbl3.t3_lkup_2 = 1220833);

于 2009-02-27T04:31:53.543 回答
0

根据您的预期结果大小,您可以使用一些会话参数来玩转:

SHOW PARAMETER optimizer_index_cost_adj;
[...]
ALTER SESSION SET optimizer_index_cost_adj = 10;

SHOW PARAMETER OPTIMIZER_MODE;
[...]
ALTER SESSION SET OPTIMIZER_MODE=FIRST_ROWS_100;

并且不要忘记检查真实的执行时间,有时计划不是真实的世界;)

于 2015-05-21T12:49:40.447 回答