Хотя я согласен с Тони, что производительность использования динамического SQL лучше, контекстные переменные - лучший подход, чем использование переменных связывания.
Использование IN_VARIABLE IS NULL OR table.fieldx = IN_VARIABLE
не идеально для обработки необязательных значений. Каждый раз, когда отправляется запрос, Oracle сначала проверяет свой общий пул, чтобы увидеть, был ли этот оператор отправлен ранее. Если это так, то план выполнения для запроса извлекается и выполняется SQL. Если оператор не может быть найден в совместно используемом пуле, Oracle должен пройти процесс синтаксического анализа оператора, разработки различных путей выполнения и разработки оптимального плана доступа (AKA «лучший путь»), прежде чем его можно будет выполнить. Этот процесс известен как «жесткий анализ» и может занять больше времени, чем сам запрос. Подробнее о hard / soft parse в Oracle читайте здесь и AskTom здесь .
Короче - это:
and (:bcode is null or q.bcode = :bcode)
... будет выполняться так же, динамически или иначе. Нет смысла использовать переменные связывания в динамическом SQL для необязательных параметров. Установка все еще разрушает SARGability ...
Параметры контекста - это функция, представленная в Oracle 9i . Они привязаны к пакету и могут использоваться для установки значений атрибутов (только для пользователей с разрешением EXECUTE на пакет, и вам придется предоставить CREATE CONTEXT для схемы). Переменные контекста могут использоваться для настройки динамического SQL, поэтому он включает в себя только то, что необходимо для запроса на основе критериев фильтра / поиска. Для сравнения, переменные Bind (также поддерживаемые в динамическом SQL) требуют указания значения, что может привести к IN_VARIABLE IS NULL OR table.fieldx = IN_VARIABLE
проверкам в поисковом запросе. На практике отдельная переменная контекста должна использоваться для каждой процедуры или функции, чтобы исключить риск загрязнения стоимости.
Вот ваш запрос с использованием переменных контекста:
L_CURSOR SYS_REFCURSOR;
L_QUERY VARCHAR2(5000) DEFAULT 'SELECT num
FROM (SELECT DISTINCT q.num
FROM CQQV q
WHERE 1 = 1 ';
BEGIN
IF IN_BCODE IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'BCODE',
IN_BCODE);
L_QUERY := L_QUERY || ' AND q.bcode = SYS_CONTEXT(''THE_CTX'', ''BCODE'') ';
END IF;
IF IN_LB IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'LB',
IN_LB);
L_QUERY := L_QUERY || ' AND q.lb = SYS_CONTEXT(''THE_CTX'', ''LB'') ';
END IF;
IF IN_TYPE IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'TYPE',
IN_TYPE);
L_QUERY := L_QUERY || ' AND q.type = SYS_CONTEXT(''THE_CTX'', ''TYPE'') ';
END IF;
IF IN_EDATE IS NOT NULL THEN
DBMS_SESSION.SET_CONTEXT('THE_CTX',
'EDATE',
IN_EDATE);
L_QUERY := L_QUERY || ' AND q.edate = SYS_CONTEXT(''THE_CTX'', ''EDATE'') - 30 ';
END IF;
L_QUERY := L_QUERY || ' ORDER BY dbms_random.value()) subq
WHERE rownum <= :numrows ';
FOR I IN 0 .. (TRUNC(LENGTH(L_QUERY) / 255)) LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(L_QUERY, I * 255 + 1, 255));
END LOOP;
OPEN L_CURSOR FOR L_QUERY USING IN_ROWNUM;
RETURN L_CURSOR;
END;
В примере все еще используется переменная связывания для rownum, , потому что значение , а не необязательно.
DBMS_SESSION.SET_CONTEXT('THE_CTX', 'LB', IN_LB);
Параметры SET_CONTEXT следующие:
- Имя переменной контекста. Там нет создания экземпляра
- Переменная в переменной контекста. Контекстная переменная похожа на переменную сеанса, предполагая, что она знакома с веб-приложениями и объектами сеанса.
- Значение переменной, определенной в параметре # 2.
Bind vs Context
Связывание переменных означает, что Oracle ожидает заполнения ссылки на переменную - в противном случае это ошибка ORA. Например:
... L_QUERY USING IN_EXAMPLE_VALUE
... ожидает заполнения одной ссылки на переменную связывания. Если IN_EXAMPLE_VALUE
равно нулю, имеет , чтобы быть :variable
в запросе. IE: AND :variable IS NULL
Использование переменной контекста означает отсутствие необходимости включать внешнюю / избыточную логику, проверяя, является ли значение нулевым.
ВАЖНО : Переменные связывания обрабатываются в порядке появления (известный как порядковый номер), NOT по имени. Вы заметите, что в предложении USING
нет описания типа данных. Порядковые числа не идеальны - если вы измените их в запросе без обновления предложения USING
, он будет прерывать запрос до тех пор, пока он не будет исправлен.