我有下面的sql返回35k行,大约需要10分钟才能运行。两个表都有数百万行。我怎样才能改进这个sql?
SELECT /*+ index(T_DIRECTORY X_DIR) */
DIRx.dir_id ,
base.id
FROM T_DIRECTORY DIRx, T_PERSON base
WHERE
DIRx.id = 26463
and DIRx.PERSONID= base.PERSONID
'| Id | Operation | Name |'
'-------------------------------------------------------'
'| 0 | SELECT STATEMENT | |'
'| 1 | NESTED LOOPS | |'
'| 2 | TABLE ACCESS BY INDEX ROWID| T_DIRECTORY |'
'| 3 | INDEX RANGE SCAN | X_DIRECTORY |'
'| 4 | TABLE ACCESS BY INDEX ROWID| T_PERSON |'
'| 5 | INDEX UNIQUE SCAN | I_PERSON |'
答案 0 :(得分:3)
首先,确保在where子句(DIRx.id)中的列和正在连接的表(base.personid)上有适当的索引,并且分析这些索引以便它们表示表中的数据 - 如果不是经过分析,Oracle可以在可以使用索引时进行完整的表扫描。
SELECT INDEX_NAME,
NUM_ROWS,
LAST_ANALYZED
FROM DBA_INDEXES
WHERE TABLE_NAME IN ('T_DIRECTORY','T_PERSON');
此外,您通过使用提示强制它使用索引,但如果一个表小于另一个表,则散列连接可能是更好的解决方案,因此可能尝试完全删除提示并查看是否有帮助。
并行查询
当此SQL运行时,您是否有多个CPU并且没有其他任何运行 - 即它是批处理过程的一部分,还是可以同时多次调用的在线过程的一部分。如果是批处理并且您有多个CPU,请尝试并行查询但如果它是在线程序则不要执行此操作(例如,使用并行查询的报告将尝试使用所有可用的CPU,如果同时运行多次,性能可能会变差或者如果你运行的并行线程多于每个CPU核心2个。
实际上,并行线程大约是每4个线程执行时间的一半。
群集表/索引
如果这些表总是以这种方式连接,你可以考虑一个聚簇表(oracle会将每个表的连接行存储在同一个块中,这样它就不必花费这么长时间来检索连接的部分,但这可以有一个如果你经常单独访问其中一个表,也会有缺点。
<强>上下文强>
孤立地查看查询并不总是揭示最佳答案 - 当它可能是错误的事情时做一些非常快的事情并没有帮助所以看看上下文,即一旦返回35000行你会做什么,是否添加了它们今天只有一个表有一个可以替代使用的子集吗?
答案 1 :(得分:1)
“我不得不对表格进行反规范化”
在OLTP数据库中,反规范化始终是坏消息。它加速了一些查询,但可以减慢其他查询。它最多会对DML操作造成性能损失,最坏的情况是它会导致数据损坏。它肯定会使我们的应用程序更加复杂。
@trevorNorth就上下文提出了一个有效的观点。数据分发很重要。 T_DIRECTORY中有多少行匹配该ID?该结果集中有多少行在T_PERSON中有匹配的行?那些实际值是否与解释计划中的基数相匹配?如果不是,刷新数据库统计信息将允许数据库发现更好的计划(没有INDEX提示)。但如果您的统计数据是最新的,那么您需要一个不同的解决方案。
比如...什么?
调整是一项艰巨的任务,因为需要考虑很多事情,具体细节确实很重要。一些顾问除了在其他人的代码中修复性能缺陷外,什么也做不了。如果调整很简单,他们将无法做到这一点。
在没有确凿事实的情况下,这里有几个猜测。解决此问题的其他方法:
T_DIRECTORY(ID, PERSONID, DIR_ID)
T_PERSON(PERSONID,ID)
答案 2 :(得分:0)
我不得不对表格进行反规范化。