sql - Oracle 处理很长的 IN 运算符列表的效率如何

标签 sql oracle oracle11g

我有以下查询(这是一个更复杂的查询的简化版本):

SELECT * FROM TPM_TASK
WHERE (PROJECTID, VERSIONID) IN ((3,1), (24,1), (4,1))

在代码中,我将构建 (PROJECTID,VERSIONID)以编程方式列出 key 列表,这个列表可能有几千对长。

我的问题是 Oracle 将如何优化此查询,因为 ProjectIdVersionId被索引。将列表转换为哈希表,类似于 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 ITERATOR 是什么意思。我猜这意味着 Oracle 正在遍历列表并对列表中的每个项目进行表访问,这对于数千个键可能不会太有效。但是,如果我确实给了它几千个 key ,也许 Oracle 足够聪明,可以更好地优化它。

注意:我不想将这些键加载到临时表中,因为坦率地说,我不喜欢临时表在 Oracle 下的工作方式,而且它们通常最终会比它们的值(value)更令人沮丧(无论如何,在我的非专家意见中。 )

最佳答案

优化器应根据列表中的项目数和表中的行数做出决定。如果表有数百万行,而列表甚至有几千个项目,我通常希望它会使用索引来进行几千个单行查找。如果表有几千行并且列表有几千个项目,我希望优化器对表进行全面扫描。当然,中间是所有有趣的事情发生的地方,并且更难准确地计算出优化器将选择的计划。

然而,一般来说,从性能的角度来看,动态构建这种查询将是有问题的,这不是因为特定查询的执行成本很高,而是因为您生成的查询是不可共享的。由于您不能使用绑定(bind)变量(或者,如果您使用绑定(bind)变量,则需要不同数量的绑定(bind)变量)。这迫使 Oracle 每次都对查询进行相当昂贵的硬解析,并对您的共享池施加压力,这可能会迫使其他可共享的查询退出,从而导致系统中的硬解析更加困难。通常,您最好将要匹配的数据扔到临时表(甚至永久表)中,这样您的查询就可以共享并只解析一次。

根据 Branko 的评论,而 Oracle 在 IN 中限制为 1000 个文字。列表,仅当您使用“正常”语法时,即

WHERE projectID IN (1,2,3,...,N)

但是,如果您使用之前发布的元组语法,则可以拥有无​​限数量的元素。

因此,例如,如果我在 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

但如果我使用元组语法
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.

关于sql - Oracle 处理很长的 IN 运算符列表的效率如何,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9690974/

相关文章:

mysql - 使用 mysql 连接表最快的方法是什么

oracle - 在脚本期间备份 SQL Plus 环境

SQL从字符串中删除字符

java - 尝试在 SQL 查询中附加两个字符串时出现 JPQL 词法错误

java - Java + SQL:INSERT INTO不起作用

python - 使用 pyodbc 和 SQL Server 的 SQL IN 运算符

mysql - 获取MySQL中小数范围的频率分布

SQL命令未正确结束?

sql - 在Oracle SQL中,如何查询某个值的记录所占的比例?

oracle - xml选择查询oracle