Performance 关于“ORDER BY”和“LIKE”子句的性能调优
我有两个表,其中有很多记录,比如TableA和TableB都有大约3000000条记录。vr2_input是用户输入的varchar输入参数,我想获得最多200条最大日期字段的TableA记录,其字符串字段类似于“vr2_input”。这两个表按如下方式连接:Performance 关于“ORDER BY”和“LIKE”子句的性能调优,performance,oracle,plsql,Performance,Oracle,Plsql,我有两个表,其中有很多记录,比如TableA和TableB都有大约3000000条记录。vr2_input是用户输入的varchar输入参数,我想获得最多200条最大日期字段的TableA记录,其字符串字段类似于“vr2_input”。这两个表按如下方式连接: select * from( select * from TableA join TableB on TableA.id = TableB.id where TableA.stringFie
select * from(
select * from
TableA join TableB on TableA.id = TableB.id
where TableA.stringField like 'vr2_input' || '%'
order by TableA.dateField desc
) where rownum < 201
查询速度很慢,我目不转睛地看了看,发现这是因为like和orderby涉及到完整的表扫描。但是,我找不到解决问题的方法。如何调整这种类型的SQL?我已经在TableA.stringField和TableA.dateField上创建了索引,但是如何在select语句中使用索引功能?数据库是oracle10g。非常感谢
更新:我使用iddqd的建议,只选择我想要的字段并运行解释计划。完成查询大约需要4分钟。IX_TableA_stringField是TableA.srv_ref字段的索引名。我在没有提示的情况下再次运行解释计划,解释计划仍然得到相同的结果
EXPLAIN PLAN FOR
select * from(
select
/*+ INDEX(TableB IX_TableA_stringField)*/
TableA.id,
TableA.stringField,
TableA.dateField,
TableA.someField2,
TableA.someField3,
TableB.someField1,
TableB.someField2,
TableB.someField3,
from TableA
join TableB on TableA.id=TableB.id
WHERE TableA.stringField like '21'||'%'
order by TableA.dateField desc
) where rownum < 201
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 871807846
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200 | 24000 | 3293 (1)| 00:00:18 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 1397 | 163K| 3293 (1)| 00:00:18 |
|* 3 | SORT ORDER BY STOPKEY | | 1397 | 90805 | 3293 (1)| 00:00:18 |
| 4 | NESTED LOOPS | | 1397 | 90805 | 3292 (1)| 00:00:18 |
| 5 | TABLE ACCESS BY INDEX ROWID| TableA | 1397 | 41910 | 492 (1)| 00:00:03 |
|* 6 | INDEX RANGE SCAN | IX_TableA_stringField | 1397 | | 6 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| TableB | 1 | 35 | 2 (0)| 00:00:01 |
|* 8 | INDEX UNIQUE SCAN | PK_TableB | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<201)
3 - filter(ROWNUM<201)
6 - access("TableA"."stringField" LIKE '21%')
filter("TableA"."stringField" LIKE '21%')
8 - access(TableA"."id"="TableB"."id")
在stringField和dateField列上创建索引。SQL引擎会自动使用它们。在stringField和dateField列上创建索引。SQL引擎会自动使用它们。表A有多少条记录如果是较小的表,您可以在该表上进行选择,然后循环检索表B记录的结果,因为选择和排序都在表A上
一个很好的实验是删除连接并测试其速度。如果允许,还可以将rownum<201作为and子句放在主查询中。很可能此时查询将所有行返回到外部查询,然后对其进行修剪?如果是较小的表,那么表A有多少条记录?您可以对该表进行选择,然后循环检索表B记录的结果,因为选择和排序都在表A上
select id from(
select /*+ INDEX(TableB stringField_indx)*/ TableB.id from
TableA join TableB on TableA.id = TableB.id
where TableA.stringField like 'vr2_input' || '%'
order by TableA.dateField desc
) where rownum < 201
next:
SELECT * FROM TableB WHERE id iN( id from first query)
一个很好的实验是删除连接并测试其速度。如果允许,还可以将rownum<201作为and子句放在主查询中。很可能此时查询将所有行返回到外部查询,然后对其进行修剪
select id from(
select /*+ INDEX(TableB stringField_indx)*/ TableB.id from
TableA join TableB on TableA.id = TableB.id
where TableA.stringField like 'vr2_input' || '%'
order by TableA.dateField desc
) where rownum < 201
next:
SELECT * FROM TableB WHERE id iN( id from first query)
请发送此表的统计数据和DDL
请发送此表的统计数据和DDL。如果您有足够的内存,可以提示查询使用哈希连接。如果您有足够的内存,可以提示查询使用哈希联接,请附上解释计划。请附上解释计划是否需要选择所有列*?如果选择所有列,则优化器更有可能进行完全扫描。如果您需要输出中的所有列,那么最好在内联视图中选择id,然后重新连接以选择其他列,这可以通过索引查找完成。尝试对这两种情况运行解释计划,以查看优化器正在执行的操作。是否需要选择所有列*?如果选择所有列,则优化器更有可能进行完全扫描。如果您需要输出中的所有列,那么最好在内联视图中选择id,然后重新连接以选择其他列,这可以通过索引查找完成。尝试对这两种情况运行解释计划,看看优化器在做什么。您说运行查询大约需要4分钟。解释计划输出显示估计为18秒。因此,在这种情况下,优化器的某些估计可能远远不够。它仍然可以选择最好的计划,但可能不是 在这种情况下,第一步是获得实际的执行计划和统计数据。使用提示/*+gather\u plan\u statistics*/运行查询,然后立即从tabledbms\u xplan执行select*命令。显示\u cursornull、null和'ALLSTATS LAST' 这将显示运行的实际执行计划,并且对于每个步骤,它将显示估计行、实际行和实际花费的时间。在这里发布输出,也许我们可以对您的问题说一些更有意义的话 如果没有这些信息,我的建议是尝试以下对查询的重写。我认为这是等效的,因为ID似乎是TableB的主键
select TableA.id,
TableA.stringField,
TableA.dateField,
TableA.someField2,
TableA.someField3,
TableB.someField1,
TableB.someField2,
TableB.someField3,
from (select * from(
select
TableA.id,
TableA.stringField,
TableA.dateField,
TableA.someField2,
TableA.someField3,
from TableA
WHERE TableA.stringField like '21'||'%'
order by TableA.dateField desc
)
where rownum < 201
) TableA
join TableB on TableA.id=TableB.id
您说运行查询大约需要4分钟。解释计划输出显示估计为18秒。因此,在这种情况下,优化器的某些估计可能远远不够。它仍然可以选择最好的计划,但可能不是 在这种情况下,第一步是获得实际的执行计划和统计数据。使用提示/*+gather\u plan\u statistics*/运行查询,然后立即从tabledbms\u xplan执行select*命令。显示\u cursornull、null和'ALLSTATS LAST' 这将显示运行的实际执行计划,并且对于每个步骤,它将显示估计行、实际行和实际花费的时间。把结果贴在这里,也许我们可以说点什么 你对你的问题很有意义 如果没有这些信息,我的建议是尝试以下对查询的重写。我认为这是等效的,因为ID似乎是TableB的主键
select TableA.id,
TableA.stringField,
TableA.dateField,
TableA.someField2,
TableA.someField3,
TableB.someField1,
TableB.someField2,
TableB.someField3,
from (select * from(
select
TableA.id,
TableA.stringField,
TableA.dateField,
TableA.someField2,
TableA.someField3,
from TableA
WHERE TableA.stringField like '21'||'%'
order by TableA.dateField desc
)
where rownum < 201
) TableA
join TableB on TableA.id=TableB.id
您可以在表A上创建一个函数索引。根据是否满足“vr2_输入”| |“%”之类的TableA.stringField条件返回1或0。该索引将使查询运行得更快。函数的逻辑将是
if (substr(TableA.stringField, 1, 9) = 'vr2_input'
THEN
return 1;
else
return 0;
使用实际列名而不是*可能会有所帮助。至少应删除常用列名 您可以在表A上创建一个函数索引。根据是否满足“vr2_输入”| |“%”之类的TableA.stringField条件返回1或0。该索引将使查询运行得更快。函数的逻辑将是
if (substr(TableA.stringField, 1, 9) = 'vr2_input'
THEN
return 1;
else
return 0;
使用实际列名而不是*可能会有所帮助。至少应删除常用列名 要优化like谓词,可以创建上下文索引并使用contains子句 看:
要优化like谓词,可以创建上下文索引并使用contains子句 看:
谢谢我已经在TableA.stringField和TableA.dateField上创建了一个索引,但速度非常慢。最好在执行了诸如create table、alter table或create index之类的ddl语句后收集新的统计数据。我已经在TableA.stringField和TableA.dateField上创建了一个索引,但它非常慢。最好在执行ddl语句(如create table、alter table或create index)后收集新的统计数据。您说它很慢-有多慢:秒、分钟、,小时数?Oracle不会对包含LIKE或ORDER BY的查询使用索引,这不是真的。您可以在SQL Plus中使用AUTOTRACE ON运行它并发布信息吗?如果没有提示,您从EXPLAIN PLAN中会得到什么?对我来说,提示是最后的手段,而不是第一件要尝试的事情。我在没有提示的情况下再次运行解释计划,解释计划仍然得到相同的结果。因此,我想知道所使用的提示是否有用。你说它很慢-有多慢:秒、分钟、小时?Oracle不会使用索引进行包含LIKE或ORDER BY的查询,这不是真的。你能在SQL Plus中使用AUTOTRACE ON运行它并发布信息吗?如果没有提示,你会从EXPLAIN PLAN中得到什么?对我来说,提示是最后的手段,而不是第一件要尝试的事情。我在没有提示的情况下再次运行解释计划,解释计划仍然得到相同的结果。所以我想知道使用的提示是否有用+1,收集计划统计数据是如此的酷。我不敢相信我以前从未听说过它。+1,收集计划统计数据太酷了。真不敢相信我以前没听说过。