我有2个表有很多记录(比如TableA和TableB都有大约3,000,000条记录).vr2_input是用户输入的varchar输入参数,我想得到最多200个“dateField”的TableA记录, stringField类似于'vr2_input'。这两个表连接如下:
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
查询很慢,我对此进行了调查,发现它是因为“喜欢”和“排序依据”涉及全表扫描。但是,我找不到解决方案来解决问题。我该如何调整这种类型的SQL?我已经在TableA.stringField和TableA.dateField上创建了一个索引但是如何在select语句中使用索引功能呢?数据库是oracle 10g。非常感谢!!
更新:我使用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")
答案 0 :(得分:3)
您说运行查询大约需要4分钟。 EXPLAIN PLAN输出显示估计为18秒。因此,在这种情况下,优化器可能远远没有达到某些估计值。 (它仍然可以选择最好的计划,但也许不是。)
这样的案例的第一步是获取实际的执行计划和统计数据。使用提示/*+ gather_plan_statistics */
运行查询,然后立即执行select * from table(dbms_xplan.display_cursor(null,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
答案 1 :(得分:1)
您需要选择所有列(*)吗?如果选择所有列,优化器将更有可能进行全扫描。如果您需要输出中的所有列,最好在内联视图中选择id,然后再联接以选择其他列,这可以通过索引查找来完成。尝试为两种情况运行解释计划,以查看优化程序正在执行的操作。
答案 2 :(得分:0)
在stringField和dateField列上创建索引。 SQL引擎会自动使用它们。
答案 3 :(得分:0)
表A具有多少记录,如果它是较小的表,您可以对该表执行选择,然后循环检索表B记录的结果,因为selectA和sort都在TableA上。
一个好的实验是删除连接并测试速度,如果允许的话,你可以把rownum&lt; 201作为主查询的AND子句。目前可能是查询将所有行返回到外部查询然后它被修剪了?
答案 4 :(得分:0)
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。
答案 5 :(得分:0)
如果你有足够的内存,你可以提示查询使用散列连接。你能否附上解释计划
答案 6 :(得分:0)
您可以在tableA上创建一个函数索引。这将根据条件TableA.stringField返回1或0,如'vr2_input'|| '%'是否满意。该索引将使查询运行得更快。该函数的逻辑将是
if (substr(TableA.stringField, 1, 9) = 'vr2_input'
THEN
return 1;
else
return 0;
使用实际列名而不是“*”可能会有所帮助。至少应删除常用列名。
答案 7 :(得分:0)