使用大型“IN”子句优化Oracle SQL

时间:2010-09-04 10:53:18

标签: java sql oracle optimization query-optimization

我在这里有一个如下查询:

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

假设IN子句中有2000个值,该值在其他表中不存在。你有任何想法加快这项行动吗?

这个问题可以接受任何一种方法。

谢谢!

10 个答案:

答案 0 :(得分:4)

  1. 创建一个涵盖'field'和'value'的索引。

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

答案 1 :(得分:1)

SELECT field
FROM table
WHERE value IN SELECT somevalue from sometable

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

答案 2 :(得分: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列表保留在ram中,您将表员工加入内存表(:1)标签中的此值列表。

(wateenmooiedag = TTT)

答案 3 :(得分:0)

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

答案 4 :(得分:0)

使用绑定变量时,Oracle查询解析和缓存机制可以更好地工作。如果您的查询可以通过使用它们来表现更好

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

使用诸如企业管理控制台之类的工具分析实际执行的性能并决定改进是更好的。创建索引可能是第一步。

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

答案 5 :(得分:0)

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

  1. 查询是否经常执行?
  2. 值val1,val2是否固定?
  3. 桌子有多大?
  4. 如果经常执行查询,则值val1,val2等是固定的并且表大(具有20,000或更多行)然后将所有值存储在另一个表(例如临时表)中并在值字段上连接两个表。

    如果下面查询中的表很大,则应该有值字段索引以提高性能。

    SELECT字段 从表 值IN('val1','val2','val3',...'valn')

    应该对表格进行分析。

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

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

    如果两个表的大小几乎相同,那么索引就无济于事。

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

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

只需将IN重写为存在即可。它会更快。

答案 9 :(得分:0)

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

static final int MAX_QUERY_SET = 1000;

我迭代这些值并对每个MAX_QUERY_SET值进行单独查询。所以对于10K值,我有10个查询。我按顺序处理查询。

实现此算法后,我能够使用常量。对于30或3000的值,我的执行时间延长了3倍。所以我坚持1000。

如果您无法处理多个查询,则可能无效。我的经验是在一个不同的数据库上收集的(Pervasive,每个语句限制为65K个字符),但我认为这个问题非常笼统,其结论应该是常见的。