Оптимизируйте Oracle SQL с большим предложением IN - PullRequest
4 голосов
/ 04 сентября 2010

Здесь у меня есть запрос, подобный приведенному ниже:

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

Допустим, в предложении IN есть 2000 значений, значение не существует в другой таблице.У вас есть идея ускорить эту операцию?

Вопрос открыт для принятия любых методов ..

Спасибо!

Ответы [ 10 ]

4 голосов
/ 04 сентября 2010
  1. Создание индекса, охватывающего «поле» и «значение».

  2. Поместите эти значения IN в временную таблицу и объедините ее.

1 голос
/ 04 сентября 2010

Вы можете объединить обычную таблицу с таблицей памяти, которая заполнена списком значений.

Я не знаю, как это сделать с Java, но я знаю, как это сделать с C #. Я думаю, что нечто подобное должно быть возможно с Java.

Читайте здесь: http://forums.oracle.com/forums/thread.jspa?threadID=892457&tstart=375

Давайте использовать коллекцию пользовательских типов (UDT). Сначала создайте таблицу с 1 миллионом строк:

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 #:

Давайте выберем сотрудников с идентификаторами 3 и 4.

Тип коллекции MDSYS.SDO_ELEM_INFO_ARRAY используется, потому что, если мы используем этот уже предопределенный тип Oracle, нам не нужно определять наш собственный тип Oracle. Вы можете заполнить коллекцию MDSYS.SDO_ELEM_INFO_ARRAY максимум 1048576 номерами.

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();
        }
      }
    }

Индекс на employee.id не используется, если не указывать подсказку / * + кардинальность (вкладка 10) * /. Этот индекс создается Oracle, поскольку id является столбцом первичного ключа.

Это означает, что вам не нужно заполнять временную таблицу. Список значений остается в оперативной памяти, и вы присоединяете свою таблицу сотрудников с этим списком значений в памяти таблица (: 1) вкладка .

(wateenmooiedag = ТТТ) * +1027 *

1 голос
/ 04 сентября 2010
SELECT field
FROM table
WHERE value IN SELECT somevalue from sometable

Насколько я знаю, вы столкнетесь с другой проблемой. Это будет ограничением пункта «IN». Используя это, вы можете избежать этого и надеяться закрепить ваш запрос

0 голосов
/ 10 ноября 2012

Я получил приемлемую производительность (время выполнения близко к безусловному извлечению строк) при выполнении аналогичного запроса с использованием следующего подхода.

static final int MAX_QUERY_SET = 1000;

Я повторяю значения и делаю отдельный запрос для каждого значения MAX_QUERY_SET.Так что для значений 10K у меня есть 10 запросов.Я обрабатываю запросы последовательно.

После реализации этого алгоритма я смог поиграть с константой.Для любого значения 30 или 3000 я получил в 3 раза больше времени выполнения.Поэтому я выбрал 1000.

Это может не сработать, если вы не можете обработать несколько запросов.Мой опыт был собран в другой базе данных (Pervasive, с ограничением в 65 тыс. Символов на утверждение), но я думаю, что этот вопрос довольно общий, и его выводы должны быть общими.

0 голосов
/ 07 сентября 2010

Просто переписать вас, чтобы существовать. Это будет быстрее.

0 голосов
/ 04 сентября 2010

Это похоже на правильный путь в Java: http://knol.google.com/k/oracle-passing-a-list-as-bind-variable#

Это похоже на решение C #.Ваш список значений остается в памяти (без временной таблицы), и он не будет сохранен на диске, и вы используете параметризованный запрос, поэтому исполнителю запроса не нужно повторять каждый запрос.Я не пробовал это с Java, но я думаю, что это будет быстро.

0 голосов
/ 04 сентября 2010

Если ни одно из других предложений не работает, а запрос занимает много времени, вы можете попробовать запустить его параллельно.

  select /*+ parallel(table) */ field ...
0 голосов
/ 04 сентября 2010

Требуется больше информации для выбора лучшего решения.

  1. Часто ли выполняется запрос?
  2. Являются ли значения val1, val2 фиксированными?
  3. Насколько велика таблица?

Если запросвыполняется часто, значения val1, val2 и т. д. являются фиксированными, и таблица имеет большой размер (например, 20000 или более строк), затем сохраняет все значения в другой таблице (например, временной таблице) и объединяет обе таблицы в поле значений.

Если таблица в приведенном ниже запросе является большой, для повышения производительности должен быть индекс по полю значения.

Поле SELECT FROM таблицы WHERE значение IN ('val1', 'val2', 'val3 ', ...' valn ')

Обе таблицы должны быть проанализированы.

Причина повышения производительности заключается в том, что оптимизатор выберет лучший метод соединения в зависимости от характеристики таблицы,Если таблица в приведенном выше запросе очень велика, объединение будет соединением с вложенным циклом, а над таблицей должен быть индекс для столбца val.

Если таблица в приведенном выше запросе очень мала (скажем, меньше, чем 200-300 строк), новая таблица (временная таблица) должна иметь индекс для столбца val.

Если обе таблицы почти одинаковыРазмерные индексы мало чем помогут.

Вывод: лучшее решение зависит от конкретной ситуации.

0 голосов
/ 04 сентября 2010

Механизм синтаксического анализа и кэширования Oracle работает лучше, когда вы используете переменные связывания. Если ваш запрос может работать лучше, используя их

поле SELECT С таблицы ГДЕ значение IN (?,? ....) и затем присваивать значения по мере необходимости.

Гораздо лучше проанализировать производительность реального исполнения с помощью некоторого инструмента, такого как консоль управления предприятием, и принять решение об улучшениях. Вероятно, создание индекса - это первый шаг.

Сохранение потенциальных значений в другой таблице и использование предложений от J Horstmann кажется правильной идеей. Пожалуйста, попробуйте.

0 голосов
/ 04 сентября 2010

Если у вас уже есть индекс в поле значений, а значения не доступны ни в одной из таблиц, к которым можно присоединиться или выполнить дополнительный выбор, то я не думаю, что есть какие-либо возможности для оптимизации.В особом случае, когда ваши значения действительно равны «val1», «val», ..., вы можете использовать подобный запрос, который будет использовать индекс для поиска по префиксу.Но я полагаю, что это был только пример.

...