Ленивая оценка операторов Oracle PL / SQL в предложениях SELECT SQL-запросов - PullRequest
1 голос
/ 23 апреля 2009

У меня проблема с производительностью оператора SELECT, который я использую в курсоре. В утверждении один из терминов в предложении SELECT является дорогостоящим для оценки (это вызов процедуры PL / SQL, который довольно интенсивно обращается к базе данных). Однако предложения WHERE и ORDER BY просты.

Я ожидал, что Oracle сначала выполнит предложение WHERE для определения набора записей, соответствующих запросу, затем выполнит предложение ORDER BY, чтобы упорядочить их, и, наконец, оценит каждое из условий в предложении SELECT , Поскольку я использую это утверждение в курсоре, из которого я затем извлекаю результаты, я ожидал, что дорогостоящая оценка члена SELECT будет выполняться только по мере необходимости, когда каждый результат запрашивается из курсора.

Однако я обнаружил, что это не та последовательность, которую использует Oracle. Вместо этого он оценивает термины в предложении SELECT для каждой записи, которая соответствует предложению WHERE, перед выполнением сортировки. Из-за этого процедура, вызываемая дорого, вызывается для каждого результата в наборе результатов, прежде чем из курсора будут возвращены какие-либо результаты.

Я хочу иметь возможность вывести первые результаты из курсора как можно быстрее. Может кто-нибудь сказать мне, как убедить Oracle не оценивать вызов процедуры в операторе SELECT до тех пор, пока не будет выполнена сортировка?

Все это, вероятно, проще описать в примере кода:

Учитывая таблицу example со столбцами a, b, c и d, у меня есть такое утверждение:

select a, b, expensive_procedure(c)
  from example
 where <the_where_clause>
 order by d;

При выполнении этого expensive_procedure() вызывается для каждой записи, которая соответствует предложению WHERE, даже если я открываю оператор как курсор и извлекаю из него только один результат.

Я пытался реструктурировать утверждение как:

select a, b, expensive_procedure(c)
  from example, (select example2.rowid, ROWNUM
                   from example example2
                  where <the_where_clause>
                  order by d)
  where example.rowid = example2.rowid;

Где присутствие ROWNUM во внутреннем операторе SELECT вынуждает Oracle сначала оценить его. Такая реструктуризация имеет желаемый выигрыш в производительности. К сожалению, это не всегда соответствует порядку, который требуется.

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

Может ли какой-нибудь гуру Oracle сказать мне, как я могу убедить Oracle прекратить выполнение PL / SQL, пока это не понадобится?

Ответы [ 6 ]

2 голосов
/ 24 апреля 2009

Зачем присоединять EXAMPLE к себе в режиме in-line? Почему бы просто:

select /*+ no_merge(v) */ a, b, expensive_procedure(c)
from 
( select a, b, c
  from example
  where <the_where_clause>
  order by d
) v;
1 голос
/ 23 апреля 2009

Делает ли это то, что вы намерены?

WITH 
cheap AS
(
    SELECT A, B, C
    FROM EXAMPLE
    WHERE <the_where_clause>
)
SELECT A, B, expensive_procedure(C)
FROM cheap
ORDER BY D
0 голосов
/ 24 апреля 2009

Одна из ключевых проблем с решениями, которые мы попробовали, заключается в том, как настроить приложение, генерирующее SQL, для правильной структуры запроса. Встроенный SQL будет различаться в зависимости от количества извлеченных столбцов, количества и типа условий в предложении where, а также количества и типа выражений в порядке.

Встроенное представление, возвращающее ROWID для присоединения к внешнему, было почти полностью универсальным решением, которое мы можем использовать, кроме случаев, когда поиск возвращает значительную часть данных. В этом случае оптимизатор решает [правильно], что соединение HASH дешевле, чем NESTED LOOP.

Другая проблема заключалась в том, что некоторые из задействованных объектов являются ПРОСМОТРАМИ, которые не могут иметь ROWID.

Для справки: "D" не было опечаткой. Выражение для порядка по не выбирается как часть возвращаемого значения. Не необычная вещь:

select index_name, column_name
from user_ind_columns
where table_name = 'TABLE_OF_INTEREST'
order by index_name, column_position;

Здесь вам не нужно знать столбец_положение, но сортировка по нему очень важна.

У нас есть причины (по которым мы не будем утомлять читателя), чтобы избежать необходимости подсказок в решении, но не похоже, что это возможно.

Спасибо за предложения - мы уже попробовали большинство из них ...

0 голосов
/ 24 апреля 2009

Если ваши WHERE условия равны, i. е.

WHERE   col1 = :value1
        AND col2 = :value2

вы можете создать составной индекс для (col1, col2, d):

CREATE INDEX ix_example_col1_col2_d ON example(col1, col2, d)

и подсказку вашего запроса, чтобы использовать его:

SELECT  /*+ INDEX (e ix_example_col1_col2_d) */
        a, b, expensive_procedure(c)
FROM    example e
WHERE   col1 = :value1
        AND col2 = :value2
ORDER BY
        d

В приведенном ниже примере t_even - это таблица 1,000,000 строк с индексом value.

Извлечение 100 столбцов из этого запроса:

SELECT  SYS_GUID()
FROM    t_even
ORDER BY
        value

мгновенно (0,03 секунд), а вот это:

SELECT  SYS_GUID()
FROM    t_even
ORDER BY
        value + 1

требуется около 170 секунд для извлечения первых 100 строк.

SYS_GUID() довольно дорого в Oracle

Как предложено другими, вы также можете использовать это:

SELECT  a, b, expensive_proc(c)
FROM    (
        SELECT  /*+ NO_MERGE */
                *
        FROM    mytable
        ORDER BY
                d
        )

, но использование индекса улучшит время ответа на ваш запрос (как скоро будет возвращена первая строка).

0 голосов
/ 23 апреля 2009

Может, какая-нибудь форма этой работы?

FOR R IN (SELECT a,b,c FROM example WHERE ...) LOOP
  e := expensive_procedure(R.c);
  ...
END LOOP;
0 голосов
/ 23 апреля 2009

Возможно, вы захотите попробовать

select a, b, expensive_procedure(c)
  from example, (select /*+ NO_MERGE */
                    example2.rowid, 
                    ROWNUM
                    from example example2
                    where <the_where_clause>
                    order by d)
  where example.rowid = example2.rowid;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...