2010-09-04 103 views
4

在这里我有一个查询象下面这样:优化Oracle SQL与大“IN”子句

SELECT field 
FROM table 
WHERE value IN ('val1', 'val2', 'val3', ... 'valn') 

比方说,有IN子句中值2000,该值不会在其他表中。你有什么想法加快这一行动吗?

这个问题是开放的接受任何种类的方法..

谢谢!

+1

如果您在该列表中输入了超过1000个值,FWIW,Oracle(11g,无论如何)都会引发错误。除此之外,它运作得很好。 – 2010-09-04 11:57:39

回答

0

这看起来像Java中的正确方法:http://knol.google.com/k/oracle-passing-a-list-as-bind-variable#

它类似于C#的解决方案。您的值列表保留在内存中(没有临时表),并且它不会持久保存到磁盘,并且您使用参数化查询,因此查询执行器不必重新分析每个查询。我没有用java试过,但我认为它会很快。

+2

上面的链接已死亡。 – lubosdz 2015-12-09 09:40:20

4
  1. 创建一个涵盖'字段'和'值'的索引。

  2. 将这些IN值放在临时表中并加入。

+0

同意?是否有任何有关2的表现统计信息或相关链接 - 似乎有点不直观?关于IN的倾斜点的任何指导都会变得效率低下,插入到温度,然后加入到温度? Thnx – StuartLC 2010-09-04 11:31:13

+0

这就是我在面试中回答的问题。面试官然后评论这个答案之后必须做些事情。我想他正在谈论同时使用桌子和家务。 – NeoNosliw 2010-09-05 08:28:05

0

如果您已经有值字段的索引,并且这些值在任何表中都不可用以加入或从中进行选择,那么我不认为有任何可能性进行优化。在特殊情况下,您的值真的是“val1”,“val”,...您可以使用类似的查询来使用索引来搜索前缀。但我认为这只是一个例子。

1
​​

据我所知,您将面临另一个问题。这将是'IN'条款的限制。使用这种方法,您可以避免这种情况,并希望能够加快查询的速度。

0

当您使用绑定变量时,Oracle查询解析和缓存机制效果更好。如果您的查询可能通过使用它们更好地执行

SELECT字段 从表 WHERE值IN(?,?....)然后根据需要分配值。

使用企业管理控制台等工具来分析真实执行的性能并决定改进要好得多。创建索引可能是要做的第一步。

将潜在值存储在另一个表中并使用J Horstmann的建议似乎是一个正确的想法。请试一试。

0

选择更好的解决方案需要更多信息。

  1. 查询是否频繁执行?
  2. 值val1,val2是否固定?
  3. 桌子有多大?

如果查询被频繁地执行,值VAL1,val2的等是固定的,表很大(具有说20,000或更多行),则所有的值存储在另一个表中(比如临时表)和关于连接两个表价值领域。

如果以下查询中的表格很大,则应在值字段上使用索引来提高性能。

SELECT场 FROM表 WHERE

表都应该分析值IN( 'VAL1', '值2', 'VAL3',...... 'VALN')。

更好性能的原因是优化器将根据表的特性选择最佳连接方法。如果上述查询中的表非常大,则联接将是嵌套循环联接,而上表应该在列val上具有索引。

如果上面的查询中的表非常小(比200-300行少),新表(temp表)应该在val列上有索引。

如果两个表的大小几乎相同,则索引不会有多大帮助。

结论:最好的解决方案取决于具体情况。

0

如果其他建议都不起作用,并且查询需要很长时间,则可以尝试并行运行它。

select /*+ parallel(table) */ field ... 
1

您可以加入一个正常的表格,并在其中填充一个值列表。

我不知道如何用Java做到这一点,但我知道如何用C#做到这一点。我认为Java应该可以做类似的事情。

这里阅读:http://forums.oracle.com/forums/thread.jspa?threadID=892457&tstart=375

让我们使用用户定义类型的集合(UDT的)。 首先创建一个表100万行:

create table employees (id number(10) not null primary key, name varchar2(100)); 

insert into employees 
select level l, 'MyName'||to_char(level) 
from dual connect by level <= 1e6; 

1000000 rows created 

commit; 

exec dbms_stats.gather_schema_stats(USER, cascade=>TRUE); 

不,我们转向C#代码:

我们选择与ID的3和4

集合类型MDSYS.SDO_ELEM_INFO_ARRAY被使用,因为员工如果我们使用这个已经预定义的Oracle类型,我们不必定义自己的Oracle类型。您可以用最大1048576数字填充收集MDSYS.SDO_ELEM_INFO_ARRAY。

using Oracle.DataAccess.Client; 
using Oracle.DataAccess.Types; 

    [OracleCustomTypeMappingAttribute("MDSYS.SDO_ELEM_INFO_ARRAY")] 
    public class NumberArrayFactory : IOracleArrayTypeFactory 
    { 
     public Array CreateArray(int numElems) 
     { 
     return new Decimal[numElems]; 
     } 

     public Array CreateStatusArray(int numElems) 
     { 
     return null; 
     } 
    } 


    private void Test() 
    { 
     OracleConnectionStringBuilder b = new OracleConnectionStringBuilder(); 
     b.UserID = "sna"; 
     b.Password = "sna"; 
     b.DataSource = "ora11"; 
     using (OracleConnection conn = new OracleConnection(b.ToString())) 
     { 
     conn.Open(); 
     using (OracleCommand comm = conn.CreateCommand()) 
     { 
      comm.CommandText = 
       @" select /*+ cardinality(tab 10) */ * " + 
       @" from employees, table(:1) tab " + 
       @" where employees.id = tab.column_value"; 

      OracleParameter p = new OracleParameter(); 
      p.OracleDbType = OracleDbType.Array; 
      p.Direction = ParameterDirection.Input; 
      p.UdtTypeName = "MDSYS.SDO_ELEM_INFO_ARRAY"; 
      p.Value = new Decimal[] { 3, 4 }; 

      comm.Parameters.Add(p); 

      int numPersons = 0; 
      using (OracleDataReader reader = comm.ExecuteReader()) 
      { 
      while (reader.Read()) 
      { 
       MessageBox.Show("Name " + reader[1].ToString()); 
       numPersons++; 
      } 
      } 
      conn.Close(); 
     } 
     } 
    } 

当省略提示/ * +基数(选项卡10)* /时,不会使用employees.id上的索引。该索引由Oracle创建,因为id是主键列。

这意味着您不必填写临时表。 vaues列表保持在内存中,并且您加入您的表员工与此内存值列表表(:1)标签

(wateenmooiedag = TTT)

0

只是重写你IN存在。它会更快。

+0

也许你可以包括一个例子? – 2010-09-08 12:08:22

0

使用以下方法进行类似查询时,我获得了可接受的性能(执行时间接近于无条件地读取行)。

static final int MAX_QUERY_SET = 1000; 

我重复的值,并作出单独的查询每个MAX_QUERY_SET值。所以对于10K值,我有10个查询。我按顺序处理查询。

实现这个算法后,我可以玩常数。对于30或3000的任何一个值,我的执行时间都增加了3倍。所以我坚持1000.

如果您无法处理多个查询,这可能无法正常工作。我的经验被收集在一个不同的数据库上(Pervasive,每条语句有65K字符限制),但我认为这个问题相当普遍,其结论应该是常见的。