Mik*_*sen 8 sql oracle oracle11g
我有以下查询(这是一个更复杂的查询的简化版本):
SELECT * FROM TPM_TASK
WHERE (PROJECTID, VERSIONID) IN ((3,1), (24,1), (4,1))
Run Code Online (Sandbox Code Playgroud)
在代码中,我将以(PROJECTID,VERSIONID)编程方式构建该密钥列表,此列表可能长达数千对.
我的问题是Oracle将如何优化此查询因为ProjectId和VersionId编制索引.将列表转换为哈希表,类似于join临时表吗?或者每次键查找一次完成一次?
我在我的测试数据库下尝试了这个查询,得到了:
SELECT STATEMENT 68.0 68 2989732 19 8759 68 ALL_ROWS
TABLE ACCESS (FULL) 68.0 68 2989732 19 8759 1 TPMDBO TPM_TASK FULL TABLE ANALYZED 1
Run Code Online (Sandbox Code Playgroud)
但是,我认为这个数据库没有足够的数据来保证索引扫描.我尝试了生产查询并得到:
SELECT STATEMENT 19.0 19 230367 23 9683 19 ALL_ROWS
INLIST ITERATOR 1
TABLE ACCESS (BY INDEX ROWID) 19.0 19 230367 23 9683 1 TPMDBO TPM_TASK BY INDEX ROWID TABLE ANALYZED 1
INDEX (RANGE SCAN) 4.0 4 64457 29 1 TPMDBO TPM_H1_TASK RANGE SCAN INDEX ANALYZED 1
Run Code Online (Sandbox Code Playgroud)
这似乎达到了指数,但我不确定INLIST ITERATOR的意思.我猜这意味着Oracle正在遍历列表并对列表中的每个项目进行表访问,这对于数千个密钥来说可能效率不高.但是,如果我确实给了几千个密钥,那么Oracle可能足够聪明,可以更好地优化它.
注意:我不想将这些密钥加载到临时表中,因为坦率地说我不喜欢临时表在Oracle下运行的方式,并且它们通常最终会比它们的价值更令人沮丧(在我的非专家意见中)无论如何.)
优化器应根据列表中的项目数和表中的行数做出决策.如果表有数百万行,并且列表甚至有几千个项目,我通常会期望它会使用索引进行几千次单行查找.如果表有几千行并且列表有几千个项目,我希望优化器能够对表进行全面扫描.当然,在中间,所有有趣的东西都会发生,并且更难以确定优化程序将选择的计划.
但是,一般情况下,从性能角度来看,动态构建此类查询会产生问题,而不是因为特定查询执行的成本有多高,而是因为您生成的查询不可共享.由于您不能使用绑定变量(或者,如果您使用绑定变量,则需要不同数量的绑定变量).这迫使Oracle每次都对查询进行相当昂贵的硬解析,并对共享池施加压力,这可能会导致其他可共享的查询,这将导致系统中更难解析.通常,您可以更好地将要匹配的数据放入临时表(甚至是永久表)中,这样您的查询就可以进行共享和解析一次.
对于Branko的评论,虽然Oracle在IN列表中限制为1000个文字,但这只是在您使用"普通"语法时,即
WHERE projectID IN (1,2,3,...,N)
Run Code Online (Sandbox Code Playgroud)
但是,如果使用先前发布的元组语法,则可以使用无限数量的元素.
因此,例如,如果我在IN列表中建立一个包含2000个项目的查询,我将收到错误
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_sql_stmt varchar2(32000);
3 l_cnt integer;
4 begin
5 l_sql_stmt := 'select count(*) from emp where empno in (';
6 for i in 1..2000
7 loop
8 l_sql_stmt := l_sql_stmt || '(1),';
9 end loop;
10 l_sql_stmt := rtrim(l_sql_stmt,',') || ')';
11 -- p.l( l_sql_stmt );
12 execute immediate l_sql_stmt into l_cnt;
13* end;
SQL> /
declare
*
ERROR at line 1:
ORA-01795: maximum number of expressions in a list is 1000
ORA-06512: at line 12
Run Code Online (Sandbox Code Playgroud)
但是如果我使用元组语法则不行
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_sql_stmt varchar2(32000);
3 l_cnt integer;
4 begin
5 l_sql_stmt := 'select count(*) from emp where (empno,empno) in (';
6 for i in 1..2000
7 loop
8 l_sql_stmt := l_sql_stmt || '(1,1),';
9 end loop;
10 l_sql_stmt := rtrim(l_sql_stmt,',') || ')';
11 -- p.l( l_sql_stmt );
12 execute immediate l_sql_stmt into l_cnt;
13* end;
SQL> /
PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8361 次 |
| 最近记录: |