Sql Oracle处理一个很长的操作员列表的效率如何
我有以下查询(这是一个复杂得多的查询的简化版本): 在代码中,我将以编程方式构建Sql Oracle处理一个很长的操作员列表的效率如何,sql,oracle,oracle11g,Sql,Oracle,Oracle11g,我有以下查询(这是一个复杂得多的查询的简化版本): 在代码中,我将以编程方式构建(projectd,VERSIONID)键列表,这个列表可能有几千对长 我的问题是,如果ProjectId和VersionId都已编制索引,Oracle将如何优化此查询。该列表是否会转换为哈希表,类似于针对临时表的join?还是每次只查找一个键 我在测试数据库下尝试了此查询,得到: SELECT STATEMENT 68.0 68 2989732 19 8759 68
(projectd,VERSIONID)
键列表,这个列表可能有几千对长
我的问题是,如果ProjectId
和VersionId
都已编制索引,Oracle将如何优化此查询。该列表是否会转换为哈希表,类似于针对临时表的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
然而,我相信这个数据库没有足够的数据来保证索引扫描。我尝试了生产查询,得到:
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
这似乎击中了索引,但我不确定INLIST迭代器的含义。我猜这意味着Oracle正在遍历列表,并对列表中的每个项目进行表访问,如果有数千个键,这可能不会太有效。然而,如果我真的给了它几千个键,也许Oracle足够聪明,可以更好地优化它
注意:我不想将这些键加载到临时表中,因为坦率地说,我不喜欢临时表在Oracle下的工作方式,而且它们通常会遇到更多的挫折(无论如何,在我的非专家意见中)优化器的决策应基于列表中的项目数和表中的行数。如果表有数百万行,而列表甚至有几千个项目,我通常认为它将使用索引进行几千个单行查找。如果表有几千行,列表有几千项,我希望优化器对表进行完整扫描。当然,在中间,所有有趣的事情都发生了,在哪里更难弄清楚优化器会选择什么样的计划。 但是,一般来说,从性能的角度来看,动态构建此类查询会有问题,这不是因为特定查询执行的成本有多高,而是因为您生成的查询不可共享。因为不能使用绑定变量(或者,如果使用绑定变量,则需要不同数量的绑定变量)。这迫使Oracle每次都要对查询进行一次相当昂贵的硬解析,并对您的共享池施加压力,这可能会强制执行其他可共享的查询,这将导致系统中更难解析。通常,将要匹配的数据扔到一个临时表(甚至是一个永久表)中会更好,这样您的查询就可以共享并解析一次 Branko评论说,虽然Oracle在列表中的
中限制为1000个文本,但这仅限于使用“正常”语法,即
WHERE projectID IN (1,2,3,...,N)
但是,如果使用前面发布的元组语法,则可以拥有无限数量的元素
因此,例如,如果我在
列表中的中建立一个包含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
但如果我使用元组语法就不会了
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.
不需要临时表的更好的解决方案可能是将数据放入PL/SQL表中,然后连接到该表。Tom Kyte有一个很好的例子:
希望这能有所帮助。请注意,Oracle的限制是。Quote:“在表达式列表中最多可以指定1000个表达式。”。所以“几千双”是个问题。有趣!超过一千个是非常罕见的,但我可能不得不重新考虑我的设计。虽然这当然回避了一个问题:我是否可以使用或来分隔表达式中的多个大,一个la,其中x IN(0,1999)或x IN(10011002,1003)
@BrankoDimitrijevic,Mike-实际上,如果使用最初发布的元组语法,则在
列表中的中可以有1000多个元素。1000个元素的限制只适用于标量元素。@JustinCave不知道,谢谢!和往常一样,您是细微差别知识的真正来源。请注意,如果您加入PL/SQL表,优化器通常很难估计列表的基数,这很容易导致执行计划不佳。Tom就如何使用基数提示来解决这些问题进行了相关讨论,但随后您却在构建的每个查询中都添加了适当的提示。非常棒!我认为这就是我认为它会起作用的方式,尽管我认为Oracle可能足够聪明,可以在运行时将一个巨大的IN列表放入一个临时表中,并用它做一些聪明的事情。源表在生产中大约有250000行,in列表通常有几百对。每当有人将搜索数据导出到Excel时(可能一天一次或两次),都会运行此查询,因此可能不值得过度优化。。如果它能在几秒钟内运行,我会很高兴的。不管怎样,你的反馈再一次非常有用!
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.