Проблема эффективности плана запросов Oracle - PullRequest
5 голосов
/ 15 ноября 2010

Следующий запрос задается в PL/SQL procedure.

SELECT e.data FROM extra e WHERE e.external_id in
    (SELECT * FROM TABLE (p_external_ids)).

Тип p_external_ids равен create or replace type "VARCHAR2TABLE" as table of VARCHAR2(4000 CHAR).

Oracle неэффективно выполняет запрос, используя полное сканирование таблицы, Подсказки по запросу не помогли, и необходимые индексы на месте. Замена части SELECT * жестко закодированными идентификаторами сокращает время выполнения запроса на factor of 20, когда количество строк в таблице составляет 200 000 .

Для справки требуется около 0,3 с для выполнения с предложением SELECT * FROM TABLE и около 0.015 ms для одного идентификатора с жестким кодом.

Каковы эффективные способы (поиск ключа) для написания хранимой процедуры для извлечения данных из таблицы для множественных идентификаторов ?Предоставленный тип коллекции должен использоваться для передачи списка идентификаторов хранимой процедуре.

Ответы [ 3 ]

7 голосов
/ 15 ноября 2010

Какие подсказки вы пробовали? Можете ли вы опубликовать план быстрого и медленного запроса?

Одной из основных проблем использования коллекций PL / SQL в SQL является то, что CBO часто неправильно угадывает количество элементов в коллекции и в результате выбирает неправильный план. В таких случаях часто полезно использовать подсказку CARDINALITY, т.е.

SELECT e.data 
  FROM extra e
 WHERE e.external_id IN (
    SELECT /*+ cardinality(ids 10) */ *
      FROM TABLE( p_external_ids ) ids
  )

говорит оптимизатору ожидать 10 элементов в P_EXTERNAL_IDS.

У Тома Кайта более подробно обсуждается подсказка мощности и коллекции PL / SQL на askTom.

Какой тип данных столбца EXTERNAL_ID? Ваша коллекция представляет собой набор строк, но EXTERNAL_ID имеет тенденцию подразумевать NUMBER. Здесь действительно есть несоответствие типов данных?

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

Можете ли вы опубликовать план быстрого и медленного запроса? Можете ли вы опубликовать точный оператор SQL, который вы используете, который включает подсказку CARDINALITY (возможно, есть синтаксическая ошибка)

1 голос
/ 15 ноября 2010

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

Что я имею в виду:

для поиска по одному индексу стоит 200, а для полного сканирования таблицы - 100000. Если вы ищете 20 значений, общая стоимость составит 4000 (менее 100000).Но если вы ищете 1000 значений, общая стоимость с использованием индексов будет 200000.

0 голосов
/ 02 августа 2016

Этот вопрос очень удовлетворительно отвечает на установка мощности для конвейерных и табличных функций , поэтому, пожалуйста, перейдите и прочитайте статью полностью!


Резюме:

метод: расширяемый оптимизатор

Расширяемый оптимизатор реализован с помощью Oracle Data Cartridge (который по сути является типом объекта, известным как тип интерфейса, который содержит один или несколько четко определенных и структурированных методов). Эта функция позволяет нам разрабатывать наши собственные вычисления мощности (как предписанный метод в типе интерфейса), а затем связывать их с нашими табличными или конвейерными функциями. Метод кардинальности типа вызывается CBO во время оптимизации запроса, чтобы определить количество строк для конвейерной или табличной функции.

Следующие цитаты и примеры взяты из статьи, но несколько приспособлены для последовательного ответа на вопрос.

1) Сделать функцию-обертку

Мы создадим небольшую функцию, которая будет получать и возвращать коллекцию нашего универсального типа VARCHAR2TABLE. Эта функция ничего не делает с самой коллекцией; это просто обертка над ним.

SQL> CREATE FUNCTION card_varchar2(
  2                  p_collection IN varchar2table
  3                  ) RETURN varchar2table IS
  4  BEGIN
  5     RETURN p_collection;
  6  END card_varchar2;
  7  /

Function created.

2) Сделать тип интерфейса

Во-вторых, мы создадим спецификацию типа интерфейса, которая будет связана с нашей простой функцией card_varchar2, следующим образом.

SQL> CREATE TYPE card_varchar2_ot AS OBJECT (
  2
  3     dummy_attribute NUMBER,
  4
  5     STATIC FUNCTION ODCIGetInterfaces (
  6                     p_interfaces OUT SYS.ODCIObjectList
  7                     ) RETURN NUMBER,
  8
  9     STATIC FUNCTION ODCIStatsTableFunction (
 10                     p_function   IN  SYS.ODCIFuncInfo,
 11                     p_stats      OUT SYS.ODCITabFuncStats,
 12                     p_args       IN  SYS.ODCIArgDescList,
 13                     p_collection IN varchar2table
 14                     ) RETURN NUMBER
 15
 16  );
 17  /

Type created.

и кузов

SQL> CREATE TYPE BODY card_varchar2_ot AS
  2
  3     STATIC FUNCTION ODCIGetInterfaces (
  4                     p_interfaces OUT SYS.ODCIObjectList
  5                     ) RETURN NUMBER IS
  6     BEGIN
  7        p_interfaces := SYS.ODCIObjectList(
  8                           SYS.ODCIObject ('SYS', 'ODCISTATS2')
  9                           );
 10        RETURN ODCIConst.success;
 11     END ODCIGetInterfaces;
 12
 13     STATIC FUNCTION ODCIStatsTableFunction (
 14                     p_function   IN  SYS.ODCIFuncInfo,
 15                     p_stats      OUT SYS.ODCITabFuncStats,
 16                     p_args       IN  SYS.ODCIArgDescList,
 17                     p_collection IN  varchar2table
 18                     ) RETURN NUMBER IS
 19     BEGIN
 20        p_stats := SYS.ODCITabFuncStats(p_collection.COUNT);
 21        RETURN ODCIConst.success;
 22     END ODCIStatsTableFunction;
 23
 24  END;
 25  /

Type body created.

3) Свяжите функцию с типом интерфейса следующим образом.

SQL> ASSOCIATE STATISTICS WITH FUNCTIONS card_varchar2 USING card_varchar2_ot;

Statistics associated.

4) Используйте эту функцию сейчас так:

SQL> SELECT *
  2  FROM   TABLE(card_varchar2('A','B','C'));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...