查询:
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和3500行中的11,000行 tbl2表明它正在对tbl1进行全表扫描。在我看来 如果它可以在tbl1上进行索引查询,它应该更快。
解释一个数据库的计划,该数据库包含tbl1和3500行中的11,000行 tbl2表明它正在对tbl1进行全表扫描。在我看来 如果它可以在tbl1上进行索引查询,它应该更快。
更新:我尝试了一些你建议的提示,解释费用变得更糟!现在我真的很困惑。
进一步更新:我终于可以访问生产数据库的副本, 并且“解释计划”显示它使用索引并且成本低得多 查询。我想有更多的数据(在tbl1和50,000行中超过100,000行 在tbl2)是让它决定索引是值得的。感谢所有帮助过的人。我仍然认为Oracle性能调优是一种黑色艺术,但我很高兴你们中的一些人理解它。
进一步更新:我已根据前雇主的要求更新了问题。他们不喜欢在谷歌查询中显示他们的表名。我应该知道的更好。
答案 0 :(得分:5)
答案很简单:因为优化器需要更多的行来查找,所以它实际上找到了。
检查统计数据,它们是最新的吗? 检查解释计划中的预期基数是否与实际结果相符?如果没有修复与该步骤相关的统计数据。
连接列的直方图可能会有所帮助。 Oracle将使用这些来估计连接产生的基数。
当然,您总是可以使用提示强制索引使用
答案 1 :(得分:3)
查看优化程序的行计数估计值会很有用,这些估计值不在您发布的SQL Developer输出中。
我注意到它正在进行的两个索引查找是RANGE SCAN而不是UNIQUE SCAN。因此,它对返回的行数的估计可能很容易(无论统计数据是否是最新的)。
我的猜测是它对TBL2的TABLE ACCESS的最终行数的估计相当高,因此它认为它会在TBL1中找到大量匹配,因此决定进行全扫描/散列连接而不是嵌套的循环/索引扫描。
为了获得一些真正的乐趣,您可以在启用事件10053的情况下运行查询,并获得显示优化程序执行的计算的跟踪。
答案 2 :(得分: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;
答案 3 :(得分:1)
您只能通过查看SQL优化器/执行程序创建的查询计划来判断。它至少部分基于索引统计数据,而这些统计数据无法仅根据定义进行预测(因此可以随时间变化)。
SQL Server studio for SQL Server 2005/2008,查询分析器用于早期版本。
(无法回想起Oracle的正确工具名称。)
答案 4 :(得分:1)
尝试添加索引提示。
SELECT /*+ index(tbl1 tbl1_index_name) */ .....
有时Oracle只知道要使用哪个索引。
答案 5 :(得分: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;
答案 6 :(得分:0)
看起来tbl1表的索引没有被提取。确保 你有一个t2_lkup_1列的索引,它不应该是多列,否则索引不适用。
(除了Matt的评论) 从您的查询中我相信您加入是因为您想要过滤掉 记录不要做JOIN,这可能会增加结果集的基数 tbl1表,如果有重复的匹配。见Jeff Atwood comment
尝试这个,它使用存在的函数和连接(在oracle上真的很快)
select * from tbl1 where tbl2.t2_lkup_1 = 1020000002981587 and exists ( select * from tbl2, tbl3 where tbl2.t2_fk_t1_pk = tbl1.t1_pk and tbl2.t2_fk_t3_pk = tbl3.t3_pk and sysdate between tbl2.t2_strt_dt and tbl2.t2_end_dt and tbl3.t3_lkup_1 = 2577304 and tbl3.t3_lkup_2 = 1220833);
答案 7 :(得分: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;
并且不要忘记检查实际执行时间,有时计划不是现实世界;)