我有桌子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
);
答案 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内置了一大堆用于调查此类问题的工具。
从这篇论文开始:
答案 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”