Функция или процедура для предложения IN - PullRequest
5 голосов
/ 18 февраля 2009

Я хочу написать функцию или процедуру, которую можно использовать в предложении IN другой процедуры. Функция или процедура будет возвращать идентификационные номера.

Основная процедура скажет что-то вроде

SELECT *
FROM EMPLOYEES
WHERE OFFICE_ID IN (GET_OFFICE_IDS);  -- GET_OFFICE_IDS requires no parameters

GET_OFFICE_IDS возвращает VARCHAR2 с идентификатором, разделенным запятыми. Когда я запускаю основную процедуру, я получаю ошибку «ORA-01722: неверный номер», которая имеет смысл, но я не знаю, куда мне нужно идти отсюда.

Нужно ли GET_OFFICE_IDS для создания временной таблицы, используемой основной процедурой? Если так, будет ли снижение производительности?

Ответы [ 5 ]

6 голосов
/ 18 февраля 2009

Вот рабочий пример решения для вложенных таблиц с использованием таблицы EMP:

create type t_ids is table of integer
/

create or replace function get_office_ids return t_ids
is
   l_ids t_ids := t_ids();
   l_idx integer := 0;
begin
   for r in (select empno from emp where deptno=10)
   loop
      l_ids.extend;
      l_idx := l_idx+1;
      l_ids(l_idx) := r.empno;
   end loop;
   return l_ids;
end;
/

select ename from emp where empno in (select * from table(get_office_ids));


ENAME
----------
CLARK
KING
TEST
MILLER
BINNSY
FARMER
3 голосов
/ 18 февраля 2009

Простой метод грубой силы:

WHERE ','||GET_OFFICE_IDS||',' LIKE '%,'||OFFICE_ID||',%'

Было бы лучше изменить GET_OFFICE_IDS для возврата вложенной таблицы и использовать что-то вроде:

OFFICE_ID IN (SELECT * FROM TABLE(GET_OFFICE_IDS))
2 голосов
/ 18 февраля 2009

Я не знаком с Oracle SQL, но вы не можете просто вставить другой оператор select в предложение IN для возврата идентификаторов?

SELECT * ИЗ СОТРУДНИКОВ, ГДЕ OFFICE_ID IN (ВЫБЕРИТЕ ID ИЗ tbl_X, ГДЕ x = y);

... или вы надеялись сделать что-то более сложное?

1 голос
/ 18 февраля 2009

РЕДАКТИРОВАТЬ: Я нарушил основное правило SO, я не ответил на OP. Поскольку уже принятый ответ, я счел целесообразным предупредить.

Как правило, смешивать SQL и PL / SQL - очень плохая идея. Есть 2 отдельных движка для кода. Есть движок SQL и движок PL / SQL. Принудительное переключение между тысячами переключателей приведет к снижению производительности.

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

Даже что-нибудь столь же глупое, как это.

create or replace function my_Date (p_Date in date)
return varchar
as
begin

    return to_char(p_Date, 'yyyy/mm/dd');

end;

Будет Убить ваше время выполнения.

Включите Включите автоматическую трассировку

затем запустите эти.

select to_char(created, 'yyyy/mm/dd'), to_char(last_ddl_time, 'yyyy/mm/dd')  from all_objects


select my_date(created), my_Date(last_DDL_TIME) From all_objects

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

И это ЧРЕЗВЫЧАЙНО простой случай ... все, что я делаю, это приведение значений. Представьте себе, если вы должны присоединиться к нему, как вы хотите. Это действительно худший случай.

Теперь подумайте о том, что оптимизатор совершенно не может сделать, когда вы прячете вещи в функции.

Когда вы делаете IN, иногда это гораздо быстрее сделать объединением. Если определенные условия выполняются, оптимизатор сделает это за вас. Он преобразует IN в JOIN. Но поскольку вы замаскировали выбор внутри функции, он больше не может определить, созрели ли условия. Вы FORCED оптимизатор, чтобы сделать что-то неоптимально.

Одной из ключевых статистических данных, на которые опирается оптимизатор, является количество строк. Это один ряд или 1 миллиард. Это известно из статистики по таблицам и индексам. Статистика вашей функции отсутствует.

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

Эти правила в вашей голове, почти ни одно не относится к SQL. Оптимизатор не является компилятором. Это не может встроить вашу функцию. Только вы можете помочь вашему оптимизатору получить лучший план.

1 голос
/ 18 февраля 2009

Вы, вероятно, можете сделать это с помощью ref_cursor (ref cursor c: = 'select' || ....)

Но конвейерная функция работает очень хорошо. используйте это так:

create or replace type type_varchar2 as table of varchar2(100);

create or replace function GET_OFFICE_IDS return TYPE_varchar2 PIPELINED
is
  retval VARCHAR2(100);
begin
  -- put some sql here which results in statements as below
 retval := '135';
 PIPE ROW (retval);
 retval := '110';
 PIPE ROW (retval);
end GET_OFFICE_IDS;


select *
from entries
where id in (SELECT COLUMN_VALUE FROM TABLE(GET_OFFICE_IDS));

Обычно конвейерная функция работает очень хорошо. Однако подзапрос с МНОЖЕМ записей работает не всегда очень хорошо.

...