为什么这个查询这么慢?

时间:2010-07-02 20:30:47

标签: sql oracle indexing

我有桌子FOO和BAR。 FOO拥有BAR PK的外键。

当我执行以下查询时,需要几秒钟。

select foo.name, foo.description, bar.quadrant from FOO, BAR
where FOO.BAR_ID = BAR.BAR_ID

这是我的解释计划:

OPERATION        OBJECT_NAME     OPTIONS     COST 
SELECT STATEMENT                                 39 
 HASH JOIN                                       39 
  TABLE ACCESS   BAR             FULL            2 
  TABLE ACCESS   FOO             FULL            36 

FOO中有6000条记录,BAR只有5条.BAR_ID列是NUMBER。

这在Oracle 10g上运行,需要大约3秒钟才能完成。考虑到执行其他查询的速度有多快,这似乎是极端的。

编辑表格defs:

CREATE TABLE BAR
 (
    "BAR_ID" NUMBER NOT NULL,
    "QUADRANT" VARCHAR2(100 BYTE) NOT NULL,
    CONSTRAINT "BAR_PK" PRIMARY KEY ("BAR_ID")
 )

 CREATE TABLE FOO
 (  "FOO_ID" NUMBER NOT NULL, 
    "BAR_ID" NUMBER NOT NULL, 
    "NAME" VARCHAR2(250 BYTE) NOT NULL, 
    "DESCRIPTION" VARCHAR2(250 BYTE),
    CONSTRAINT "FOO_PK" PRIMARY KEY ("FOO_ID"), 
    CONSTRAINT "FOO__FK1" FOREIGN KEY ("BAR_ID") REFERENCES BAR ("BAR_ID") ENABLE
 );

6 个答案:

答案 0 :(得分:3)

您确定自己拥有良好的统计数据吗?我从你的DDL创建了一个测试用例,并在统计之前看到了这个计划:

--------------------------------------------------------------------------- 
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     | 
--------------------------------------------------------------------------- 
|   0 | SELECT STATEMENT   |      |  4996 |  1619K|    10  (10)| 00:00:01 | 
|*  1 |  HASH JOIN         |      |  4996 |  1619K|    10  (10)| 00:00:01 | 
|   2 |   TABLE ACCESS FULL| BAR  |     5 |   325 |     3   (0)| 00:00:01 | 
|   3 |   TABLE ACCESS FULL| FOO  |  4996 |  1302K|     6   (0)| 00:00:01 | 
--------------------------------------------------------------------------- 

(如果你得到dbms_xplan输出,你也会看到“用于这个语句的动态采样”)。

这样做之后:

SQL> begin dbms_stats.gather_table_stats(user,'FOO'); end;
  2  /

PL/SQL procedure successfully completed.

SQL> c/FOO/BAR/
  1* begin dbms_stats.gather_table_stats(user,'BAR'); end;
SQL> /

PL/SQL procedure successfully completed.

我明白了:

--------------------------------------------------------------------------------------- 
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     | 
--------------------------------------------------------------------------------------- 
|   0 | SELECT STATEMENT             |        |  4996 |   131K|     9  (12)| 00:00:01 | 
|   1 |  MERGE JOIN                  |        |  4996 |   131K|     9  (12)| 00:00:01 | 
|   2 |   TABLE ACCESS BY INDEX ROWID| BAR    |     5 |    40 |     2   (0)| 00:00:01 | 
|   3 |    INDEX FULL SCAN           | BAR_PK |     5 |       |     1   (0)| 00:00:01 | 
|*  4 |   SORT JOIN                  |        |  4996 | 94924 |     7  (15)| 00:00:01 | 
|   5 |    TABLE ACCESS FULL         | FOO    |  4996 | 94924 |     6   (0)| 00:00:01 | 
--------------------------------------------------------------------------------------- 

答案 1 :(得分:2)

获取查询的TKPROF跟踪,了解实际发生的情况 - explain plan只是估算值。

基本上,在查询之前执行ALTER SESSION SET SQL_TRACE = TRUE命令,执行查询,然后ALTER SESSION SET SQL_TRACE = FALSE。然后找到由USER_DUMP_DEST参数确定的位置生成的跟踪文件(查看v$parameter视图)。使用TKPROF实用程序将原始跟踪文件处理为更易读的格式,并检查结果(并在此处发布)。

(有关详细信息,请参阅Oracle.com上的Using SQL Trace and TKPROF。)

答案 2 :(得分:1)

Oracle内置了一大堆用于调查此类问题的工具。

从这篇论文开始:

http://method-r.com/downloads/doc_download/10-for-developers-making-friends-with-the-oracle-database-cary-millsap

答案 3 :(得分:0)

表格是否经常更新?

foo.description是一个巨大的CLOB吗?

网络延迟是否使得查询看起来需要很长时间?

这些表格是否真的很复杂?

这些表曾经非常庞大并且已经删除了大量数据吗?

答案 4 :(得分:0)

根据我的记忆,Oracle会将此视为一个忽略索引的简单连接。基本的想法是,因为您不限制任何一个表中的数据并将它们连接在一起,它认为全表扫描将更好地工作。如果foo表在bar_id列中对于多行有空,那么您可能想要使用索引提示。

例如,如果您基于单个bar_id运行查询,则解释计划可能会按预期使用索引。如果没有索引,它将对条形表进行全面扫描,因为它非常小,并且在foo表上进行全扫描,因为您没有过滤出bar_id的任何值。

最后一点需要确保更新表和索引的统计信息。这对于稀疏索引很重要,因为Oracle可能意识到索引可以显着改变查询的成本。

答案 5 :(得分:0)

对FOO表进行全表扫描是非常合理的,该表有4996行,你正确的查询,你要求oracle“发送所有Foo记录及其bar.quadrant”